From 1466fb2400af367db9d0cb1041020278a871a4f3 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 30 Oct 2024 13:37:02 -0400 Subject: alloc: Stop supporting pathological flexible array ABIs --- src/alloc.c | 6 +++--- src/alloc.h | 41 ++++++++++++++++++++++++++--------------- tests/alloc.c | 4 ---- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 4e68e13..79e4ce7 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -247,7 +247,7 @@ void arena_destroy(struct arena *arena) { sanitize_uninit(arena); } -void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, size_t size) { +void varena_init(struct varena *varena, size_t align, size_t offset, size_t size) { varena->align = align; varena->offset = offset; varena->size = size; @@ -256,7 +256,7 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, // The smallest size class is at least as many as fit in the smallest // aligned allocation size - size_t min_count = (flex_size(align, min, offset, size, 1) - offset + size - 1) / size; + size_t min_count = (flex_size(align, offset, size, 1) - offset + size - 1) / size; varena->shift = bit_width(min_count - 1); } @@ -269,7 +269,7 @@ static size_t varena_size_class(struct varena *varena, size_t count) { /** Get the exact size of a flexible struct. */ static size_t varena_exact_size(const struct varena *varena, size_t count) { - return flex_size(varena->align, 0, varena->offset, varena->size, count); + return flex_size(varena->align, varena->offset, varena->size, count); } /** Get the arena for the given array length. */ diff --git a/src/alloc.h b/src/alloc.h index 34d9273..1fafbab 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -62,13 +62,31 @@ static inline size_t size_mul(size_t size, size_t count) { #define sizeof_member(type, member) \ sizeof(((type *)NULL)->member) +/** + * @internal + * Our flexible struct size calculations assume that structs have the minimum + * trailing padding to align the type properly. A pathological ABI that adds + * extra padding would result in us under-allocating space for those structs, + * so we static_assert() that no such padding exists. + */ +#define ASSERT_FLEX_ABI(type, member) \ + ASSERT_FLEX_ABI_( \ + ALIGN_CEIL(alignof(type), offsetof(type, member)) >= sizeof(type), \ + "Unexpected tail padding in " #type) + +/** + * @internal + * The contortions here allow static_assert() to be used in expressions, rather + * than just declarations. + */ +#define ASSERT_FLEX_ABI_(...) \ + ((void)sizeof(struct { char _; static_assert(__VA_ARGS__); })) + /** * Saturating flexible struct size. * * @align * Struct alignment. - * @min - * Minimum struct size. * @offset * Flexible array member offset. * @size @@ -79,17 +97,10 @@ static inline size_t size_mul(size_t size, size_t count) { * The size of the struct with count flexible array elements. Saturates * to the maximum aligned value on overflow. */ -static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t size, size_t count) { +static inline size_t flex_size(size_t align, size_t offset, size_t size, size_t count) { size_t ret = size_mul(size, count); ret = size_add(ret, offset + align - 1); ret = align_floor(align, ret); - - // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the - // type has more padding than necessary for alignment - if (min > align_ceil(align, offset)) { - ret = ret < min ? min : ret; - } - return ret; } @@ -107,7 +118,8 @@ static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t s * to the maximum aligned value on overflow. */ #define sizeof_flex(type, member, count) \ - flex_size(alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0]), count) + (ASSERT_FLEX_ABI(type, member), flex_size( \ + alignof(type), offsetof(type, member), sizeof_member(type, member[0]), count)) /** * General memory allocator. @@ -298,14 +310,12 @@ struct varena { * The varena to initialize. * @align * alignof(type) - * @min - * sizeof(type) * @offset * offsetof(type, flexible_array) * @size * sizeof(flexible_array[i]) */ -void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, size_t size); +void varena_init(struct varena *varena, size_t align, size_t offset, size_t size); /** * Initialize a varena for the given type and flexible array. @@ -318,7 +328,8 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, * The name of the flexible array member. */ #define VARENA_INIT(varena, type, member) \ - varena_init(varena, alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0])) + (ASSERT_FLEX_ABI(type, member), varena_init( \ + varena, alignof(type), offsetof(type, member), sizeof_member(type, member[0]))) /** * Free an arena-allocated flexible struct. diff --git a/tests/alloc.c b/tests/alloc.c index 046613a..5fc934b 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -30,10 +30,6 @@ void check_alloc(void) { size_t too_many = SIZE_MAX / sizeof(int) + 1; bfs_check(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); - // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) - // Doesn't happen in typical ABIs - bfs_check(flex_size(8, 16, 4, 4, 1) == 16); - // Make sure we detect allocation size overflows #if __GNUC__ && !__clang__ # pragma GCC diagnostic ignored "-Walloc-size-larger-than=" -- cgit v1.2.3