ExportXMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Major - P3
    • WT12.0.0, 9.1.0-rc0
    • Affects Version/s: None
    • Component/s: Block Cache
    • 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.

            Assignee:
            Will Korteland
            Reporter:
            Chenhao Qu
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: