仅FreeBSD存在的arena分配器单元测试失败问题求助
Arena分配器FreeBSD 14.0测试失败排查
问题描述
我开发的arena内存分配器在Linux、Windows、macOS系统上运行单元测试全部通过,但在FreeBSD 14.0系统中出现测试失败:测试检查curr_pool[96]至curr_pool[99]是否被填充为0xA5时失败,实际值均为0。
核心分配函数代码
#ifdef DEBUG #include <string.h> #define D(x) x #else #define D(x) (void) 0 #endif /* Allocates a pointer from `arena`. * * The allocated pointer is at least aligned to `alignment`. * * `alignment` must be a power of 2. * * `size` must be a multiple of `alignment`. * * If a request can not be entertained, i.e. would overflow, or `arena` is full, * the function returns `nullptr`. The function also returns a `nullptr` if the * requested `size` or `alignment` is 0 or if `alignment` is not a power of 2, * or if `size` is not a multiple of `alignment`. * * Any allocations made prior to this call are not freed on failure, and remain * valid until the arena is either reset or destroyed. */ void *arena_alloc(Arena *arena, size_t alignment, size_t size) { if (size == 0 || alignment == 0 || (alignment != 1 && !is_power_of_two(alignment)) || !is_multiple_of(size, alignment)) { return nullptr; } M_Pool *curr_pool = arena->pools[arena->current - 1]; uint8_t *const p = curr_pool->buf + curr_pool->offset; const uintptr_t original = ((uintptr_t) p); if (original > UINTPTR_MAX - alignment) { return nullptr; } const uintptr_t remain = original & (alignment - 1); const uintptr_t aligned = remain != 0 ? original + (alignment - remain) : original; const size_t offset = aligned - original; if (size > SIZE_MAX - offset) { return nullptr; } size += offset; if (size > curr_pool->buf_len - curr_pool->offset) { return nullptr; } /* Set the optional padding for alignment immediately before a user block, * and the bytes immediately following such a block to non-zero. * The intent is to trigger OBOB failures to inappropiate app use of * strlen()/strnlen(), which keep forging ahead till encountering ascii NUL. */ D( /* 0xA5 is used in FreeBSD's PHK malloc for debugging purposes. */ if (remain) { memset(p + (alignment - remain), 0xA5, alignment - remain); } ); curr_pool->offset += size; D(memset(curr_pool->buf + curr_pool->offset, 0xA5, curr_pool->buf_len - curr_pool->offset)); arena->last_alloc_size = size; /* Equal to "aligned", but preserves provenance. */ return p + offset; }
单元测试代码
static void test_arena_alloc(void) { Arena *const arena = arena_new(nullptr, 100); TEST_ASSERT(arena); TEST_CHECK(arena_alloc(arena, 1, 112) == nullptr); TEST_CHECK(arena_alloc(arena, 0, 1) == nullptr); TEST_CHECK(arena_alloc(arena, 1, 0) == nullptr); TEST_CHECK(arena_alloc(arena, 2, 5) == nullptr); TEST_CHECK(arena_alloc(arena, 3, 5) == nullptr); TEST_CHECK(arena_alloc(arena, 1, 95)); uint8_t *const curr_pool = arena->pools[0]->buf; TEST_CHECK(curr_pool[96] == 0xA5 && curr_pool[97] == 0xA5 && curr_pool[98] == 0xA5 && curr_pool[99] == 0xA5); arena_reset(arena); #ifdef HAVE_STDALIGN_H const int *const a = arena_alloc(arena, alignof (int), 5 * sizeof *a); const double *const b = arena_alloc(arena, alignof (double), 2 * sizeof *b); const char *const c = arena_alloc(arena, 1, 10); const short *const d = arena_alloc(arena, alignof (short), 5 * sizeof *d); TEST_CHECK(a && is_aligned(a, alignof (int))); TEST_CHECK(b && is_aligned(b, alignof (double))); TEST_CHECK(c && is_aligned(c, 1)); TEST_CHECK(d && is_aligned(d, alignof (short))); #endif arena_destroy(arena); }
各平台测试输出
Linux/Windows/macOS测试通过输出
cc -std=c11 -fPIC -Wall -Wextra -Werror -Wwrite-strings -Wno-unused-variable -Wno-parentheses -Wpedantic -Warray-bounds -Wno-unused-function -Wstrict-prototypes -Wdeprecated -DDEBUG tests.c -o tests ./tests arena_alloc --verbose=3 Test arena_alloc: tests.c:92: arena... ok tests.c:94: arena_alloc(arena, 1, 112) == nullptr... ok tests.c:95: arena_alloc(arena, 0, 1) == nullptr... ok tests.c:96: arena_alloc(arena, 1, 0) == nullptr... ok tests.c:97: arena_alloc(arena, 2, 5) == nullptr... ok tests.c:98: arena_alloc(arena, 3, 5) == nullptr... ok tests.c:100: arena_alloc(arena, 1, 95)... ok tests.c:103: curr_pool[96] == 0xA5 && curr_pool[97] == 0xA5 && curr_pool[98] == 0xA5 && curr_pool[99] == 0xA5... ok tests.c:125: a && is_aligned(a, alignof (int))... ok tests.c:126: b && is_aligned(b, alignof (double))... ok tests.c:127: c && is_aligned(c, 1)... ok tests.c:128: d && is_aligned(d, alignof (short))... ok SUCCESS: All conditions have passed. Summary: Count of run unit tests: 1 Count of successful unit tests: 1 Count of failed unit tests: 0 SUCCESS: No unit tests have failed.
FreeBSD 14.0测试失败输出
Test arena_alloc: tests.c:92: arena... ok tests.c:94: arena_alloc(arena, 1, 112) == nullptr... ok tests.c:95: arena_alloc(arena, 0, 1) == nullptr... ok tests.c:96: arena_alloc(arena, 1, 0) == nullptr... ok tests.c:97: arena_alloc(arena, 2, 5) == nullptr... ok tests.c:98: arena_alloc(arena, 3, 5) == nullptr... ok tests.c:100: arena_alloc(arena, 1, 95)... ok tests.c:104: curr_pool[96] == 0xA5 && curr_pool[97] == 0xA5 && curr_pool[98] == 0xA5 && curr_pool[99] == 0xA5... failed tests.c:114: a && is_aligned(a, alignof (int))... ok tests.c:115: b && is_aligned(b, alignof (double))... ok tests.c:116: c && is_aligned(c, 1)... ok tests.c:117: d && is_aligned(d, alignof (short))... ok FAILED: 1 condition has failed.
编译配置(Makefile)
CFLAGS += -std=c11 CFLAGS += -fPIC CFLAGS += -Wall CFLAGS += -Wextra CFLAGS += -Werror CFLAGS += -Wwrite-strings CFLAGS += -Wno-unused-variable CFLAGS += -Wno-parentheses CFLAGS += -Wpedantic CFLAGS += -Warray-bounds CFLAGS += -Wno-unused-function CFLAGS += -Wstrict-prototypes CFLAGS += -Wdeprecated TARGET := arena TEST_TARGET := tests SLIB_TARGET := libarena.a DLIB_TARGET := libarena.so RM := /bin/rm -f release: CFLAGS += -O2 -s -DTEST_MAIN release: $(TARGET) debug: CFLAGS += -DTEST_MAIN -DDEBUG -g3 -ggdb -fsanitize=address,leak,undefined debug: $(TARGET) static: $(SLIB_TARGET) $(SLIB_TARGET): $(TARGET).o $(AR) rcs $@ $^ shared: $(DLIB_TARGET) shared: LDFLAGS += -shared $(DLIB_TARGET): $(TARGET).o $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) test: CFLAGS += -DDEBUG test: $(TEST_TARGET) ./$(TEST_TARGET) --verbose=3 clean: $(RM) $(TARGET) $(TEST_TARGET) $(TARGET).o $(SLIB_TARGET) $(DLIB_TARGET) .PHONY: release debug static shared test clean .DELETE_ON_ERROR:
问题排查与解决方案
可能原因分析
- DEBUG宏未正确定义:填充
0xA5的逻辑被包裹在D()宏中,仅在DEBUG模式下执行。虽然Makefile的test目标指定了-DDEBUG,但FreeBSD的clang编译器可能存在宏定义优先级或传递问题,导致实际编译时未启用DEBUG逻辑。 - Pool缓冲区长度不符预期:测试中创建的arena指定每个pool大小为100,但如果
arena_new函数在FreeBSD下对内存大小做了对齐调整(比如向上对齐到系统页边界或其他值),会导致curr_pool->buf_len不等于100,从而memset填充的字节数为0或不足。 - 编译器优化干扰:FreeBSD下的clang可能在DEBUG模式下仍优化掉无直接副作用的
memset操作。
排查与修复步骤
- 验证DEBUG宏是否生效:在测试代码中添加如下代码,编译时若未定义DEBUG会触发编译错误:
#ifndef DEBUG #error "DEBUG macro is not defined!" #endif - 检查Pool缓冲区实际长度:在测试中添加打印语句,确认
curr_pool->buf_len的值:TEST_CHECK(arena_alloc(arena, 1, 95)); uint8_t *const curr_pool = arena->pools[0]->buf; printf("buf_len: %zu, offset: %zu, fill count: %zu\n", arena->pools[0]->buf_len, arena->pools[0]->offset, arena->pools[0]->buf_len - arena->pools[0]->offset); - 强制禁用优化:在Makefile的
test目标中添加-O0,避免编译器优化掉memset操作:test: CFLAGS += -DDEBUG -O0 - 检查arena_new的内存分配逻辑:确认
arena_new中是否对传入的pool大小做了调整,若有则确保调整后的大小符合测试预期。
内容的提问来源于stack exchange,提问作者Madagascar
相关产品推荐
相关产品推荐

