From: Matan Breizman Date: Sun, 19 Jul 2026 12:07:10 +0000 (+0000) Subject: crimson/seastore: fix LogNode overflow from unsigned underflow in expect_overflow X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=271817bbb8bd52ee74490a9ea12f4a4d4bdac2f7;p=ceph.git crimson/seastore: fix LogNode overflow from unsigned underflow in expect_overflow The can_ow branch of LogManager's expect_overflow computed: ``` remain = capacity() - get_last_pos() - reserved_len ``` all uint32 operands, once the log leaf node is near full (get_last_pos() + reserved_len >= capacity()) this subtraction underflows. The node never rolls to a new leaf, instead it overflows the 16 KB block at commit-time delta replay. Captured at the replay overflow point (per-collection batching, 4K randwrite): ``` LogNode OVERFLOW at replay: pos=16641 size=58 reserved_len=3098 reserved_size=6 capacity=16328 LIMIT=16384 log_node.h : In function 'void LogKVNodeLayout::set_last_pos(uint32_t)', ceph_assert(pos <= LOG_NODE_BLOCK_SIZE) ``` Here reserved_len (3098) is accurate: ~13543 committed + 3098 pending = 16641 = pos, i.e. the node was already ~13.5 KB full and the predictor should have rolled over. Instead capacity - get_last_pos - reserved_len went negative (16328 - ~13273 - 3098 < 0), wrapped in uint32, and the check passed; pos reached 16641 > 16384 and set_last_pos asserted. This was exposed by the per-coll batching where many overwrites append in a single txn. Signed-off-by: Matan Breizman --- diff --git a/src/crimson/os/seastore/omap_manager/log/log_node.cc b/src/crimson/os/seastore/omap_manager/log/log_node.cc index e87a3997b3fe..fdb68aeaccbb 100644 --- a/src/crimson/os/seastore/omap_manager/log/log_node.cc +++ b/src/crimson/os/seastore/omap_manager/log/log_node.cc @@ -324,14 +324,14 @@ bool LogNode::expect_overflow(const std::string &key, size_t ksize = key.size(); if (can_ow) { int gap = ow_gap_from_last_entry(key.size(), vsize); - uint64_t remain = capacity() - get_last_pos() - reserved_len; if (gap >= 0) { gap += static_cast(gap); } else { uint64_t d = static_cast(-gap); gap -= d; } - return remain < get_entry_size(ksize, vsize); + return get_last_pos() + reserved_len + get_entry_size(ksize, vsize) + > capacity(); } else if (get_size() + reserved_size + 1 > d_bitmap_t::MAX_ENTRY) { return true; } else if (is_ow_key(key) && !can_ow) {