现象
在 shm 后端复用(reopen)已填充的 arena 后继续写入时,sbo_alloc 在 blocks_alloc 内 用户态 100% CPU 死循环,永不返回。gdb 显示进程稳定卡在 blocks_alloc 的 spin_lock cmpxchg 循环里。
调用栈(gdb,稳定复现)
#0 blocks_alloc (libblockmalloc) ← spin_lock(&meta->lock) 死转
#1 sbo_find_alloc (深度 3)
#2 sbo_find_alloc
#3 sbo_find_alloc
#4 sbo_alloc
#5 shm_set_raw (kvspace-c)
... kvspaceSet -> 上层
反汇编确认 #0 停在 spin_lock 的 pause/xchg/test %rdx,%rdx/jne 自旋序列。
根因(已定位)
传给 blocks_alloc 的 blocks_meta_t 是全 0xFF 的未初始化内存:
total_size = 0xFFFFFFFFFFFFFFFF (-1)
malloc_blocks = -1
free_next_id = -1
lock = 1 ← 读到非零“锁”,自旋永远抢不到
地址换算:该 meta 落在 sbo_meta 区内偏移 +0x7e0(2016B),即 sbo_pool_meta(meta, si) 的结果。而本 arena:
data_size = 134217728 (128MB = 8×64⁴)
sbo_align_to(16777216) = {level=5, multiple=1} ⟹ root_slots = 1
即只有 si=0 一个合法 pool。但被下降到的 box 的 slot_id ≈ 40(由 +0x7e0 偏移反推),sbo_pool_meta(meta, 40) 越过 slot_pools[1] 数组、读到未初始化区域 → 全 0xFF meta。
sbo_find_alloc 里两处 sbo_pool_meta(meta, box->slot_id) / sbo_pool_mem(...)(约 slotsboxobj.h:400-401、216-223)信任 box->slot_id。当树在 free + 重新 alloc 翻搅下某个被下降的 box 的 slot_id 变成越界值时,就会取到垃圾 pool。
触发条件
- 全新 arena 单次写入:正常(所有 key 都是新 alloc,无 free)。
- reopen 已填充 arena 后重写同一批 key:挂(约 900 个 key 各做一次
sbo_free+sbo_alloc)。
fresh 无 free,reopen 有大量 free+realloc 翻搅——差异点即 sbo_free/复用路径与 box 树状态维护。
两个应修的问题
-
正确性(主):free + realloc 翻搅后,被 sbo_find_alloc 下降的 box 出现越界 slot_id(root_slots=1 时任何非 0 都非法)。需查 sbo_free_obj_slots / sbo_update_parent / children[] / sbo_child 在复用路径下是否会写坏 box 或 slot_id。注:sbo_free_obj_slots/sbo_alloc_obj_slots 里 if (box->parent > 0) 对 root(parent=0)与「parent 恰为 block 0」的子节点语义有歧义(0 既表示 root 又表示 block-id 0),疑点之一。
-
健壮性(纵深防御):blocks_alloc/spin_lock 对未初始化/损坏的 meta(magic 或 total_size 越界)无任何校验,直接在垃圾 lock 上无限自旋,而非返回 -1 或断言失败。建议 sbo_find_alloc 在 sbo_pool_meta 前校验 box->slot_id < meta->root_slots;blocks_alloc 入口对 meta 做基本合法性检查(如 total_size 合理、magic)。
复现
cd kvlang
SHM=/tmp/x.shm; rm -f $SHM
./bin/kvlanglayout stdlib/string.kv shm://$SHM # rc=0,写入 stdlib
KVSPACE=shm://$SHM ./bin/kvlang test # reopen + 重 layout stdlib → 挂
环境:Linux x86_64;slotsboxmalloc.so 24024B;blockmalloc 0.1.1。
现象
在 shm 后端复用(reopen)已填充的 arena 后继续写入时,
sbo_alloc在blocks_alloc内 用户态 100% CPU 死循环,永不返回。gdb 显示进程稳定卡在blocks_alloc的spin_lockcmpxchg 循环里。调用栈(gdb,稳定复现)
反汇编确认
#0停在spin_lock的pause/xchg/test %rdx,%rdx/jne自旋序列。根因(已定位)
传给
blocks_alloc的blocks_meta_t是全 0xFF 的未初始化内存:地址换算:该 meta 落在
sbo_meta区内偏移+0x7e0(2016B),即sbo_pool_meta(meta, si)的结果。而本 arena:data_size = 134217728(128MB = 8×64⁴)sbo_align_to(16777216) = {level=5, multiple=1}⟹root_slots = 1即只有 si=0 一个合法 pool。但被下降到的 box 的
slot_id ≈ 40(由+0x7e0偏移反推),sbo_pool_meta(meta, 40)越过slot_pools[1]数组、读到未初始化区域 → 全 0xFF meta。sbo_find_alloc里两处sbo_pool_meta(meta, box->slot_id)/sbo_pool_mem(...)(约 slotsboxobj.h:400-401、216-223)信任box->slot_id。当树在 free + 重新 alloc 翻搅下某个被下降的 box 的slot_id变成越界值时,就会取到垃圾 pool。触发条件
sbo_free+sbo_alloc)。fresh 无 free,reopen 有大量 free+realloc 翻搅——差异点即
sbo_free/复用路径与 box 树状态维护。两个应修的问题
正确性(主):free + realloc 翻搅后,被
sbo_find_alloc下降的 box 出现越界slot_id(root_slots=1 时任何非 0 都非法)。需查sbo_free_obj_slots/sbo_update_parent/children[]/sbo_child在复用路径下是否会写坏 box 或slot_id。注:sbo_free_obj_slots/sbo_alloc_obj_slots里if (box->parent > 0)对 root(parent=0)与「parent 恰为 block 0」的子节点语义有歧义(0 既表示 root 又表示 block-id 0),疑点之一。健壮性(纵深防御):
blocks_alloc/spin_lock对未初始化/损坏的meta(magic 或 total_size 越界)无任何校验,直接在垃圾 lock 上无限自旋,而非返回 -1 或断言失败。建议sbo_find_alloc在sbo_pool_meta前校验box->slot_id < meta->root_slots;blocks_alloc入口对meta做基本合法性检查(如 total_size 合理、magic)。复现
环境:Linux x86_64;slotsboxmalloc.so 24024B;blockmalloc 0.1.1。