From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS22989 209.51.188.0/24 X-Spam-Status: No, score=-3.8 required=3.0 tests=AWL,BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id DA8FD1F4B4 for ; Sun, 18 Apr 2021 02:02:31 +0000 (UTC) Received: from localhost ([::1]:60410 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lXwla-0000my-P4 for normalperson@yhbt.net; Sat, 17 Apr 2021 22:02:30 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:51974) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lXwlW-0000mg-V2 for bug-gnulib@gnu.org; Sat, 17 Apr 2021 22:02:26 -0400 Received: from zimbra.cs.ucla.edu ([131.179.128.68]:58066) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lXwlS-00008x-49 for bug-gnulib@gnu.org; Sat, 17 Apr 2021 22:02:26 -0400 Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id D715A160054 for ; Sat, 17 Apr 2021 19:02:17 -0700 (PDT) Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id zADhILGPasEg for ; Sat, 17 Apr 2021 19:02:14 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 350131600CD for ; Sat, 17 Apr 2021 19:02:14 -0700 (PDT) X-Virus-Scanned: amavisd-new at zimbra.cs.ucla.edu Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id 4BgvnzUzE_82 for ; Sat, 17 Apr 2021 19:02:13 -0700 (PDT) Received: from [192.168.1.9] (cpe-172-91-119-151.socal.res.rr.com [172.91.119.151]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id 8D103160054 for ; Sat, 17 Apr 2021 19:02:13 -0700 (PDT) To: Gnulib bugs From: Paul Eggert Organization: UCLA Computer Science Department Subject: ptrdiff_t overflow checks for malloc-posix etc. Message-ID: <64417dd6-7edb-2045-d827-2dd24b151205@cs.ucla.edu> Date: Sat, 17 Apr 2021 19:02:13 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.7.1 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------73E2EA06FCBEF1A7D45BF60C" Content-Language: en-US Received-SPF: pass client-ip=131.179.128.68; envelope-from=eggert@cs.ucla.edu; helo=zimbra.cs.ucla.edu X-Spam_score_int: -41 X-Spam_score: -4.2 X-Spam_bar: ---- X-Spam_report: (-4.2 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: bug-gnulib@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Gnulib discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnulib-bounces+normalperson=yhbt.net@gnu.org Sender: "bug-gnulib" This is a multi-part message in MIME format. --------------73E2EA06FCBEF1A7D45BF60C Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: quoted-printable I installed the attached patches into Gnulib to make its malloc=20 replacements ptrdiff_t safe. This should help us move in a direction=20 where we can use idx_t (which is signed and therefore safer) for sizes=20 and indexes, instead of using size_t. In creating these patches I found a reasonable amount of cruft of which=20 I tried to clean up some (see ChangeLog entry). If I went too far please=20 let me know and I=E2=80=99ll work to unclean it. I initially attempted to come up with new modules malloc-ptrdiff_t, etc.=20 but ran into complexity issues with all the possible combinations the=20 various malloc modules. So instead, I simply added the fixes to=20 malloc-posix, realloc-posix, and realloc-posix, where they will=20 automatically percolate into malloc-gnu etc. Come to think of it, why do we have both malloc-gnu and malloc-posix=20 modules (and similarly for calloc and realloc)? Was it because GNU=20 realloc was incompatible with C99 realloc, so we needed realloc-gnu vs=20 realloc-posix modules? If so, I suggest that we stop worrying about it,=20 as that worry is now obsolete - C17 allows the GNU behavior. In other words, I suggest that we remove malloc-posix, realloc-posix and=20 calloc-posix, or failing that simply make them obsolete compatibility=20 aliases for malloc-gnu etc. This would simplify the configuration of=20 malloc-using code, and any runtime cost would surely be insignificant=20 (and would occur only on older or non-GNU hosts). The first attached patch does the heavy lifting; the second shows how=20 the xalloc module can be simplified because of the malloc etc. fixes.=20 Other simplifications are possible elsewhere; one step at a time. --------------73E2EA06FCBEF1A7D45BF60C Content-Type: text/x-patch; charset=UTF-8; name="0001-malloc-etc.-check-for-ptrdiff_t-overflow.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="0001-malloc-etc.-check-for-ptrdiff_t-overflow.patch" =46rom 1ca8ecafc9066c9c0fb4b1030088969a11dce3af Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 17 Apr 2021 18:44:25 -0700 Subject: [PATCH 1/2] malloc, etc.: check for ptrdiff_t overflow MIME-Version: 1.0 Content-Type: text/plain; charset=3DUTF-8 Content-Transfer-Encoding: 8bit In glibc 2.30 and later, malloc, realloc and calloc reject attempts to create objects larger than PTRDIFF_MAX bytes. This patch changes malloc-gnu etc. to support this behavior on non-GNU hosts. It also makes this change for malloc-posix etc. since it=E2=80=99s a safety measure that ought to be in POSIX (perhaps we can talk them into that...). In writing this patch I found a complicated set of code that had accumulated over the years, some written by yours truly. I got rid of the code I couldn=E2=80=99t see the need for nowadays. Among othe= r things, the GNU realloc behavior is no longer incompatible with the C standard, because in C17 the latter was relaxed to allow the former. If I went too far in cleaning up, the old stuff can be resurrected. This change is mostly for 32-bit platforms, since practical 64-bit platforms cannot create objects larger than PTRDIFF_MAX bytes anyway. * doc/posix-functions/calloc.texi: * doc/posix-functions/malloc.texi: * doc/posix-functions/realloc.texi: Mention ptrdiff_t issues, and go into more detail about what the gnu extension module does. * doc/posix-functions/realloc.texi: Fix now-obsolete commentary about C99 vs glibc, as C17 allows the glibc behavior and POSIX will follow suit when it gets around to it. * lib/calloc.c, lib/malloc.c, lib/realloc.c: Simplify by always supplying a GNU-compatible version, as that suffices for correctness and is good enough for performance. Include xalloc-oversized.h, and use xalloc_oversized to check for ptrdiff_t overflow. (NEED_CALLOC_GNU, NEED_MALLOC_GNU, NEED_REALLOC_GNU): Remove. * m4/calloc.m4 (_AC_FUNC_CALLOC_IF): * m4/malloc.m4 (_AC_FUNC_MALLOC_IF): * m4/realloc.m4 (_AC_FUNC_REALLOC_IF): Don=E2=80=99t start with a newline. Fix message to match behavior. * m4/calloc.m4 (_AC_FUNC_CALLOC_IF): Don=E2=80=99t test for size_t overfl= ow, as the ptrdiff_t test is good enough. * m4/calloc.m4 (gl_FUNC_CALLOC_GNU): * m4/malloc.m4 (gl_FUNC_MALLOC_GNU): * m4/realloc.m4 (gl_FUNC_REALLOC_GNU): Do not define HAVE_CALLOC_GNU, HAVE_MALLOC_GNU, HAVE_REALLOC_GNU. It=E2=80=99s not worth the aggravation of maintaining these, as they are confusing (they don=E2=80=99t really mean GNU-compatible anyway). Don=E2=80=99t bother testing for GNU behavior if we have already decided to replace the function, since the replacement is always GNUish. * m4/calloc.m4 (gl_FUNC_CALLOC_POSIX): * m4/realloc.m4 (gl_FUNC_REALLOC_POSIX): Defer to gl_FUNC_MALLOC_POSIX. * m4/malloc.m4 (gl_FUNC_MALLOC_PTRDIFF, gl_CHECK_MALLOC_PTRDIFF): New macros. (gl_FUNC_MALLOC_POSIX): Use them to check for ptrdiff_t overflow. * modules/calloc-gnu, modules/malloc-gnu, modules/realloc-gnu: Remove no-longer-needed module indicators. * modules/calloc-posix, modules/malloc-posix, modules/realloc-posix: Depend on xalloc-oversized. * modules/malloc-posix: Require gl_FUNC_MALLOC_POSIX instead of calling it directly, so that other code can require it. * modules/realloc-posix: Depend on free-posix and malloc-posix. --- ChangeLog | 62 +++++++++++++++++++++++++ doc/posix-functions/calloc.texi | 17 +++++-- doc/posix-functions/malloc.texi | 17 ++++--- doc/posix-functions/realloc.texi | 37 ++++++++++----- lib/calloc.c | 37 +++++---------- lib/malloc.c | 32 +++++-------- lib/realloc.c | 55 +++++++--------------- m4/calloc.m4 | 67 +++++++-------------------- m4/malloc.m4 | 79 ++++++++++++++++++++++++-------- m4/realloc.m4 | 32 ++++--------- modules/calloc-gnu | 1 - modules/calloc-posix | 1 + modules/malloc-gnu | 1 - modules/malloc-posix | 4 +- modules/realloc-gnu | 1 - modules/realloc-posix | 4 +- 16 files changed, 243 insertions(+), 204 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e4c95524..825201fe2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,65 @@ +2021-04-17 Paul Eggert + + malloc, etc.: check for ptrdiff_t overflow + In glibc 2.30 and later, malloc, realloc and calloc reject + attempts to create objects larger than PTRDIFF_MAX bytes. + This patch changes malloc-gnu etc. to support this behavior + on non-GNU hosts. It also makes this change for malloc-posix etc. + since it=E2=80=99s a safety measure that ought to be in POSIX (perhaps + we can talk them into that...). + + In writing this patch I found a complicated set of code that had + accumulated over the years, some written by yours truly. I got + rid of the code I couldn=E2=80=99t see the need for nowadays. Among ot= her + things, the GNU realloc behavior is no longer incompatible with + the C standard, because in C17 the latter was relaxed to allow the + former. If I went too far in cleaning up, the old stuff can be + resurrected. + + This change is mostly for 32-bit platforms, since practical 64-bit + platforms cannot create objects larger than PTRDIFF_MAX bytes anyway. + * doc/posix-functions/calloc.texi: + * doc/posix-functions/malloc.texi: + * doc/posix-functions/realloc.texi: + Mention ptrdiff_t issues, and go into more detail about what + the gnu extension module does. + * doc/posix-functions/realloc.texi: Fix now-obsolete commentary + about C99 vs glibc, as C17 allows the glibc behavior and POSIX + will follow suit when it gets around to it. + * lib/calloc.c, lib/malloc.c, lib/realloc.c: + Simplify by always supplying a GNU-compatible version, + as that suffices for correctness and is good enough for performance. + Include xalloc-oversized.h, and use xalloc_oversized to + check for ptrdiff_t overflow. + (NEED_CALLOC_GNU, NEED_MALLOC_GNU, NEED_REALLOC_GNU): Remove. + * m4/calloc.m4 (_AC_FUNC_CALLOC_IF): + * m4/malloc.m4 (_AC_FUNC_MALLOC_IF): + * m4/realloc.m4 (_AC_FUNC_REALLOC_IF): + Don=E2=80=99t start with a newline. Fix message to match behavior. + * m4/calloc.m4 (_AC_FUNC_CALLOC_IF): Don=E2=80=99t test for size_t over= flow, + as the ptrdiff_t test is good enough. + * m4/calloc.m4 (gl_FUNC_CALLOC_GNU): + * m4/malloc.m4 (gl_FUNC_MALLOC_GNU): + * m4/realloc.m4 (gl_FUNC_REALLOC_GNU): + Do not define HAVE_CALLOC_GNU, HAVE_MALLOC_GNU, HAVE_REALLOC_GNU. + It=E2=80=99s not worth the aggravation of maintaining these, as they + are confusing (they don=E2=80=99t really mean GNU-compatible anyway). + Don=E2=80=99t bother testing for GNU behavior if we have already decide= d + to replace the function, since the replacement is always GNUish. + * m4/calloc.m4 (gl_FUNC_CALLOC_POSIX): + * m4/realloc.m4 (gl_FUNC_REALLOC_POSIX): + Defer to gl_FUNC_MALLOC_POSIX. + * m4/malloc.m4 (gl_FUNC_MALLOC_PTRDIFF, gl_CHECK_MALLOC_PTRDIFF): + New macros. + (gl_FUNC_MALLOC_POSIX): Use them to check for ptrdiff_t overflow. + * modules/calloc-gnu, modules/malloc-gnu, modules/realloc-gnu: + Remove no-longer-needed module indicators. + * modules/calloc-posix, modules/malloc-posix, modules/realloc-posix: + Depend on xalloc-oversized. + * modules/malloc-posix: Require gl_FUNC_MALLOC_POSIX instead of + calling it directly, so that other code can require it. + * modules/realloc-posix: Depend on free-posix and malloc-posix. + 2021-04-17 Bruno Haible =20 stdio: Fix build error in some configurations (regression 2021-04-11). diff --git a/doc/posix-functions/calloc.texi b/doc/posix-functions/calloc= =2Etexi index 22c51be02..9ba40c0d4 100644 --- a/doc/posix-functions/calloc.texi +++ b/doc/posix-functions/calloc.texi @@ -12,11 +12,22 @@ Portability problems fixed by Gnulib: Upon failure, the function does not set @code{errno} to @code{ENOMEM} on= some platforms: mingw, MSVC 14. -@end itemize =20 -Portability problems not fixed by Gnulib: -@itemize +@item +On some platforms, @code{calloc (n, s)} can succeed even if +multiplying @code{n} by @code{s} would exceed @code{PTRDIFF_MAX} or +@code{SIZE_MAX}. Although failing to check for exceeding +@code{PTRDIFF_MAX} is arguably allowed by POSIX it can lead to +undefined behavior later, so @code{calloc-posix} does not allow +going over the limit. @end itemize =20 Extension: Gnulib provides a module @samp{calloc-gnu} that substitutes a= @code{calloc} implementation that behaves more like the glibc implementa= tion. +It fixes this portability problem: + +@itemize +@item +On some platforms, @code{calloc (0, s)} and @code{calloc (n, 0)} +return @code{NULL} on success. +@end itemize diff --git a/doc/posix-functions/malloc.texi b/doc/posix-functions/malloc= =2Etexi index 6d5995078..829517311 100644 --- a/doc/posix-functions/malloc.texi +++ b/doc/posix-functions/malloc.texi @@ -12,14 +12,19 @@ Portability problems fixed by Gnulib: Upon failure, the function does not set @code{errno} to @code{ENOMEM} on= some platforms: mingw, MSVC 14. -@end itemize =20 -Portability problems not fixed by Gnulib: -@itemize -@item @code{malloc (0)} always returns a NULL pointer on some platforms:= -AIX 5.1. +@item +On some platforms, @code{malloc (n)} can succeed even if @code{n} +exceeds @code{PTRDIFF_MAX}. Although this behavior is arguably +allowed by POSIX it can lead to behavior not defined by POSIX later, +so @code{malloc-posix} does not allow going over the limit. @end itemize =20 Extension: Gnulib provides a module @samp{malloc-gnu} that substitutes a= @code{malloc} implementation that behaves more like the glibc implementa= tion, -regarding the result of @code{malloc (0)}. +by fixing this portability problem: + +@itemize +@item +On some platforms, @code{malloc (0)} returns @code{NULL} on success. +@end itemize diff --git a/doc/posix-functions/realloc.texi b/doc/posix-functions/reall= oc.texi index 1f0b18e95..282e36051 100644 --- a/doc/posix-functions/realloc.texi +++ b/doc/posix-functions/realloc.texi @@ -12,24 +12,37 @@ Portability problems fixed by Gnulib: Upon failure, the function does not set @code{errno} to @code{ENOMEM} on= some platforms: mingw, MSVC 14. -@end itemize =20 -Portability problems not fixed by Gnulib: -@itemize @item -It is not portable to call @code{realloc} with a size of 0. With a +On some platforms, @code{realloc (p, n)} can succeed even if @code{n} +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 + +Without the @samp{realloc-gnu} module described below, it is not portabl= e +to call @code{realloc} with a size of 0. With a NULL pointer argument, this is the same ambiguity as @code{malloc (0)} on whether a unique zero-size object is created. With a non-NULL -pointer argument, C99 requires that if @code{realloc (p, 0)} returns -@code{NULL} then @code{p} is still valid. Among implementations that -obey C99, behavior varies on whether @code{realloc (p, 0)} always +pointer argument @code{p}, C17 says that it is implementation-defined +whether @code{realloc (p, 0)} frees @code{p}. +Behavior varies on whether @code{realloc (p, 0)} always frees @code{p} +and successfully returns a null pointer, or always fails and leaves @code{p} valid, or usually succeeds and returns a -unique zero-size object; either way, a program not suspecting these +unique zero-size object; a program not suspecting these variations in semantics will leak memory (either the still-valid @code{p}, or the -non-NULL return value). Meanwhile, several implementations violate -C99, by always calling @code{free (p)} but returning NULL: -glibc, Cygwin -@end itemize +non-NULL return value). =20 Extension: Gnulib provides a module @samp{realloc-gnu} that substitutes = a @code{realloc} implementation that behaves more like the glibc implement= ation. +It fixes these portability problems: + +@itemize +@item +On some platforms, @code{realloc (NULL, 0)} returns @code{NULL} on succe= ss. + +@item +On some platforms, @code{realloc (p, 0)} with non-null @code{p} +might not free @code{p}, or might clobber @code{errno}, +or might not return @code{NULL}. +@end itemize diff --git a/lib/calloc.c b/lib/calloc.c index 5ab1975cf..c9da08047 100644 --- a/lib/calloc.c +++ b/lib/calloc.c @@ -19,49 +19,34 @@ =20 #include =20 -/* The gnulib module 'calloc-gnu' defines HAVE_CALLOC_GNU. */ -#if GNULIB_CALLOC_GNU && !HAVE_CALLOC_GNU -# define NEED_CALLOC_GNU 1 -#endif - /* Specification. */ #include =20 #include =20 +#include "xalloc-oversized.h" + /* Call the system's calloc below. */ #undef calloc =20 -/* Allocate and zero-fill an NxS-byte block of memory from the heap. - If N or S is zero, allocate and zero-fill a 1-byte block. */ +/* Allocate and zero-fill an NxS-byte block of memory from the heap, + even if N or S is zero. */ =20 void * rpl_calloc (size_t n, size_t s) { - void *result; - -#if NEED_CALLOC_GNU if (n =3D=3D 0 || s =3D=3D 0) + n =3D s =3D 1; + + if (xalloc_oversized (n, s)) { - n =3D 1; - s =3D 1; - } - else - { - /* Defend against buggy calloc implementations that mishandle - size_t overflow. */ - size_t bytes =3D n * s; - if (bytes / s !=3D n) - { - errno =3D ENOMEM; - return NULL; - } + errno =3D ENOMEM; + return NULL; } -#endif =20 - result =3D calloc (n, s); + void *result =3D calloc (n, s); =20 -#if !HAVE_CALLOC_POSIX +#if !HAVE_MALLOC_POSIX if (result =3D=3D NULL) errno =3D ENOMEM; #endif diff --git a/lib/malloc.c b/lib/malloc.c index 272b8b9f4..a900046ec 100644 --- a/lib/malloc.c +++ b/lib/malloc.c @@ -20,43 +20,35 @@ #define _GL_USE_STDLIB_ALLOC 1 #include =20 -/* The gnulib module 'malloc-gnu' defines HAVE_MALLOC_GNU. */ -#if GNULIB_MALLOC_GNU && !HAVE_MALLOC_GNU -# define NEED_MALLOC_GNU 1 -#endif - #include =20 -/* A function definition is only needed if NEED_MALLOC_GNU is defined ab= ove - or if the module 'malloc-posix' requests it. */ -#if NEED_MALLOC_GNU || (GNULIB_MALLOC_POSIX && !HAVE_MALLOC_POSIX) +#include =20 -# include +#include "xalloc-oversized.h" =20 /* Call the system's malloc below. */ # undef malloc =20 -/* Allocate an N-byte block of memory from the heap. - If N is zero, allocate a 1-byte block. */ +/* Allocate an N-byte block of memory from the heap, even if N is 0. */= =20 void * rpl_malloc (size_t n) { - void *result; - -# if NEED_MALLOC_GNU if (n =3D=3D 0) n =3D 1; -# endif =20 - result =3D malloc (n); + if (xalloc_oversized (n, 1)) + { + errno =3D ENOMEM; + return NULL; + } + + void *result =3D malloc (n); =20 -# if !HAVE_MALLOC_POSIX +#if !HAVE_MALLOC_POSIX if (result =3D=3D NULL) errno =3D ENOMEM; -# endif +#endif =20 return result; } - -#endif diff --git a/lib/realloc.c b/lib/realloc.c index c3e3cdfc5..c0d94b439 100644 --- a/lib/realloc.c +++ b/lib/realloc.c @@ -21,66 +21,43 @@ #define _GL_USE_STDLIB_ALLOC 1 #include =20 -/* The gnulib module 'realloc-gnu' defines HAVE_REALLOC_GNU. */ -#if GNULIB_REALLOC_GNU && !HAVE_REALLOC_GNU -# define NEED_REALLOC_GNU 1 -#endif - -/* Infer the properties of the system's malloc function. - The gnulib module 'malloc-gnu' defines HAVE_MALLOC_GNU. */ -#if GNULIB_MALLOC_GNU && HAVE_MALLOC_GNU -# define SYSTEM_MALLOC_GLIBC_COMPATIBLE 1 -#endif - #include =20 -/* A function definition is only needed if NEED_REALLOC_GNU is defined a= bove - or if the module 'realloc-posix' requests it. */ -#if NEED_REALLOC_GNU || (GNULIB_REALLOC_POSIX && !HAVE_REALLOC_POSIX) +#include =20 -# include +#include "xalloc-oversized.h" =20 -/* Call the system's malloc and realloc below. */ -# undef malloc -# undef realloc +/* Call the system's realloc below. */ +#undef realloc =20 /* Change the size of an allocated block of memory P to N bytes, - with error checking. If N is zero, change it to 1. If P is NULL, - use malloc. */ + with error checking. If P is NULL, use malloc. Otherwise if N is ze= ro, + free P and return NULL. */ =20 void * rpl_realloc (void *p, size_t n) { - void *result; + if (p =3D=3D NULL) + return malloc (n); =20 -# if NEED_REALLOC_GNU if (n =3D=3D 0) { - n =3D 1; - - /* In theory realloc might fail, so don't rely on it to free. */ free (p); - p =3D NULL; + return NULL; } -# endif =20 - if (p =3D=3D NULL) + if (xalloc_oversized (n, 1)) { -# if GNULIB_REALLOC_GNU && !NEED_REALLOC_GNU && !SYSTEM_MALLOC_GLIBC_COM= PATIBLE - if (n =3D=3D 0) - n =3D 1; -# endif - result =3D malloc (n); + errno =3D ENOMEM; + return NULL; } - else - result =3D realloc (p, n); =20 -# if !HAVE_REALLOC_POSIX + void *result =3D realloc (p, n); + +#if !HAVE_MALLOC_POSIX if (result =3D=3D NULL) errno =3D ENOMEM; -# endif +#endif =20 return result; } - -#endif diff --git a/m4/calloc.m4 b/m4/calloc.m4 index eeeb033d8..143f3bc3f 100644 --- a/m4/calloc.m4 +++ b/m4/calloc.m4 @@ -1,4 +1,4 @@ -# calloc.m4 serial 23 +# calloc.m4 serial 24 =20 # Copyright (C) 2004-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation @@ -14,12 +14,10 @@ =20 # _AC_FUNC_CALLOC_IF([IF-WORKS], [IF-NOT]) # ------------------------------------- -# If 'calloc (0, 0)' is properly handled, run IF-WORKS, otherwise, IF-NO= T. +# If calloc is compatible with GNU calloc, run IF-WORKS, otherwise, IF-N= OT. AC_DEFUN([_AC_FUNC_CALLOC_IF], -[ - AC_REQUIRE([AC_TYPE_SIZE_T])dnl - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([for GNU libc compatible calloc], +[ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether calloc (0, n) and calloc (n, 0) return nonnull= ], [ac_cv_func_calloc_0_nonnull], [if test $cross_compiling !=3D yes; then ac_cv_func_calloc_0_nonnull=3Dyes @@ -35,32 +33,6 @@ AC_DEFUN([_AC_FUNC_CALLOC_IF], ]])], [], [ac_cv_func_calloc_0_nonnull=3Dno]) - AC_RUN_IFELSE( - [AC_LANG_PROGRAM( - [AC_INCLUDES_DEFAULT], - [[int result; - typedef struct { char c[8]; } S8; - size_t n =3D (size_t) -1 / sizeof (S8) + 2; - S8 * volatile s =3D calloc (n, sizeof (S8)); - if (s) - { - s[0].c[0] =3D 1; - if (s[n - 1].c[0]) - result =3D 0; - else - result =3D 2; - } - else - result =3D 3; - free (s); - return result; - ]])], - dnl The exit code of this program is 0 if calloc() succeeded wi= th a - dnl wrap-around bug (which it shouldn't), 2 if calloc() succeed= ed in - dnl a non-flat address space, 3 if calloc() failed, or 1 if som= e leak - dnl sanitizer terminated the program as a result of the calloc(= ) call. - [ac_cv_func_calloc_0_nonnull=3Dno], - []) else case "$host_os" in # Guess yes on glibc systems. @@ -82,38 +54,31 @@ AC_DEFUN([_AC_FUNC_CALLOC_IF], $2 ;; esac -])# AC_FUNC_CALLOC +]) =20 =20 # gl_FUNC_CALLOC_GNU # ------------------ -# Report whether 'calloc (0, 0)' is properly handled, and replace calloc= if -# needed. +# Replace calloc if it is not compatible with GNU libc. AC_DEFUN([gl_FUNC_CALLOC_GNU], [ AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) - _AC_FUNC_CALLOC_IF( - [AC_DEFINE([HAVE_CALLOC_GNU], [1], - [Define to 1 if your system has a GNU libc compatible 'ca= lloc' - function, and to 0 otherwise.])], - [AC_DEFINE([HAVE_CALLOC_GNU], [0]) - REPLACE_CALLOC=3D1 - ]) + AC_REQUIRE([gl_FUNC_CALLOC_POSIX]) + test $REPLACE_CALLOC =3D 1 || _AC_FUNC_CALLOC_IF([], [REPLACE_CALLOC=3D= 1]) ])# gl_FUNC_CALLOC_GNU =20 - # gl_FUNC_CALLOC_POSIX # -------------------- # Test whether 'calloc' is POSIX compliant (sets errno to ENOMEM when it= -# fails), and replace calloc if it is not. +# fails, and doesn't mess up with ptrdiff_t or size_t overflow), +# and replace calloc if it is not. AC_DEFUN([gl_FUNC_CALLOC_POSIX], [ AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) - AC_REQUIRE([gl_CHECK_MALLOC_POSIX]) - if test $gl_cv_func_malloc_posix =3D yes; then - AC_DEFINE([HAVE_CALLOC_POSIX], [1], - [Define if the 'calloc' function is POSIX compliant.]) - else - REPLACE_CALLOC=3D1 - fi + AC_REQUIRE([gl_FUNC_MALLOC_POSIX]) + REPLACE_CALLOC=3D$REPLACE_MALLOC + 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 <=3D SIZE_MAX on all known Gnulib porting target= s. + dnl A separate size_t test would slow down 'configure'. ]) diff --git a/m4/malloc.m4 b/m4/malloc.m4 index 32ab42ec0..503da2cf8 100644 --- a/m4/malloc.m4 +++ b/m4/malloc.m4 @@ -1,4 +1,4 @@ -# malloc.m4 serial 22 +# malloc.m4 serial 23 dnl Copyright (C) 2007, 2009-2021 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -7,9 +7,8 @@ dnl with or without modifications, as long as this notice= is preserved. # This is adapted with modifications from upstream Autoconf here: # https://git.savannah.gnu.org/cgit/autoconf.git/commit/?id=3D04be2b7a29= d65d9a08e64e8e56e594c91749598c AC_DEFUN([_AC_FUNC_MALLOC_IF], -[ - AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles - AC_CACHE_CHECK([for GNU libc compatible malloc], +[ AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles + AC_CACHE_CHECK([whether malloc (0) returns nonnull], [ac_cv_func_malloc_0_nonnull], [AC_RUN_IFELSE( [AC_LANG_PROGRAM( @@ -44,47 +43,89 @@ AC_DEFUN([_AC_FUNC_MALLOC_IF], =20 # gl_FUNC_MALLOC_GNU # ------------------ -# Test whether 'malloc (0)' is handled like in GNU libc, and replace mal= loc if -# it is not. +# Replace malloc if it is not compatible with GNU libc. AC_DEFUN([gl_FUNC_MALLOC_GNU], [ AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) - dnl _AC_FUNC_MALLOC_IF is defined in Autoconf. - _AC_FUNC_MALLOC_IF( - [AC_DEFINE([HAVE_MALLOC_GNU], [1], - [Define to 1 if your system has a GNU libc compatible 'ma= lloc' - function, and to 0 otherwise.])], - [AC_DEFINE([HAVE_MALLOC_GNU], [0]) - REPLACE_MALLOC=3D1 + AC_REQUIRE([gl_FUNC_MALLOC_POSIX]) + test $REPLACE_MALLOC =3D 1 || _AC_FUNC_MALLOC_IF([], [REPLACE_MALLOC=3D= 1]) +]) + +# gl_FUNC_MALLOC_PTRDIFF +# ---------------------- +# Test whether malloc (N) reliably fails when N exceeds PTRDIFF_MAX, +# and replace malloc otherwise. +AC_DEFUN([gl_FUNC_MALLOC_PTRDIFF], +[ + AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) + AC_REQUIRE([gl_CHECK_MALLOC_PTRDIFF]) + test "$gl_cv_malloc_ptrdiff" =3D yes || REPLACE_MALLOC=3D1 +]) + +# Test whether malloc, realloc, calloc refuse to create objects +# larger than what can be expressed in ptrdiff_t. +# Set gl_cv_func_malloc_gnu to yes or no accordingly. +AC_DEFUN([gl_CHECK_MALLOC_PTRDIFF], +[ + AC_CACHE_CHECK([whether malloc is ptrdiff_t safe], + [gl_cv_malloc_ptrdiff], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + ]], + [[/* 64-bit ptrdiff_t is so wide that no practical platform + can exceed it. */ + #define WIDE_PTRDIFF (PTRDIFF_MAX >> 31 >> 31 !=3D 0) + + /* On rare machines where size_t fits in ptrdiff_t there + is no problem. */ + #define NARROW_SIZE (SIZE_MAX <=3D PTRDIFF_MAX) + + /* glibc 2.30 and later malloc refuses to exceed ptrdiff_t + bounds even on 32-bit platforms. We don't know which + non-glibc systems are safe. */ + #define KNOWN_SAFE (2 < __GLIBC__ + (30 <=3D __GLIBC_MINOR__= )) + + #if WIDE_PTRDIFF || NARROW_SIZE || KNOWN_SAFE + return 0; + #else + #error "malloc might not be ptrdiff_t safe" + syntax error + #endif + ]])], + [gl_cv_malloc_ptrdiff=3Dyes], + [gl_cv_malloc_ptrdiff=3Dno]) ]) ]) =20 # gl_FUNC_MALLOC_POSIX # -------------------- # Test whether 'malloc' is POSIX compliant (sets errno to ENOMEM when it= -# fails), and replace malloc if it is not. +# fails, and doesn't mess up with ptrdiff_t overflow), and replace +# malloc if it is not. AC_DEFUN([gl_FUNC_MALLOC_POSIX], [ AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) + AC_REQUIRE([gl_FUNC_MALLOC_PTRDIFF]) AC_REQUIRE([gl_CHECK_MALLOC_POSIX]) - if test $gl_cv_func_malloc_posix =3D yes; then + if test "$gl_cv_func_malloc_posix" =3D yes; then AC_DEFINE([HAVE_MALLOC_POSIX], [1], - [Define if the 'malloc' function is POSIX compliant.]) + [Define if malloc, realloc, and calloc set errno on allocation fai= lure.]) else REPLACE_MALLOC=3D1 fi ]) =20 -# Test whether malloc, realloc, calloc are POSIX compliant, +# Test whether malloc, realloc, calloc set errno on failure. # Set gl_cv_func_malloc_posix to yes or no accordingly. AC_DEFUN([gl_CHECK_MALLOC_POSIX], [ - AC_CACHE_CHECK([whether malloc, realloc, calloc are POSIX compliant], + AC_CACHE_CHECK([whether malloc, realloc, calloc set errno on failure],= [gl_cv_func_malloc_posix], [ dnl It is too dangerous to try to allocate a large amount of memor= y: dnl some systems go to their knees when you do that. So assume tha= t - dnl all Unix implementations of the function are POSIX compliant. + dnl all Unix implementations of the function set errno on failure.= AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[]], diff --git a/m4/realloc.m4 b/m4/realloc.m4 index a80a02a6b..4939516a0 100644 --- a/m4/realloc.m4 +++ b/m4/realloc.m4 @@ -1,4 +1,4 @@ -# realloc.m4 serial 20 +# realloc.m4 serial 21 dnl Copyright (C) 2007, 2009-2021 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -7,9 +7,8 @@ dnl with or without modifications, as long as this notice= is preserved. # This is adapted with modifications from upstream Autoconf here: # https://git.savannah.gnu.org/cgit/autoconf.git/commit/?id=3D04be2b7a29= d65d9a08e64e8e56e594c91749598c AC_DEFUN([_AC_FUNC_REALLOC_IF], -[ - AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles - AC_CACHE_CHECK([for GNU libc compatible realloc], +[ 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( @@ -44,33 +43,22 @@ AC_DEFUN([_AC_FUNC_REALLOC_IF], =20 # gl_FUNC_REALLOC_GNU # ------------------- -# Test whether 'realloc (0, 0)' is handled like in GNU libc, and replace= -# realloc if it is not. +# Replace realloc if it is not compatible with GNU libc. AC_DEFUN([gl_FUNC_REALLOC_GNU], [ AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) - dnl _AC_FUNC_REALLOC_IF is defined in Autoconf. - _AC_FUNC_REALLOC_IF( - [AC_DEFINE([HAVE_REALLOC_GNU], [1], - [Define to 1 if your system has a GNU libc compatible 're= alloc' - function, and to 0 otherwise.])], - [AC_DEFINE([HAVE_REALLOC_GNU], [0]) - REPLACE_REALLOC=3D1 - ]) + AC_REQUIRE([gl_FUNC_REALLOC_POSIX]) + test $REPLACE_REALLOC =3D 1 || _AC_FUNC_REALLOC_IF([], [REPLACE_REALLO= C=3D1]) ])# gl_FUNC_REALLOC_GNU =20 # gl_FUNC_REALLOC_POSIX # --------------------- # Test whether 'realloc' is POSIX compliant (sets errno to ENOMEM when i= t -# fails), and replace realloc if it is not. +# fails, and doesn't mess up with ptrdiff_t overflow), +# and replace realloc if it is not. AC_DEFUN([gl_FUNC_REALLOC_POSIX], [ AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) - AC_REQUIRE([gl_CHECK_MALLOC_POSIX]) - if test $gl_cv_func_malloc_posix =3D yes; then - AC_DEFINE([HAVE_REALLOC_POSIX], [1], - [Define if the 'realloc' function is POSIX compliant.]) - else - REPLACE_REALLOC=3D1 - fi + AC_REQUIRE([gl_FUNC_MALLOC_POSIX]) + REPLACE_REALLOC=3D$REPLACE_MALLOC ]) diff --git a/modules/calloc-gnu b/modules/calloc-gnu index ffc8b50ef..2aa2dd1c0 100644 --- a/modules/calloc-gnu +++ b/modules/calloc-gnu @@ -13,7 +13,6 @@ gl_FUNC_CALLOC_GNU if test $REPLACE_CALLOC =3D 1; then AC_LIBOBJ([calloc]) fi -gl_MODULE_INDICATOR([calloc-gnu]) =20 Makefile.am: =20 diff --git a/modules/calloc-posix b/modules/calloc-posix index cead843ae..dc9cadd5c 100644 --- a/modules/calloc-posix +++ b/modules/calloc-posix @@ -8,6 +8,7 @@ m4/malloc.m4 =20 Depends-on: stdlib +xalloc-oversized [test $REPLACE_REALLOC =3D 1] =20 configure.ac: gl_FUNC_CALLOC_POSIX diff --git a/modules/malloc-gnu b/modules/malloc-gnu index 0bfa92d75..f8bcdae55 100644 --- a/modules/malloc-gnu +++ b/modules/malloc-gnu @@ -17,7 +17,6 @@ gl_FUNC_MALLOC_GNU if test $REPLACE_MALLOC =3D 1; then AC_LIBOBJ([malloc]) fi -gl_MODULE_INDICATOR([malloc-gnu]) =20 Makefile.am: =20 diff --git a/modules/malloc-posix b/modules/malloc-posix index 1a2d72c5a..bafcf3801 100644 --- a/modules/malloc-posix +++ b/modules/malloc-posix @@ -7,14 +7,14 @@ m4/malloc.m4 =20 Depends-on: stdlib +xalloc-oversized [test $REPLACE_MALLOC =3D 1] =20 configure.ac: -gl_FUNC_MALLOC_POSIX +AC_REQUIRE([gl_FUNC_MALLOC_POSIX]) if test $REPLACE_MALLOC =3D 1; then AC_LIBOBJ([malloc]) fi gl_STDLIB_MODULE_INDICATOR([malloc-posix]) -gl_MODULE_INDICATOR([malloc-posix]) =20 Makefile.am: =20 diff --git a/modules/realloc-gnu b/modules/realloc-gnu index 760edcda1..7752d8c6a 100644 --- a/modules/realloc-gnu +++ b/modules/realloc-gnu @@ -17,7 +17,6 @@ gl_FUNC_REALLOC_GNU if test $REPLACE_REALLOC =3D 1; then AC_LIBOBJ([realloc]) fi -gl_MODULE_INDICATOR([realloc-gnu]) =20 Makefile.am: =20 diff --git a/modules/realloc-posix b/modules/realloc-posix index 7f481102b..a30356f31 100644 --- a/modules/realloc-posix +++ b/modules/realloc-posix @@ -8,6 +8,9 @@ m4/malloc.m4 =20 Depends-on: stdlib +free-posix [test $REPLACE_REALLOC =3D 1] +malloc-posix [test $REPLACE_REALLOC =3D 1] +xalloc-oversized [test $REPLACE_REALLOC =3D 1] =20 configure.ac: gl_FUNC_REALLOC_POSIX @@ -15,7 +18,6 @@ if test $REPLACE_REALLOC =3D 1; then AC_LIBOBJ([realloc]) fi gl_STDLIB_MODULE_INDICATOR([realloc-posix]) -gl_MODULE_INDICATOR([realloc-posix]) =20 Makefile.am: =20 --=20 2.27.0 --------------73E2EA06FCBEF1A7D45BF60C Content-Type: text/x-patch; charset=UTF-8; name="0002-xalloc-adjust-to-malloc-ptrdiff_t-change.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="0002-xalloc-adjust-to-malloc-ptrdiff_t-change.patch" =46rom 7415a0fa4abc1b8e8af37afd5b5408c1d5dfff2e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 17 Apr 2021 18:48:38 -0700 Subject: [PATCH 2/2] xalloc: adjust to malloc ptrdiff_t change * lib/xmalloc.c (HAVE_GNU_CALLOC, HAVE_GNU_MALLOC, HAVE_GNU_REALLOC): Remove. (xmalloc, xrealloc, xcalloc): Simplify by assuming GNU behavior. * modules/xalloc (Depends-on): Add calloc-gnu, malloc-gnu, realloc-gnu. --- ChangeLog | 7 +++++++ lib/xmalloc.c | 41 ++++------------------------------------- modules/xalloc | 3 +++ 3 files changed, 14 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index 825201fe2..03221a292 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2021-04-17 Paul Eggert =20 + xalloc: adjust to malloc ptrdiff_t change + * lib/xmalloc.c (HAVE_GNU_CALLOC, HAVE_GNU_MALLOC, HAVE_GNU_REALLOC): + Remove. + (xmalloc, xrealloc, xcalloc): Simplify by assuming GNU behavior. + * modules/xalloc (Depends-on): Add calloc-gnu, malloc-gnu, + realloc-gnu. + malloc, etc.: check for ptrdiff_t overflow In glibc 2.30 and later, malloc, realloc and calloc reject attempts to create objects larger than PTRDIFF_MAX bytes. diff --git a/lib/xmalloc.c b/lib/xmalloc.c index 4a6589571..39ce893ad 100644 --- a/lib/xmalloc.c +++ b/lib/xmalloc.c @@ -27,34 +27,13 @@ #include #include =20 -/* 1 if calloc, malloc and realloc are known to be compatible with GNU. - This matters if we are not also using the calloc-gnu, malloc-gnu - and realloc-gnu modules, which define HAVE_CALLOC_GNU, - HAVE_MALLOC_GNU and HAVE_REALLOC_GNU and support the GNU API even - on non-GNU platforms. */ -#if defined HAVE_CALLOC_GNU || (defined __GLIBC__ && !defined __UCLIBC__= ) -enum { HAVE_GNU_CALLOC =3D 1 }; -#else -enum { HAVE_GNU_CALLOC =3D 0 }; -#endif -#if defined HAVE_MALLOC_GNU || (defined __GLIBC__ && !defined __UCLIBC__= ) -enum { HAVE_GNU_MALLOC =3D 1 }; -#else -enum { HAVE_GNU_MALLOC =3D 0 }; -#endif -#if defined HAVE_REALLOC_GNU || (defined __GLIBC__ && !defined __UCLIBC_= _) -enum { HAVE_GNU_REALLOC =3D 1 }; -#else -enum { HAVE_GNU_REALLOC =3D 0 }; -#endif - /* Allocate N bytes of memory dynamically, with error checking. */ =20 void * xmalloc (size_t n) { void *p =3D malloc (n); - if (!p && (HAVE_GNU_MALLOC || n)) + if (!p) xalloc_die (); return p; } @@ -65,15 +44,8 @@ xmalloc (size_t n) void * xrealloc (void *p, size_t n) { - if (!HAVE_GNU_REALLOC && !n && p) - { - /* The GNU and C99 realloc behaviors disagree here. Act like GNU.= */ - free (p); - return NULL; - } - void *r =3D realloc (p, n); - if (!r && (n || (HAVE_GNU_REALLOC && !p))) + if (!r && (!p || n)) xalloc_die (); return r; } @@ -175,13 +147,8 @@ xzalloc (size_t n) void * xcalloc (size_t n, size_t s) { - void *p; - /* Test for overflow, since objects with size greater than - PTRDIFF_MAX cause pointer subtraction to go awry. Omit size-zero - tests if HAVE_GNU_CALLOC, since GNU calloc never returns NULL if - successful. */ - if (xalloc_oversized (n, s) - || (! (p =3D calloc (n, s)) && (HAVE_GNU_CALLOC || n !=3D 0))) + void *p =3D calloc (n, s); + if (!p) xalloc_die (); return p; } diff --git a/modules/xalloc b/modules/xalloc index 5fa386a5d..000933e94 100644 --- a/modules/xalloc +++ b/modules/xalloc @@ -8,10 +8,13 @@ m4/xalloc.m4 =20 Depends-on: c99 +calloc-gnu extern-inline idx intprops +malloc-gnu minmax +realloc-gnu stdint xalloc-die xalloc-oversized --=20 2.27.0 --------------73E2EA06FCBEF1A7D45BF60C--