2024-11-04 Paul Eggert <eggert@cs.ucla.edu>
+ realloc-posix: realloc (..., 0) now returns nonnull
+ * lib/realloc.c (rpl_realloc): Simplify and tune by using
+ HAVE_REALLOC_0_NONNULL and HAVE_MALLOC_PTRDIFF, and
+ by having just one call to realloc instead of two.
+ * lib/reallocarray.c (reallocarray): Simplify and tune
+ by delegating the zero case to the revised realloc.
+ * m4/eealloc.m4 (_AC_FUNC_REALLOC_IF): Since only eealloc uses
+ this macro now, move its definition here ...
+ * m4/realloc.m4: ... from here.
+ (gl_FUNC_REALLOC_0_NONNULL): Also check that realloc (p, 0)
+ returns nonnull. Require gl_FUNC_REALLOC_POSIX.
+ Define HAVE_REALLOC_0_NONNULL.
+ * m4/reallocarray.m4 (gl_FUNC_REALLOCARRAY):
+ Also replace reallocarray if it returns a null pointer for size zero.
+ * modules/eealloc (Files): Remove m4/realloc.m4.
+ * modules/realloc-posix (Depends-on): Add extensions-aix.
+ * modules/reallocarray (Files): Add m4/realloc.m4.
+
stdlib: simplify preprocessor conditionals
* lib/stdlib.in.h: Omit some redundant tests in conditionals.
exceeds @code{PTRDIFF_MAX}. Although this behavior is arguably
allowed by POSIX it can lead to behavior not defined by POSIX later,
so @code{realloc-posix} does not allow going over the limit.
-@end itemize
+@item
It is not portable to call @code{realloc} with a size of 0. With a
null pointer argument, this is the same ambiguity as @code{malloc (0)}
as to whether a successful call returns a null pointer or a pointer to a
-new zero-sized memory region.
+new zero-sized memory region. The @code{realloc-posix} module
+implements the latter behavior.
Behavior is a real mess for @code{realloc (p, 0)} with non-null @code{p}.
C23 says behavior is undefined.
@end enumerate
@noindent
+The @code{realloc-posix} module implements behavior (5).
+
A program not suspecting these variations in semantics will either:
@itemize
@code{realloc} failure), or have double-free bugs (if it does not check),
when it assumes behavior (4) or (5) but the system implements (1), (2) or (3).
@end itemize
+@end itemize
Portability problems not fixed by Gnulib:
when @code{p} points to a region of size @code{psize} and @code{n <= psize},
@code{realloc (p, n)} can fail and return a null pointer:
glibc 2.40 and probably other platforms.
-
-@item
-If @code{realloc (p, 0)} frees @code{p} and returns a null pointer,
-some platforms do not set @code{errno} to @code{EINVAL},
-even though POSIX.1-2024 requires this:
-glibc 2.1.1--2.40, most likely glibc 2.41+ at least by default,
-Android, mingw, MSVC.
@end itemize
multiplying @code{n} by @code{s} would exceed @code{PTRDIFF_MAX},
which can lead to undefined behavior later:
FreeBSD 13, NetBSD 9, OpenBSD 6, musl 1.2.
-@end itemize
-Portability problems not fixed by Gnulib:
-@itemize
@item
It is not portable to call
@code{reallocarray (p, n, s)} when either @code{n} or @code{s} is zero,
as @code{reallocarray} has the same issues with zero sizes
that @code{realloc} does. @xref{realloc}.
@end itemize
+
+Portability problems not fixed by Gnulib:
+@itemize
+@end itemize
abort ();
#endif
- /* When P is null, act like glibc malloc, i.e., like malloc (1)
+ /* realloc (NULL, 0) acts like glibc malloc (0), i.e., like malloc (1)
except the caller cannot dereference any non-null return.
- When P is non-null, POSIX.1-2024 extends C17 to say that
- realloc (P, 0) either fails and returns a null pointer,
+ realloc (P, 0) with non-null P is a messier situation.
+ As mentioned above, C23 says behavior is undefined.
+ POSIX.1-2024 extends C17 to say realloc (P, 0)
+ either fails by setting errno and returning a null pointer,
or succeeds by freeing P and then either:
(a) setting errno=EINVAL and returning a null pointer; or
(b) acting like a successful malloc (0).
- GNU realloc acts like (a) except it does not set errno;
+ glibc 1 through 2.1 realloc acted like (b),
+ which conforms to C17, to C23 and to POSIX.1-2024.
+ glibc 2.1.1+ realloc acts like (a) except it does not set errno;
this conforms to C17 and to C23 but not to POSIX.1-2024.
Quite possibly future versions of POSIX will change,
due either to C23 or to (a)'s semantics being messy.
matches BSD and V7 realloc, and requires no extra code at
caller sites. */
- void *result = realloc (p, 1);
-#if !HAVE_MALLOC_POSIX
- if (result == NULL)
- errno = ENOMEM;
+#if !HAVE_REALLOC_0_NONNULL
+ n = 1;
#endif
- return result;
}
+#if !HAVE_MALLOC_PTRDIFF
ptrdiff_t signed_n;
if (ckd_add (&signed_n, n, 0))
{
errno = ENOMEM;
return NULL;
}
+#endif
void *result = realloc (p, n);
return NULL;
}
- /* Work around realloc glitch by treating a 0 size as if it were 1,
- to avoid undefined behavior in strict C23 platforms,
- and so that returning NULL is equivalent to failing. */
- if (nbytes == 0)
- nbytes = 1;
-
/* Call realloc, setting errno to ENOMEM on failure. */
return realloc (ptr, nbytes);
}
[
AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
AC_REQUIRE([gl_FUNC_MALLOC_POSIX])
- if test $REPLACE_MALLOC_FOR_MALLOC_POSIX = 1; then
- REPLACE_CALLOC_FOR_CALLOC_POSIX=1
- fi
+ REPLACE_CALLOC_FOR_CALLOC_POSIX=$REPLACE_MALLOC_FOR_MALLOC_POSIX
dnl Although in theory we should also test for size_t overflow,
dnl in practice testing for ptrdiff_t overflow suffices
dnl since PTRDIFF_MAX <= SIZE_MAX on all known Gnulib porting targets.
# eealloc.m4
-# serial 5
+# serial 6
dnl Copyright (C) 2003, 2009-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
[If realloc(NULL,0) is != NULL, define this to 1. Otherwise define this
to 0.])
])
+
+m4_version_prereq([2.73], [], [
+# This is copied from upstream Autoconf here:
+# https://git.savannah.gnu.org/cgit/autoconf.git/tree/lib/autoconf/functions.m4?id=f8c82d292699fbce6d60abb46259a3781578f7fc#n1483
+# _AC_FUNC_REALLOC_IF(IF-WORKS, IF-NOT[, UNKNOWN-ASSUME])
+# -------------------------------------------------------
+# If 'realloc (0, 0)' returns nonnull, run IF-WORKS, otherwise, IF-NOT.
+# If it is not known whether it works, assume the shell word UNKNOWN-ASSUME,
+# which should end in "yes" or in something else (the latter is the default).
+AC_DEFUN([_AC_FUNC_REALLOC_IF],
+[
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+ AC_CACHE_CHECK([whether realloc (0, 0) returns nonnull],
+ [ac_cv_func_realloc_0_nonnull],
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ /* Use prealloc to test; 'volatile' prevents the compiler
+ from optimizing the realloc call away. */
+ void *(*volatile prealloc) (void *, size_t) = realloc;]],
+ [[void *p = prealloc (0, 0);
+ int result = !p;
+ free (p);
+ return result;]])],
+ [ac_cv_func_realloc_0_nonnull=yes],
+ [ac_cv_func_realloc_0_nonnull=no],
+ [AS_CASE([$host_os],
+ [# Guess yes on platforms where we know the result.
+ *-gnu* | freebsd* | netbsd* | openbsd* | bitrig* \
+ | gnu* | *-musl* | midipix* | midnightbsd* \
+ | hpux* | solaris* | cygwin* | mingw* | windows* | msys*],
+ [ac_cv_func_realloc_0_nonnull="guessing yes"],
+ [# Guess as follows if we don't know.
+ ac_cv_func_realloc_0_nonnull=m4_default([$3], ["guessing no"])])])])
+ AS_CASE([$ac_cv_func_realloc_0_nonnull], [*yes], [$1], [$2])
+])# _AC_FUNC_REALLOC_IF
+])
# realloc.m4
-# serial 37
+# serial 38
dnl Copyright (C) 2007, 2009-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
[test -n "$gl_cv_func_realloc_sanitize" || gl_cv_func_realloc_sanitize=no])
])
-m4_version_prereq([2.73], [], [
-# This is copied from upstream Autoconf here:
-# https://git.savannah.gnu.org/cgit/autoconf.git/tree/lib/autoconf/functions.m4?id=f8c82d292699fbce6d60abb46259a3781578f7fc#n1483
-# _AC_FUNC_REALLOC_IF(IF-WORKS, IF-NOT[, UNKNOWN-ASSUME])
-# -------------------------------------------------------
-# If 'realloc (0, 0)' returns nonnull, run IF-WORKS, otherwise, IF-NOT.
-# If it is not known whether it works, assume the shell word UNKNOWN-ASSUME,
-# which should end in "yes" or in something else (the latter is the default).
-AC_DEFUN([_AC_FUNC_REALLOC_IF],
-[
- AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
- AC_CACHE_CHECK([whether realloc (0, 0) returns nonnull],
- [ac_cv_func_realloc_0_nonnull],
- [AC_RUN_IFELSE(
- [AC_LANG_PROGRAM(
- [[#include <stdlib.h>
- /* Use prealloc to test; 'volatile' prevents the compiler
- from optimizing the realloc call away. */
- void *(*volatile prealloc) (void *, size_t) = realloc;]],
- [[void *p = prealloc (0, 0);
- int result = !p;
- free (p);
- return result;]])],
- [ac_cv_func_realloc_0_nonnull=yes],
- [ac_cv_func_realloc_0_nonnull=no],
- [AS_CASE([$host_os],
- [# Guess yes on platforms where we know the result.
- *-gnu* | freebsd* | netbsd* | openbsd* | bitrig* \
- | gnu* | *-musl* | midipix* | midnightbsd* \
- | hpux* | solaris* | cygwin* | mingw* | windows* | msys*],
- [ac_cv_func_realloc_0_nonnull="guessing yes"],
- [# Guess as follows if we don't know.
- ac_cv_func_realloc_0_nonnull=m4_default([$3], ["guessing no"])])])])
- AS_CASE([$ac_cv_func_realloc_0_nonnull], [*yes], [$1], [$2])
-])# _AC_FUNC_REALLOC_IF
-])
-
# gl_FUNC_REALLOC_POSIX
# ---------------------
# Test whether 'realloc' is POSIX compliant (sets errno to ENOMEM when it
AC_DEFINE([NEED_SANITIZED_REALLOC], [1],
[Define to 1 if realloc should abort upon undefined behaviour.])
else
- if test $REPLACE_MALLOC_FOR_MALLOC_POSIX = 1; then
- REPLACE_REALLOC_FOR_REALLOC_POSIX=1
- fi
+ REPLACE_REALLOC_FOR_REALLOC_POSIX=$REPLACE_MALLOC_FOR_MALLOC_POSIX
fi
])
# gl_FUNC_REALLOC_0_NONNULL
# -------------------------
-# Replace realloc if it is not compatible with GNU libc.
+# Replace realloc if realloc (..., 0) returns null.
AC_DEFUN([gl_FUNC_REALLOC_0_NONNULL],
[
AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
-
- _AC_FUNC_REALLOC_IF([], [REPLACE_REALLOC_FOR_REALLOC_POSIX=1],
- ["$gl_cross_guess_normal"])
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+ AC_REQUIRE([gl_FUNC_REALLOC_POSIX])
+ AC_CACHE_CHECK([whether realloc (..., 0) returns nonnull],
+ [gl_cv_func_realloc_0_nonnull],
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ /* Use prealloc to test; "volatile" prevents the compiler
+ from optimizing the realloc call away. */
+ void *(*volatile prealloc) (void *, size_t) = realloc;]],
+ [[void *p = prealloc (0, 0);
+ int result = !p;
+ p = prealloc (p, 0);
+ result |= !p;
+ free (p);
+ return result;]])],
+ [gl_cv_func_realloc_0_nonnull=yes],
+ [gl_cv_func_realloc_0_nonnull=no],
+ [AS_CASE([$host_os],
+ [# Guess yes on platforms where we know the result.
+ freebsd* | netbsd* | openbsd* | darwin* | bitrig* \
+ | *-musl* | midipix* | midnightbsd* \
+ | hpux* | solaris* | cygwin*],
+ [gl_cv_func_realloc_0_nonnull="guessing yes"],
+ [# Guess as follows if we don't know.
+ gl_cv_func_realloc_0_nonnull=$gl_cross_guess_normal])])])
+ AS_CASE([$gl_cv_func_realloc_0_nonnull],
+ [*yes],
+ [AC_DEFINE([HAVE_REALLOC_0_NONNULL], [1],
+ [Define to 1 if realloc (..., 0) returns nonnull.])],
+ [REPLACE_REALLOC_FOR_REALLOC_POSIX=1])
])
# reallocarray.m4
-# serial 5
+# serial 6
dnl Copyright (C) 2017-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
AC_REQUIRE([gl_CHECK_MALLOC_PTRDIFF])
+ AC_REQUIRE([gl_FUNC_REALLOC_0_NONNULL])
+ REPLACE_REALLOCARRAY=$REPLACE_REALLOC_FOR_REALLOC_POSIX
gl_CHECK_FUNCS_ANDROID([reallocarray], [[#include <stdlib.h>]])
if test "$ac_cv_func_reallocarray" = no; then
HAVE_REALLOCARRAY=0
case "$gl_cv_onwards_func_reallocarray" in
future*) REPLACE_REALLOCARRAY=1 ;;
esac
- elif test "$gl_cv_malloc_ptrdiff" = no; then
- REPLACE_REALLOCARRAY=1
+ else
+ case $gl_cv_func_realloc_0_nonnull in
+ *yes) ;;
+ *) REPLACE_REALLOCARRAY=1 ;;
+ esac
fi
])
lib/eealloc.c
m4/eealloc.m4
m4/malloc.m4
-m4/realloc.m4
Depends-on:
extern-inline
m4/malloc.m4
Depends-on:
+extensions-aix
stdckdint [test $REPLACE_REALLOC_FOR_REALLOC_POSIX = 1]
stdlib
Files:
lib/reallocarray.c
m4/malloc.m4
+m4/realloc.m4
m4/reallocarray.m4
Depends-on: