1
0
mirror of https://git.savannah.gnu.org/git/gnulib.git synced 2025-08-08 17:22:05 +03:00

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.
This commit is contained in:
Paul Eggert
2024-11-03 22:41:41 -08:00
parent 8835e92de4
commit d884e6fc4a
12 changed files with 123 additions and 80 deletions

View File

@@ -1,5 +1,23 @@
2024-11-04 Paul Eggert <eggert@cs.ucla.edu> 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 stdlib: simplify preprocessor conditionals
* lib/stdlib.in.h: Omit some redundant tests in conditionals. * lib/stdlib.in.h: Omit some redundant tests in conditionals.

View File

@@ -20,12 +20,13 @@ On some platforms, @code{realloc (p, n)} can succeed even if @code{n}
exceeds @code{PTRDIFF_MAX}. Although this behavior is arguably exceeds @code{PTRDIFF_MAX}. Although this behavior is arguably
allowed by POSIX it can lead to behavior not defined by POSIX later, allowed by POSIX it can lead to behavior not defined by POSIX later,
so @code{realloc-posix} does not allow going over the limit. 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 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)} 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 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}. Behavior is a real mess for @code{realloc (p, 0)} with non-null @code{p}.
C23 says behavior is undefined. C23 says behavior is undefined.
@@ -69,6 +70,8 @@ musl libc, macOS, FreeBSD, NetBSD, OpenBSD, Solaris, Cygwin.
@end enumerate @end enumerate
@noindent @noindent
The @code{realloc-posix} module implements behavior (5).
A program not suspecting these variations in semantics will either: A program not suspecting these variations in semantics will either:
@itemize @itemize
@@ -82,6 +85,7 @@ Falsely respond to memory exhaustion (if it wisely checks for
@code{realloc} failure), or have double-free bugs (if it does not check), @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). when it assumes behavior (4) or (5) but the system implements (1), (2) or (3).
@end itemize @end itemize
@end itemize
Portability problems not fixed by Gnulib: Portability problems not fixed by Gnulib:
@@ -91,11 +95,4 @@ When not growing an already-allocated region, i.e.,
when @code{p} points to a region of size @code{psize} and @code{n <= psize}, 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: @code{realloc (p, n)} can fail and return a null pointer:
glibc 2.40 and probably other platforms. 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 @end itemize

View File

@@ -31,13 +31,14 @@ On some platforms, @code{reallocarray (p, n, s)} can succeed even if
multiplying @code{n} by @code{s} would exceed @code{PTRDIFF_MAX}, multiplying @code{n} by @code{s} would exceed @code{PTRDIFF_MAX},
which can lead to undefined behavior later: which can lead to undefined behavior later:
FreeBSD 13, NetBSD 9, OpenBSD 6, musl 1.2. FreeBSD 13, NetBSD 9, OpenBSD 6, musl 1.2.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
@item @item
It is not portable to call It is not portable to call
@code{reallocarray (p, n, s)} when either @code{n} or @code{s} is zero, @code{reallocarray (p, n, s)} when either @code{n} or @code{s} is zero,
as @code{reallocarray} has the same issues with zero sizes as @code{reallocarray} has the same issues with zero sizes
that @code{realloc} does. @xref{realloc}. that @code{realloc} does. @xref{realloc}.
@end itemize @end itemize
Portability problems not fixed by Gnulib:
@itemize
@end itemize

View File

@@ -51,15 +51,19 @@ rpl_realloc (void *p, size_t n)
abort (); abort ();
#endif #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. 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) with non-null P is a messier situation.
realloc (P, 0) either fails and returns a null pointer, 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: or succeeds by freeing P and then either:
(a) setting errno=EINVAL and returning a null pointer; or (a) setting errno=EINVAL and returning a null pointer; or
(b) acting like a successful malloc (0). (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. this conforms to C17 and to C23 but not to POSIX.1-2024.
Quite possibly future versions of POSIX will change, Quite possibly future versions of POSIX will change,
due either to C23 or to (a)'s semantics being messy. due either to C23 or to (a)'s semantics being messy.
@@ -67,20 +71,19 @@ rpl_realloc (void *p, size_t n)
matches BSD and V7 realloc, and requires no extra code at matches BSD and V7 realloc, and requires no extra code at
caller sites. */ caller sites. */
void *result = realloc (p, 1); #if !HAVE_REALLOC_0_NONNULL
#if !HAVE_MALLOC_POSIX n = 1;
if (result == NULL)
errno = ENOMEM;
#endif #endif
return result;
} }
#if !HAVE_MALLOC_PTRDIFF
ptrdiff_t signed_n; ptrdiff_t signed_n;
if (ckd_add (&signed_n, n, 0)) if (ckd_add (&signed_n, n, 0))
{ {
errno = ENOMEM; errno = ENOMEM;
return NULL; return NULL;
} }
#endif
void *result = realloc (p, n); void *result = realloc (p, n);

View File

@@ -33,12 +33,6 @@ reallocarray (void *ptr, size_t nmemb, size_t size)
return NULL; 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. */ /* Call realloc, setting errno to ENOMEM on failure. */
return realloc (ptr, nbytes); return realloc (ptr, nbytes);
} }

View File

@@ -72,9 +72,7 @@ AC_DEFUN([gl_FUNC_CALLOC_POSIX],
[ [
AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
AC_REQUIRE([gl_FUNC_MALLOC_POSIX]) AC_REQUIRE([gl_FUNC_MALLOC_POSIX])
if test $REPLACE_MALLOC_FOR_MALLOC_POSIX = 1; then REPLACE_CALLOC_FOR_CALLOC_POSIX=$REPLACE_MALLOC_FOR_MALLOC_POSIX
REPLACE_CALLOC_FOR_CALLOC_POSIX=1
fi
dnl Although in theory we should also test for size_t overflow, dnl Although in theory we should also test for size_t overflow,
dnl in practice testing for ptrdiff_t overflow suffices dnl in practice testing for ptrdiff_t overflow suffices
dnl since PTRDIFF_MAX <= SIZE_MAX on all known Gnulib porting targets. dnl since PTRDIFF_MAX <= SIZE_MAX on all known Gnulib porting targets.

View File

@@ -1,5 +1,5 @@
# eealloc.m4 # eealloc.m4
# serial 5 # serial 6
dnl Copyright (C) 2003, 2009-2024 Free Software Foundation, Inc. dnl Copyright (C) 2003, 2009-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it, dnl gives unlimited permission to copy and/or distribute it,
@@ -33,3 +33,40 @@ AC_DEFUN([gl_EEREALLOC],
[If realloc(NULL,0) is != NULL, define this to 1. Otherwise define this [If realloc(NULL,0) is != NULL, define this to 1. Otherwise define this
to 0.]) 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
])

View File

@@ -1,5 +1,5 @@
# realloc.m4 # realloc.m4
# serial 37 # serial 38
dnl Copyright (C) 2007, 2009-2024 Free Software Foundation, Inc. dnl Copyright (C) 2007, 2009-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it, dnl gives unlimited permission to copy and/or distribute it,
@@ -18,43 +18,6 @@ AC_DEFUN([gl_FUNC_REALLOC_SANITIZED],
[test -n "$gl_cv_func_realloc_sanitize" || gl_cv_func_realloc_sanitize=no]) [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 # gl_FUNC_REALLOC_POSIX
# --------------------- # ---------------------
# Test whether 'realloc' is POSIX compliant (sets errno to ENOMEM when it # Test whether 'realloc' is POSIX compliant (sets errno to ENOMEM when it
@@ -70,19 +33,45 @@ AC_DEFUN([gl_FUNC_REALLOC_POSIX],
AC_DEFINE([NEED_SANITIZED_REALLOC], [1], AC_DEFINE([NEED_SANITIZED_REALLOC], [1],
[Define to 1 if realloc should abort upon undefined behaviour.]) [Define to 1 if realloc should abort upon undefined behaviour.])
else else
if test $REPLACE_MALLOC_FOR_MALLOC_POSIX = 1; then REPLACE_REALLOC_FOR_REALLOC_POSIX=$REPLACE_MALLOC_FOR_MALLOC_POSIX
REPLACE_REALLOC_FOR_REALLOC_POSIX=1
fi
fi fi
]) ])
# gl_FUNC_REALLOC_0_NONNULL # 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_DEFUN([gl_FUNC_REALLOC_0_NONNULL],
[ [
AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
_AC_FUNC_REALLOC_IF([], [REPLACE_REALLOC_FOR_REALLOC_POSIX=1], AC_REQUIRE([gl_FUNC_REALLOC_POSIX])
["$gl_cross_guess_normal"]) 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])
]) ])

View File

@@ -1,5 +1,5 @@
# reallocarray.m4 # reallocarray.m4
# serial 5 # serial 6
dnl Copyright (C) 2017-2024 Free Software Foundation, Inc. dnl Copyright (C) 2017-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it, dnl gives unlimited permission to copy and/or distribute it,
@@ -13,14 +13,19 @@ AC_DEFUN([gl_FUNC_REALLOCARRAY],
AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
AC_REQUIRE([gl_CHECK_MALLOC_PTRDIFF]) 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>]]) gl_CHECK_FUNCS_ANDROID([reallocarray], [[#include <stdlib.h>]])
if test "$ac_cv_func_reallocarray" = no; then if test "$ac_cv_func_reallocarray" = no; then
HAVE_REALLOCARRAY=0 HAVE_REALLOCARRAY=0
case "$gl_cv_onwards_func_reallocarray" in case "$gl_cv_onwards_func_reallocarray" in
future*) REPLACE_REALLOCARRAY=1 ;; future*) REPLACE_REALLOCARRAY=1 ;;
esac esac
elif test "$gl_cv_malloc_ptrdiff" = no; then else
REPLACE_REALLOCARRAY=1 case $gl_cv_func_realloc_0_nonnull in
*yes) ;;
*) REPLACE_REALLOCARRAY=1 ;;
esac
fi fi
]) ])

View File

@@ -6,7 +6,6 @@ lib/eealloc.h
lib/eealloc.c lib/eealloc.c
m4/eealloc.m4 m4/eealloc.m4
m4/malloc.m4 m4/malloc.m4
m4/realloc.m4
Depends-on: Depends-on:
extern-inline extern-inline

View File

@@ -7,6 +7,7 @@ m4/realloc.m4
m4/malloc.m4 m4/malloc.m4
Depends-on: Depends-on:
extensions-aix
stdckdint [test $REPLACE_REALLOC_FOR_REALLOC_POSIX = 1] stdckdint [test $REPLACE_REALLOC_FOR_REALLOC_POSIX = 1]
stdlib stdlib

View File

@@ -4,6 +4,7 @@ reallocarray function that is glibc compatible.
Files: Files:
lib/reallocarray.c lib/reallocarray.c
m4/malloc.m4 m4/malloc.m4
m4/realloc.m4
m4/reallocarray.m4 m4/reallocarray.m4
Depends-on: Depends-on: