]> eyrie.org Git - kerberos/krb5-strength.git/blob - util/xmalloc.c
Make xmalloc diagnostic suppression conditional
[kerberos/krb5-strength.git] / util / xmalloc.c
1 /*
2  * malloc routines with failure handling.
3  *
4  * Usage:
5  *
6  *      extern xmalloc_handler_t memory_error;
7  *      extern const char *string;
8  *      char *buffer;
9  *      va_list args;
10  *
11  *      xmalloc_error_handler = memory_error;
12  *      buffer = xmalloc(1024);
13  *      xrealloc(buffer, 2048);
14  *      free(buffer);
15  *      buffer = xcalloc(1, 1024);
16  *      free(buffer);
17  *      buffer = xstrdup(string);
18  *      free(buffer);
19  *      buffer = xstrndup(string, 25);
20  *      free(buffer);
21  *      xasprintf(&buffer, "%s", "some string");
22  *      free(buffer);
23  *      xvasprintf(&buffer, "%s", args);
24  *
25  * xmalloc, xcalloc, xrealloc, and xstrdup behave exactly like their C library
26  * counterparts without the leading x except that they will never return NULL.
27  * Instead, on error, they call xmalloc_error_handler, passing it the name of
28  * the function whose memory allocation failed, the amount of the allocation,
29  * and the file and line number where the allocation function was invoked
30  * (from __FILE__ and __LINE__).  This function may do whatever it wishes,
31  * such as some action to free up memory or a call to sleep to hope that
32  * system resources return.  If the handler returns, the interrupted memory
33  * allocation function will try its allocation again (calling the handler
34  * again if it still fails).
35  *
36  * xreallocarray behaves the same as the OpenBSD reallocarray function but for
37  * the same error checking, which in turn is the same as realloc but with
38  * calloc-style arguments and size overflow checking.
39  *
40  * xstrndup behaves like xstrdup but only copies the given number of
41  * characters.  It allocates an additional byte over its second argument and
42  * always nul-terminates the string.
43  *
44  * xasprintf and xvasprintf behave just like their GNU glibc library
45  * implementations except that they do the same checking as described above.
46  * xasprintf will only be able to provide accurate file and line information
47  * on systems that support variadic macros.
48  *
49  * The default error handler, if none is set by the caller, prints an error
50  * message to stderr and exits with exit status 1.  An error handler must take
51  * a const char * (function name), size_t (bytes allocated), const char *
52  * (file), and int (line).
53  *
54  * xmalloc will return a pointer to a valid memory region on an xmalloc of 0
55  * bytes, ensuring this by allocating space for one character instead of 0
56  * bytes.
57  *
58  * The functions defined here are actually x_malloc, x_realloc, etc.  The
59  * header file defines macros named xmalloc, etc. that pass the file name and
60  * line number to these functions.
61  *
62  * The canonical version of this file is maintained in the rra-c-util package,
63  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
64  *
65  * Written by Russ Allbery <eagle@eyrie.org>
66  * Copyright 2015, 2023 Russ Allbery <eagle@eyrie.org>
67  * Copyright 2012-2014
68  *     The Board of Trustees of the Leland Stanford Junior University
69  * Copyright 2004-2006 Internet Systems Consortium, Inc. ("ISC")
70  * Copyright 1991, 1994-2003 The Internet Software Consortium and Rich Salz
71  *
72  * This code is derived from software contributed to the Internet Software
73  * Consortium by Rich Salz.
74  *
75  * Permission to use, copy, modify, and distribute this software for any
76  * purpose with or without fee is hereby granted, provided that the above
77  * copyright notice and this permission notice appear in all copies.
78  *
79  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
80  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
81  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
82  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
83  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
84  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
85  * PERFORMANCE OF THIS SOFTWARE.
86  *
87  * SPDX-License-Identifier: ISC
88  */
89
90 #include <config.h>
91 #include <portable/system.h>
92
93 #include <util/messages.h>
94 #include <util/xmalloc.h>
95
96
97 /*
98  * The default error handler.
99  */
100 void
101 xmalloc_fail(const char *function, size_t size, const char *file, int line)
102 {
103     if (size == 0)
104         sysdie("failed to format output with %s at %s line %d", function, file,
105                line);
106     else
107         sysdie("failed to %s %lu bytes at %s line %d", function,
108                (unsigned long) size, file, line);
109 }
110
111 /* Assign to this variable to choose a handler other than the default. */
112 xmalloc_handler_type xmalloc_error_handler = xmalloc_fail;
113
114
115 void *
116 x_malloc(size_t size, const char *file, int line)
117 {
118     void *p;
119     size_t real_size;
120
121     real_size = (size > 0) ? size : 1;
122     p = malloc(real_size);
123     while (p == NULL) {
124         (*xmalloc_error_handler)("malloc", size, file, line);
125         p = malloc(real_size);
126     }
127     return p;
128 }
129
130
131 void *
132 x_calloc(size_t n, size_t size, const char *file, int line)
133 {
134     void *p;
135
136     n = (n > 0) ? n : 1;
137     size = (size > 0) ? size : 1;
138     p = calloc(n, size);
139     while (p == NULL) {
140         (*xmalloc_error_handler)("calloc", n *size, file, line);
141         p = calloc(n, size);
142     }
143     return p;
144 }
145
146
147 void *
148 x_realloc(void *p, size_t size, const char *file, int line)
149 {
150     void *newp;
151
152     newp = realloc(p, size);
153     if (size == 0)
154         return newp;
155
156         /*
157          * GCC 13.2.0 (and some earlier versions) misdiagnose this error
158          * handling as a use-after-free of p, but the C standard guarantees
159          * that if realloc fails (which is true in every case when it returns
160          * NULL when size > 0), p is unchanged and still valid.
161          */
162 #if __GNUC__ >= 12 && !defined(__clang__)
163 #    pragma GCC diagnostic push
164 #    pragma GCC diagnostic ignored "-Wuse-after-free"
165 #endif
166     while (newp == NULL) {
167         (*xmalloc_error_handler)("realloc", size, file, line);
168         newp = realloc(p, size);
169     }
170     return newp;
171 #if __GNUC__ >= 12 && !defined(__clang__)
172 #    pragma GCC diagnostic pop
173 #endif
174 }
175
176
177 void *
178 x_reallocarray(void *p, size_t n, size_t size, const char *file, int line)
179 {
180     void *newp;
181
182     newp = reallocarray(p, n, size);
183     if (size == 0 || n == 0)
184         return newp;
185
186         /*
187          * GCC 13.2.0 (and some earlier versions) misdiagnose this error
188          * handling as a use-after-free of p, but the documentation of
189          * reallocarray guarantees that if reallocarray fails (which is true in
190          * every case when it returns NULL when size > 0 and n > 0), p is
191          * unchanged and still valid.
192          */
193 #if __GNUC__ >= 12 && !defined(__clang__)
194 #    pragma GCC diagnostic push
195 #    pragma GCC diagnostic ignored "-Wuse-after-free"
196 #endif
197     while (newp == NULL) {
198         (*xmalloc_error_handler)("reallocarray", n *size, file, line);
199         newp = reallocarray(p, n, size);
200     }
201 #if __GNUC__ >= 12 && !defined(__clang__)
202 #    pragma GCC diagnostic pop
203 #endif
204     return newp;
205 }
206
207
208 char *
209 x_strdup(const char *s, const char *file, int line)
210 {
211     char *p;
212     size_t len;
213
214     len = strlen(s) + 1;
215     p = malloc(len);
216     while (p == NULL) {
217         (*xmalloc_error_handler)("strdup", len, file, line);
218         p = malloc(len);
219     }
220     memcpy(p, s, len);
221     return p;
222 }
223
224
225 /*
226  * Avoid using the system strndup function since it may not exist (on Mac OS
227  * X, for example), and there's no need to introduce another portability
228  * requirement.
229  */
230 char *
231 x_strndup(const char *s, size_t size, const char *file, int line)
232 {
233     const char *p;
234     size_t length;
235     char *copy;
236
237     /* Don't assume that the source string is nul-terminated. */
238     for (p = s; (size_t) (p - s) < size && *p != '\0'; p++)
239         ;
240     length = p - s;
241     copy = malloc(length + 1);
242     while (copy == NULL) {
243         (*xmalloc_error_handler)("strndup", length + 1, file, line);
244         copy = malloc(length + 1);
245     }
246     memcpy(copy, s, length);
247     copy[length] = '\0';
248     return copy;
249 }
250
251
252 void
253 x_vasprintf(char **strp, const char *fmt, va_list args, const char *file,
254             int line)
255 {
256     va_list args_copy;
257     int status;
258
259     va_copy(args_copy, args);
260     status = vasprintf(strp, fmt, args_copy);
261     va_end(args_copy);
262     while (status < 0) {
263         va_copy(args_copy, args);
264         status = vsnprintf(NULL, 0, fmt, args_copy);
265         va_end(args_copy);
266         status = (status < 0) ? 0 : status + 1;
267         (*xmalloc_error_handler)("vasprintf", status, file, line);
268         va_copy(args_copy, args);
269         status = vasprintf(strp, fmt, args_copy);
270         va_end(args_copy);
271     }
272 }
273
274
275 #if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS
276 void
277 x_asprintf(char **strp, const char *file, int line, const char *fmt, ...)
278 {
279     va_list args, args_copy;
280     int status;
281
282     va_start(args, fmt);
283     va_copy(args_copy, args);
284     status = vasprintf(strp, fmt, args_copy);
285     va_end(args_copy);
286     while (status < 0) {
287         va_copy(args_copy, args);
288         status = vsnprintf(NULL, 0, fmt, args_copy);
289         va_end(args_copy);
290         status = (status < 0) ? 0 : status + 1;
291         (*xmalloc_error_handler)("asprintf", status, file, line);
292         va_copy(args_copy, args);
293         status = vasprintf(strp, fmt, args_copy);
294         va_end(args_copy);
295     }
296     va_end(args);
297 }
298 #else  /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */
299 void
300 x_asprintf(char **strp, const char *fmt, ...)
301 {
302     va_list args, args_copy;
303     int status;
304
305     va_start(args, fmt);
306     va_copy(args_copy, args);
307     status = vasprintf(strp, fmt, args_copy);
308     va_end(args_copy);
309     while (status < 0) {
310         va_copy(args_copy, args);
311         status = vsnprintf(NULL, 0, fmt, args_copy);
312         va_end(args_copy);
313         status = (status < 0) ? 0 : status + 1;
314         (*xmalloc_error_handler)("asprintf", status, __FILE__, __LINE__);
315         va_copy(args_copy, args);
316         status = vasprintf(strp, fmt, args_copy);
317         va_end(args_copy);
318     }
319     va_end(args);
320 }
321 #endif /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */