-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: Block Cache
-
None
-
Storage Engines - Persistence
-
316.971
-
SE Persistence backlog
-
None
Found while reading the victim cache path during the AF-21528 investigation. Independent of the staleness bug filed alongside this one.
Summary
__evict_page_victim_cache() builds buf_orig directly over page->dsk. If compression is skipped or fails, cache_buf still points at the live page image, and the function then writes the disagg block header into that live image in place.
WT_ITEM buf_orig = {
.data = page->dsk,
.size = page->dsk->mem_size,
.mem = (void *)page->dsk,
...
};
WT_ITEM *cache_buf = &buf_orig;
...
if (compressed_buf != NULL)
cache_buf = compressed_buf;
...
WT_BLOCK_DISAGG_HEADER *blk = WT_BLOCK_HEADER_REF(cache_buf->data);
memset(blk, 0, sizeof(*blk));
blk->magic = WT_BLOCK_DISAGG_MAGIC_BASE;
blk->previous_checksum = page->disagg_info->block_meta.checksum;
F_SET(blk, WT_BLOCK_DISAGG_MODIFIED);
blk->checksum = __wt_checksum(...);
__wt_page_header_byteswap(dsk);
At the end only the page-header byteswap is undone:
if (compressed_buf != NULL) __wt_scr_free(session, &compressed_buf); else __wt_page_header_byteswap(dsk);
The disagg block header is left permanently overwritten: magic, flags (including WT_BLOCK_DISAGG_MODIFIED), checksum and previous_checksum.
This is not only an error path. __wt_blkcache_compress() reports "block too small" and "incompressible" with a zero return and no output buffer, so small or incompressible leaf pages take it routinely.
Why it matters
page->dsk is frequently not owned by the page. On a shared-disk-cache hit, bt_read.c:330-334 passes shared_dsk_item->data straight into __wti_page_inmem(), so page->dsk aliases the cache item's buffer, which is refcounted and can be shared by more than one reader. In the AF-21528 core both pointers were 0x721deefd6000 with ref_count = 1.
Stamping the header in place therefore scribbles on a buffer the shared disk cache owns, corrupting the stored image's header for any subsequent or concurrent reader of that entry.
Suggested fix
Copy the image into a scratch buffer before stamping the header when compression did not produce one, or save and restore the original disagg block header around the put.