2 * malloc routines with failure handling.
6 * extern xmalloc_handler_t memory_error;
7 * extern const char *string;
11 * xmalloc_error_handler = memory_error;
12 * buffer = xmalloc(1024);
13 * xrealloc(buffer, 2048);
15 * buffer = xcalloc(1, 1024);
17 * buffer = xstrdup(string);
19 * buffer = xstrndup(string, 25);
21 * xasprintf(&buffer, "%s", "some string");
23 * xvasprintf(&buffer, "%s", args);
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).
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.
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.
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.
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).
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
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.
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/>.
65 * Written by Russ Allbery <eagle@eyrie.org>
66 * Copyright 2015, 2023 Russ Allbery <eagle@eyrie.org>
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
72 * This code is derived from software contributed to the Internet Software
73 * Consortium by Rich Salz.
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.
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.
87 * SPDX-License-Identifier: ISC
91 #include <portable/system.h>
93 #include <util/messages.h>
94 #include <util/xmalloc.h>
98 * The default error handler.
101 xmalloc_fail(const char *function, size_t size, const char *file, int line)
104 sysdie("failed to format output with %s at %s line %d", function, file,
107 sysdie("failed to %s %lu bytes at %s line %d", function,
108 (unsigned long) size, file, line);
111 /* Assign to this variable to choose a handler other than the default. */
112 xmalloc_handler_type xmalloc_error_handler = xmalloc_fail;
116 x_malloc(size_t size, const char *file, int line)
121 real_size = (size > 0) ? size : 1;
122 p = malloc(real_size);
124 (*xmalloc_error_handler)("malloc", size, file, line);
125 p = malloc(real_size);
132 x_calloc(size_t n, size_t size, const char *file, int line)
137 size = (size > 0) ? size : 1;
140 (*xmalloc_error_handler)("calloc", n *size, file, line);
148 x_realloc(void *p, size_t size, const char *file, int line)
152 newp = realloc(p, size);
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.
162 #if !defined(__clang__)
163 # pragma GCC diagnostic push
164 # pragma GCC diagnostic ignored "-Wuse-after-free"
166 while (newp == NULL) {
167 (*xmalloc_error_handler)("realloc", size, file, line);
168 newp = realloc(p, size);
171 #if !defined(__clang__)
172 # pragma GCC diagnostic pop
178 x_reallocarray(void *p, size_t n, size_t size, const char *file, int line)
182 newp = reallocarray(p, n, size);
183 if (size == 0 || n == 0)
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.
193 #if !defined(__clang__)
194 # pragma GCC diagnostic push
195 # pragma GCC diagnostic ignored "-Wuse-after-free"
197 while (newp == NULL) {
198 (*xmalloc_error_handler)("reallocarray", n *size, file, line);
199 newp = reallocarray(p, n, size);
201 #if !defined(__clang__)
202 # pragma GCC diagnostic pop
209 x_strdup(const char *s, const char *file, int line)
217 (*xmalloc_error_handler)("strdup", len, file, line);
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
231 x_strndup(const char *s, size_t size, const char *file, int line)
237 /* Don't assume that the source string is nul-terminated. */
238 for (p = s; (size_t) (p - s) < size && *p != '\0'; p++)
241 copy = malloc(length + 1);
242 while (copy == NULL) {
243 (*xmalloc_error_handler)("strndup", length + 1, file, line);
244 copy = malloc(length + 1);
246 memcpy(copy, s, length);
253 x_vasprintf(char **strp, const char *fmt, va_list args, const char *file,
259 va_copy(args_copy, args);
260 status = vasprintf(strp, fmt, args_copy);
263 va_copy(args_copy, args);
264 status = vsnprintf(NULL, 0, fmt, 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);
275 #if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS
277 x_asprintf(char **strp, const char *file, int line, const char *fmt, ...)
279 va_list args, args_copy;
283 va_copy(args_copy, args);
284 status = vasprintf(strp, fmt, args_copy);
287 va_copy(args_copy, args);
288 status = vsnprintf(NULL, 0, fmt, 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);
298 #else /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */
300 x_asprintf(char **strp, const char *fmt, ...)
302 va_list args, args_copy;
306 va_copy(args_copy, args);
307 status = vasprintf(strp, fmt, args_copy);
310 va_copy(args_copy, args);
311 status = vsnprintf(NULL, 0, fmt, 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);
321 #endif /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */