Decouple __wt_overwrite_and_free from HAVE_DIAGNOSTIC so it can be enabled in release builds

XMLWordPrintableJSON

    • Type: Task
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Observability
    • None
    • Storage Engines - Transactions
    • 220.411
    • SE Transactions - 2026-09-25
    • 3

      __wt_overwrite_and_free() / _wt_overwrite_and_free_len() functions scribble WT_DEBUG_BYTE (0xab) over a structure before freeing it, making use-after-free bugs deterministic and easier to identify. Today this is gated on HAVE_DIAGNOSTIC, so it is absent from every release build.

      Give this code its own switch so it can be enabled independently of a full diagnostic build.

      There are 15 call sites (11 + 4 _len):

      File:line Object
      block_open.c:140 block
      block_mgr.c:350 bm
      block_cache.c:540 blkcache_item
      bt_handle.c:258 btree
      bt_read.c:172, bt_delete.c:437, rec_child.c:183 ref->page_del
      bt_split.c:44 split page-index (_len)
      bt_discard.c:132 page disk image (_len, dsk->mem_size)
      bt_discard.c:134 page
      bt_discard.c:360 ref (_len, WT_REF_CLEAR_SIZE)
      conn_dhandle.c:168 dhandle
      cur_std.c:1145 cursor
      session_dhandle.c:98 dhandle_cache
      generation.c:432 split-generation stash (_len)

      Currently on release builds, this call defaults to wt_free, so it could present as silent data corruption that crashes arbitrarily at a later point. This makes the bugs expensive to diagnose in production. 

      WT_DEBUG_BYTE would fault immediately and never present as valid memory. Proposed change: 

      Option 1: debug_mode=(overwrite_free=true)

      debug_mode is an existing config category with 15 sub-options, backed by WT_CONN_DEBUG_* flags. One of them realloc_malloc=true already does this operation of using __wt_explicit_overwrite

          if (session != NULL && FLD_ISSET(S2C(session)->debug_flags, WT_CONN_DEBUG_REALLOC_MALLOC) &&
            (bytes_allocated_ret != NULL)) {
              if ((p = malloc(bytes_to_allocate)) == NULL)
                  WT_RET_MSG(session, __wt_errno(), "memory allocation of %" WT_SIZET_FMT " bytes failed",
                    bytes_to_allocate);
              if (tmpp != NULL) {
                  memcpy(p, tmpp, *bytes_allocated_ret);
                  __wt_explicit_overwrite(tmpp, bytes_allocated);
                  __wt_free(session, tmpp);
              }
      

      Option 2: dedicated CMake option

      HAVE_REF_TRACK does something similar where we have a diagnostic sub-feature, independently switchable from diagnostic builds. 

      Performance Impact:

      We should not enable both macro variants with a single switch. Since some of the paths are hot. Either use two separate switches, or cap the _len variant:

      __wt_explicit_overwrite(p, WT_MIN(len, WT_POISON_MAX));

      Poisoning the first 64–128 bytes destroys the WT_PAGE_HEADER, which is read first.

      Enabling this converts any currently-benign stale read into a hard crash, could potentially affect current non-crashing nodes. We should also confirm if there is a significant performance impact from turning this on in production builds. 

            Assignee:
            [DO NOT USE] Backlog - Storage Engines Team
            Reporter:
            Alana Huang
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: