Repository navigation
slab/multislab/pool: hardening and limits #6
Copy link
Copy link
Closed
Description
| # | Where | Problem | Fix |
|---|---|---|---|
| 1 | multislab::deallocate |
Release build: a double free still decrements used, so the slab is freed with live blocks (confirmed). Foreign pointer: find_owner returns null, then dereferenced. cfiber 1fd1edf rejects both. |
Decide: reject or documented UB. Rejecting means returning on null owner, misaligned offset or clear bit, before touching used. |
| 2 | multislab::grow (:492) |
A throwing slab allocation leaks the node header (confirmed). | Scope guard, or a single allocation for header + slab. |
| 3 | slab::slab (:118) |
block_count_ <= MaxBlocks assert rejects more memory than required_memory and accepts less. Narrows to uint32_t before checking. |
min(memory_size / BlockSize, MaxBlocks) in size_t, assert >= 1, fix the doc. |
| 4 | pool::erase (:389) |
erase(end()) dereferences null; only size_ > 0 is asserted. |
assert(it != end()). |
| 5 | slab::owns, ptr_to_index, deallocate |
constexpr with reinterpret_cast: well-formed (P2448), never constant-evaluable. |
Drop constexpr. (std::byte* subtraction under if consteval needs the C++26 void* cast, P2738.) |
| 6 | geometry | BlockSize * MaxBlocks wraps silently; words_for overflows near UINT32_MAX; bitmap_find_free returns an int32_t index. |
requires on no overflow and MaxBlocks <= INT32_MAX, or use capacity as the not-found value. |
| 7 | slab_limit (pool.cppm:87) |
Implicit: pool<int> p(4) is a cap, p{4} an element. slab_limit{-1} is UINT32_MAX (confirmed). |
Decide: explicit plus unsigned_integral or a non-negative assert. The comment at :84 is wrong: explicit still accepts slab_limit{4}. |
Contract gate
Library checks are plain assert, so a consumer's contract evaluation semantic does not reach them. datagram_engine passes -fcontract-evaluation-semantic=enforce|ignore directory-wide, libmem sources included, and libmem ignores it.
- Add a header, included in each
.cppmglobal module fragment where<cassert>is today:#if defined(__cpp_contracts) #define LIBMEM_ASSERT(...) contract_assert(__VA_ARGS__) #else #define LIBMEM_ASSERT(...) assert(__VA_ARGS__) #endif
- Replace every runtime
assertinsrc/libmem/with it, not only those in this issue. Checks written for 1, 3 and 4 use it too. pre/postwait for Clang. Adding them later needs no standard bump.- GCC 15 has no
__cpp_contractsand getsassert, so the floor stays.
Tests
Death tests for 1 (debug) and 4, run on both paths. In a GCC leg, static_assert(__cpp_contracts) when __GNUC__ >= 16, so a toolchain change cannot silently drop libmem back to assert. A throwing resource for 2 under LSan. Oversized and undersized memory for 3. A compile-fail check or static_assert for 7.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity