fix(compress): keep Fast dictionary projection live

The Fast dictionary-table projection was populated before the Rust
orchestrator ran its window-update and match-state publication callbacks.
That left base and nextToUpdate pointing at the initial sentinel for MT
prefix jobs, which caused ZSTD_rust_fillHashTable to dereference an invalid
range. Project pointers to the live match-state fields so Rust reads the
same values the former C adapter would have read at the Fast branch.

Test Plan:
- git diff --check
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo check --manifest-path rust/Cargo.toml --tests
- ulimit -v 41943040; make -j1
- capped 4 MiB -T0 compress/decompress round trip with cmp
This commit is contained in:
2026-07-21 20:52:27 +02:00
parent 4a3d5b9d27
commit 846e18f6d9
2 changed files with 63 additions and 60 deletions
+18 -17
View File
@@ -2780,11 +2780,11 @@ typedef void (*ZSTD_rust_loadDictionaryContent_overflowCorrect_f)(
* fields needed by that leaf in the ABI projection; the complete
* ZSTD_MatchState_t layout remains private to C. */
typedef struct {
U32* hashTable;
const BYTE* base;
U32 nextToUpdate;
U32 hashLog;
U32 minMatch;
U32** hashTable;
const BYTE** base;
const U32* nextToUpdate;
const U32* hashLog;
const U32* minMatch;
int fullTableLoad;
int forCDict;
} ZSTD_rust_loadDictionaryContentFastTableState;
@@ -2795,17 +2795,15 @@ typedef char ZSTD_rust_load_dictionary_fast_table_layout[
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, nextToUpdate)
== 2 * sizeof(void*)
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, hashLog)
== 2 * sizeof(void*) + sizeof(U32)
== 3 * sizeof(void*)
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, minMatch)
== 2 * sizeof(void*) + 2 * sizeof(U32)
== 4 * sizeof(void*)
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, fullTableLoad)
== 2 * sizeof(void*) + 3 * sizeof(U32)
== 5 * sizeof(void*)
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, forCDict)
== 2 * sizeof(void*) + 3 * sizeof(U32) + sizeof(int)
== 5 * sizeof(void*) + sizeof(int)
&& sizeof(ZSTD_rust_loadDictionaryContentFastTableState)
== ((offsetof(ZSTD_rust_loadDictionaryContentFastTableState, forCDict)
+ sizeof(int) + sizeof(void*) - 1)
/ sizeof(void*) * sizeof(void*)))
== 5 * sizeof(void*) + 2 * sizeof(int))
? 1 : -1];
typedef void (*ZSTD_rust_loadDictionaryContent_fillTable_f)(
void* context, const void* iend, int dtlm, int tfp);
@@ -7016,11 +7014,14 @@ static size_t ZSTD_loadDictionaryContent_callback(
assert((tfp == ZSTD_tfp_forCDict && dtlm == ZSTD_dtlm_full)
|| (tfp != ZSTD_tfp_forCDict && dtlm == ZSTD_dtlm_fast));
fastTable.hashTable = ms->hashTable;
fastTable.base = ms->window.base;
fastTable.nextToUpdate = ms->nextToUpdate;
fastTable.hashLog = ms->cParams.hashLog;
fastTable.minMatch = ms->cParams.minMatch;
/* These fields are mutated by the window/publish callbacks before the
* Fast branch runs. Keep pointers to the live match-state fields rather
* than a stale snapshot taken before the Rust orchestrator starts. */
fastTable.hashTable = &ms->hashTable;
fastTable.base = &ms->window.base;
fastTable.nextToUpdate = &ms->nextToUpdate;
fastTable.hashLog = &ms->cParams.hashLog;
fastTable.minMatch = &ms->cParams.minMatch;
fastTable.fullTableLoad = dtlm == ZSTD_dtlm_full;
fastTable.forCDict = tfp == ZSTD_tfp_forCDict;