This for clarity and to document the overflow checking better; I think no current Gnulib or glibc targets have SIZE_MAX <= INTMAX, so it shouldn’t change behavior on current targets. * lib/obstack.c: Include stdckdint.h, not limits.h. Remove no-longer needed ! (SIZE_MAX <= INT_MAX) check. (align_chunk_size_up): Rename from align_size_up, and change API to detect overflow directly. All callers changed. (align_chunk_size_up, _obstack_newchunk): Use ckd_add instead of checking overflow by hand in a way that assumed INT_MAX < SIZE_MAX. (call_chunkfun, _obstack_begin_worker, _obstack_newchunk): Accept and use _OBSTACK_CHUNK_SIZE_T not size_t. This affects only glibc, since the two types are the same in Gnulib. All callers changed. * modules/obstack (Depends-on): Add stdckdint-h. --- ChangeLog | 18 +++++++++++++ lib/obstack.c | 67 +++++++++++++++++++++---------------------------- modules/obstack | 1 + 3 files changed, 47 insertions(+), 39 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 68b375e9b5..d60a9e5658 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2025-05-06 Paul Eggert <egg...@cs.ucla.edu> + + obstack: don’t assume INT_MAX < SIZE_MAX + This for clarity and to document the overflow checking better; + I think no current Gnulib or glibc targets have SIZE_MAX <= INTMAX, + so it shouldn’t change behavior on current targets. + * lib/obstack.c: Include stdckdint.h, not limits.h. + Remove no-longer needed ! (SIZE_MAX <= INT_MAX) check. + (align_chunk_size_up): Rename from align_size_up, + and change API to detect overflow directly. All callers changed. + (align_chunk_size_up, _obstack_newchunk): Use ckd_add instead of + checking overflow by hand in a way that assumed INT_MAX < SIZE_MAX. + (call_chunkfun, _obstack_begin_worker, _obstack_newchunk): + Accept and use _OBSTACK_CHUNK_SIZE_T not size_t. This affects + only glibc, since the two types are the same in Gnulib. All + callers changed. + * modules/obstack (Depends-on): Add stdckdint-h. + 2025-05-06 Bruno Haible <br...@clisp.org> obstack: Fix library namespacing (regression yesterday). diff --git a/lib/obstack.c b/lib/obstack.c index 8346c898ea..2313437e3a 100644 --- a/lib/obstack.c +++ b/lib/obstack.c @@ -36,20 +36,11 @@ # define __alignof__(type) alignof (type) #endif -#include <limits.h> +#include <stdckdint.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> -/* Some work would need to be done to port this module - to unusual platforms where size_t fits in int. - For now, document the assumption that INT_MAX < SIZE_MAX, - and therefore size_t calculations are modulo SIZE_MAX + 1 - instead of having undefined behavior on overflow. */ -#if SIZE_MAX <= INT_MAX - #error "SIZE_MAX <= INT_MAX" -#endif - #ifndef _OBSTACK_NO_ERROR_HANDLER /* The functions allocating more room by calling 'obstack_chunk_alloc' @@ -80,12 +71,14 @@ struct obstack *_obstack_compat = NULL; compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0); #endif -/* Return the least multiple of MASK + 1 that is not less than SIZE. - MASK + 1 must be a power of 2. On overflow, return zero. */ -static size_t -align_size_up (size_t mask, size_t size) +/* Set *R to the least multiple of MASK + 1 that is not less than SIZE. + MASK + 1 must be a power of 2. Return true (setting *R = 0) + if the result overflows, false otherwise. */ +static bool +align_chunk_size_up (_OBSTACK_CHUNK_SIZE_T *r, size_t mask, + _OBSTACK_CHUNK_SIZE_T size) { - return size + (mask & -size); + return ckd_add (r, mask & -size, size); } /* Call functions with either the traditional malloc/free calling @@ -93,7 +86,7 @@ align_size_up (size_t mask, size_t size) argument), based on the value of use_extra_arg. */ static void * -call_chunkfun (struct obstack *h, size_t size) +call_chunkfun (struct obstack *h, _OBSTACK_CHUNK_SIZE_T size) { if (h->use_extra_arg) return h->chunkfun.extra (h->extra_arg, size); @@ -131,15 +124,12 @@ _obstack_begin_worker (struct obstack *h, _obstack_chunk sans contents, followed by minimal padding, up to but possibly not including the start of an aligned object. This value is zero if no size is large enough. */ - size_t aligned_prefix_size - = align_size_up (alignment - 1, - (alignment - 1 - + offsetof (struct _obstack_chunk, contents))); - - size_t size = chunk_size; - if (!aligned_prefix_size) - size = 0; - else if (size < aligned_prefix_size) + _OBSTACK_CHUNK_SIZE_T aligned_prefix_size; + bool v = align_chunk_size_up (&aligned_prefix_size, alignment - 1, + offsetof (struct _obstack_chunk, contents)); + + _OBSTACK_CHUNK_SIZE_T size = chunk_size; + if (size < aligned_prefix_size) { size = aligned_prefix_size; @@ -152,8 +142,7 @@ _obstack_begin_worker (struct obstack *h, h->chunk_size = size; h->alignment_mask = alignment - 1; - chunk = h->chunk = (0 < h->chunk_size && h->chunk_size == size - ? call_chunkfun (h, size) : NULL); + chunk = h->chunk = v ? NULL : call_chunkfun (h, size); if (!chunk) (*obstack_alloc_failed_handler) (); h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents, @@ -205,25 +194,25 @@ void _obstack_newchunk (struct obstack *h, _OBSTACK_INDEX_T length) { struct _obstack_chunk *old_chunk = h->chunk; - struct _obstack_chunk *new_chunk = NULL; + struct _obstack_chunk *new_chunk; size_t obj_size = h->next_free - h->object_base; char *object_base; /* Compute size for new chunk. */ - size_t sum1 = obj_size + length; - size_t sum1a = align_size_up (h->alignment_mask, sum1); - size_t sum2 = (offsetof (struct _obstack_chunk, contents) - + h->alignment_mask + sum1a); - size_t new_size = sum2 + (obj_size >> 3) + 100; - if (new_size < sum2) - new_size = sum2; + _OBSTACK_CHUNK_SIZE_T s, new_size; + bool v = length < 0; + v |= ckd_add (&s, obj_size, length); + v |= align_chunk_size_up (&s, h->alignment_mask, s); + v |= ckd_add (&s, s, + (offsetof (struct _obstack_chunk, contents) + + h->alignment_mask)); + if (ckd_add (&new_size, s, (obj_size >> 3) + 100)) + new_size = s; if (new_size < h->chunk_size) new_size = h->chunk_size; - /* Allocate and initialize the new chunk, - checking for overflow and for nonpositive LENGTH. */ - if (obj_size < sum1 && sum1a && sum1a < sum2) - new_chunk = call_chunkfun (h, new_size); + /* Allocate and initialize the new chunk. */ + new_chunk = v ? NULL : call_chunkfun (h, new_size); if (!new_chunk) (*obstack_alloc_failed_handler)(); h->chunk = new_chunk; diff --git a/modules/obstack b/modules/obstack index 4c29255669..0d6c266ae9 100644 --- a/modules/obstack +++ b/modules/obstack @@ -14,6 +14,7 @@ flexmember [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] gettext-h [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] gnulib-i18n [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] exitfail [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] +stdckdint-h [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] stddef-h [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] stdint-h [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] stdlib-h [test $HAVE_OBSTACK = 0 || test $REPLACE_OBSTACK = 1] -- 2.49.0