-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: History Store
-
None
-
Storage Engines, Storage Engines - Transactions
-
49.846
-
SE Transactions - 2026-07-03
-
1
Problem
Two history store cursor functions — _curhs_remove_int and curhs_update in src/cursor/cur_hs.c — use a retry loop around _wt_hs_modify to handle WT_RESTART:
while ((ret = __wt_hs_modify(cbt, hs_tombstone)) == WT_RESTART) { WT_WITH_PAGE_INDEX(session, ret = __curhs_search(cbt, false)); WT_ERR(ret); } // missing WT_ERR(ret) here
If _wt_hs_modify returns a non-WT_RESTART error (e.g. OOM during wt_page_modify_init, or a write conflict from wt_txn_modify_check), the loop exits with ret != 0 but there is no WT_ERR(ret) to trigger the cleanup at err:. The allocated hs_tombstone (and hs_upd in _curhs_update) are leaked.
The first history store insertion path (the do...while loop around line 1090) already uses the correct pattern:
} while ((ret = __wt_hs_modify(cbt, hs_upd)) == WT_RESTART);
WT_ERR(ret);
Fix
Add WT_ERR(ret) immediately after the closing brace of each while loop. The hs_tombstone = hs_upd = NULL assignment added after is purely defensive — since no fallible code follows the WT_ERR(ret) in either function, there is no actual double-free risk. The null assignment is kept to make the ownership transfer explicit to future readers and to match the established idiom in the file.
Testing
The fix was verified to compile cleanly across all build targets.