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:
@@ -2780,11 +2780,11 @@ typedef void (*ZSTD_rust_loadDictionaryContent_overflowCorrect_f)(
|
|||||||
* fields needed by that leaf in the ABI projection; the complete
|
* fields needed by that leaf in the ABI projection; the complete
|
||||||
* ZSTD_MatchState_t layout remains private to C. */
|
* ZSTD_MatchState_t layout remains private to C. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
U32* hashTable;
|
U32** hashTable;
|
||||||
const BYTE* base;
|
const BYTE** base;
|
||||||
U32 nextToUpdate;
|
const U32* nextToUpdate;
|
||||||
U32 hashLog;
|
const U32* hashLog;
|
||||||
U32 minMatch;
|
const U32* minMatch;
|
||||||
int fullTableLoad;
|
int fullTableLoad;
|
||||||
int forCDict;
|
int forCDict;
|
||||||
} ZSTD_rust_loadDictionaryContentFastTableState;
|
} ZSTD_rust_loadDictionaryContentFastTableState;
|
||||||
@@ -2795,17 +2795,15 @@ typedef char ZSTD_rust_load_dictionary_fast_table_layout[
|
|||||||
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, nextToUpdate)
|
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, nextToUpdate)
|
||||||
== 2 * sizeof(void*)
|
== 2 * sizeof(void*)
|
||||||
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, hashLog)
|
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, hashLog)
|
||||||
== 2 * sizeof(void*) + sizeof(U32)
|
== 3 * sizeof(void*)
|
||||||
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, minMatch)
|
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, minMatch)
|
||||||
== 2 * sizeof(void*) + 2 * sizeof(U32)
|
== 4 * sizeof(void*)
|
||||||
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, fullTableLoad)
|
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, fullTableLoad)
|
||||||
== 2 * sizeof(void*) + 3 * sizeof(U32)
|
== 5 * sizeof(void*)
|
||||||
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, forCDict)
|
&& offsetof(ZSTD_rust_loadDictionaryContentFastTableState, forCDict)
|
||||||
== 2 * sizeof(void*) + 3 * sizeof(U32) + sizeof(int)
|
== 5 * sizeof(void*) + sizeof(int)
|
||||||
&& sizeof(ZSTD_rust_loadDictionaryContentFastTableState)
|
&& sizeof(ZSTD_rust_loadDictionaryContentFastTableState)
|
||||||
== ((offsetof(ZSTD_rust_loadDictionaryContentFastTableState, forCDict)
|
== 5 * sizeof(void*) + 2 * sizeof(int))
|
||||||
+ sizeof(int) + sizeof(void*) - 1)
|
|
||||||
/ sizeof(void*) * sizeof(void*)))
|
|
||||||
? 1 : -1];
|
? 1 : -1];
|
||||||
typedef void (*ZSTD_rust_loadDictionaryContent_fillTable_f)(
|
typedef void (*ZSTD_rust_loadDictionaryContent_fillTable_f)(
|
||||||
void* context, const void* iend, int dtlm, int tfp);
|
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)
|
assert((tfp == ZSTD_tfp_forCDict && dtlm == ZSTD_dtlm_full)
|
||||||
|| (tfp != ZSTD_tfp_forCDict && dtlm == ZSTD_dtlm_fast));
|
|| (tfp != ZSTD_tfp_forCDict && dtlm == ZSTD_dtlm_fast));
|
||||||
fastTable.hashTable = ms->hashTable;
|
/* These fields are mutated by the window/publish callbacks before the
|
||||||
fastTable.base = ms->window.base;
|
* Fast branch runs. Keep pointers to the live match-state fields rather
|
||||||
fastTable.nextToUpdate = ms->nextToUpdate;
|
* than a stale snapshot taken before the Rust orchestrator starts. */
|
||||||
fastTable.hashLog = ms->cParams.hashLog;
|
fastTable.hashTable = &ms->hashTable;
|
||||||
fastTable.minMatch = ms->cParams.minMatch;
|
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.fullTableLoad = dtlm == ZSTD_dtlm_full;
|
||||||
fastTable.forCDict = tfp == ZSTD_tfp_forCDict;
|
fastTable.forCDict = tfp == ZSTD_tfp_forCDict;
|
||||||
|
|
||||||
|
|||||||
@@ -141,17 +141,17 @@ type LoadDictionaryContentOverflowCorrectFn =
|
|||||||
unsafe extern "C" fn(context: *mut c_void, ip: *const c_void, iend: *const c_void);
|
unsafe extern "C" fn(context: *mut c_void, ip: *const c_void, iend: *const c_void);
|
||||||
/// Direct projection for the Fast dictionary-table leaf.
|
/// Direct projection for the Fast dictionary-table leaf.
|
||||||
///
|
///
|
||||||
/// C copies `nextToUpdate` rather than exposing a pointer into the private
|
/// C exposes pointers to the live scalar fields needed by the Fast leaf.
|
||||||
/// match state. `fullTableLoad` and `forCDict` are normalized from the C enum
|
/// `fullTableLoad` and `forCDict` are normalized from the C enum inputs after
|
||||||
/// inputs after their original validity assertion has run.
|
/// their original validity assertion has run.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
struct ZSTD_rust_loadDictionaryContentFastTableState {
|
struct ZSTD_rust_loadDictionaryContentFastTableState {
|
||||||
hash_table: *mut c_uint,
|
hash_table: *const *mut c_uint,
|
||||||
base: *const u8,
|
base: *const *const u8,
|
||||||
next_to_update: c_uint,
|
next_to_update: *const c_uint,
|
||||||
hash_log: c_uint,
|
hash_log: *const c_uint,
|
||||||
min_match: c_uint,
|
min_match: *const c_uint,
|
||||||
full_table_load: c_int,
|
full_table_load: c_int,
|
||||||
for_cdict: c_int,
|
for_cdict: c_int,
|
||||||
}
|
}
|
||||||
@@ -229,28 +229,25 @@ const _: () = {
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
offset_of!(ZSTD_rust_loadDictionaryContentFastTableState, hash_log)
|
offset_of!(ZSTD_rust_loadDictionaryContentFastTableState, hash_log)
|
||||||
== 2 * size_of::<usize>() + size_of::<c_uint>()
|
== 3 * size_of::<usize>()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
offset_of!(ZSTD_rust_loadDictionaryContentFastTableState, min_match)
|
offset_of!(ZSTD_rust_loadDictionaryContentFastTableState, min_match)
|
||||||
== 2 * size_of::<usize>() + 2 * size_of::<c_uint>()
|
== 4 * size_of::<usize>()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
offset_of!(
|
offset_of!(
|
||||||
ZSTD_rust_loadDictionaryContentFastTableState,
|
ZSTD_rust_loadDictionaryContentFastTableState,
|
||||||
full_table_load
|
full_table_load
|
||||||
) == 2 * size_of::<usize>() + 3 * size_of::<c_uint>()
|
) == 5 * size_of::<usize>()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
offset_of!(ZSTD_rust_loadDictionaryContentFastTableState, for_cdict)
|
offset_of!(ZSTD_rust_loadDictionaryContentFastTableState, for_cdict)
|
||||||
== 2 * size_of::<usize>() + 3 * size_of::<c_uint>() + size_of::<c_int>()
|
== 5 * size_of::<usize>() + size_of::<c_int>()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
size_of::<ZSTD_rust_loadDictionaryContentFastTableState>()
|
size_of::<ZSTD_rust_loadDictionaryContentFastTableState>()
|
||||||
== (offset_of!(ZSTD_rust_loadDictionaryContentFastTableState, for_cdict)
|
== 5 * size_of::<usize>() + 2 * size_of::<c_int>()
|
||||||
+ size_of::<c_int>())
|
|
||||||
.div_ceil(size_of::<usize>())
|
|
||||||
* size_of::<usize>()
|
|
||||||
);
|
);
|
||||||
assert!(size_of::<LoadDictionaryContentLoadMatchFn>() == size_of::<usize>());
|
assert!(size_of::<LoadDictionaryContentLoadMatchFn>() == size_of::<usize>());
|
||||||
assert!(size_of::<LoadDictionaryContentLoadTreeFn>() == size_of::<usize>());
|
assert!(size_of::<LoadDictionaryContentLoadTreeFn>() == size_of::<usize>());
|
||||||
@@ -369,6 +366,25 @@ fn dictionary_table_policy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn fill_fast_dictionary_table(
|
||||||
|
fast_table: &ZSTD_rust_loadDictionaryContentFastTableState,
|
||||||
|
end: *const c_void,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
ZSTD_rust_fillHashTable(
|
||||||
|
*fast_table.hash_table,
|
||||||
|
*fast_table.base,
|
||||||
|
*fast_table.next_to_update,
|
||||||
|
end,
|
||||||
|
*fast_table.hash_log,
|
||||||
|
*fast_table.min_match,
|
||||||
|
fast_table.full_table_load,
|
||||||
|
fast_table.for_cdict,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn load_dictionary_content(
|
unsafe fn load_dictionary_content(
|
||||||
state: &ZSTD_rust_loadDictionaryContentState,
|
state: &ZSTD_rust_loadDictionaryContentState,
|
||||||
src: *const c_void,
|
src: *const c_void,
|
||||||
@@ -446,16 +462,7 @@ unsafe fn load_dictionary_content(
|
|||||||
) {
|
) {
|
||||||
DictionaryTablePolicy::Fast => unsafe {
|
DictionaryTablePolicy::Fast => unsafe {
|
||||||
let fast_table = &*state.fast_table;
|
let fast_table = &*state.fast_table;
|
||||||
ZSTD_rust_fillHashTable(
|
fill_fast_dictionary_table(fast_table, iend.cast())
|
||||||
fast_table.hash_table,
|
|
||||||
fast_table.base,
|
|
||||||
fast_table.next_to_update,
|
|
||||||
iend.cast(),
|
|
||||||
fast_table.hash_log,
|
|
||||||
fast_table.min_match,
|
|
||||||
fast_table.full_table_load,
|
|
||||||
fast_table.for_cdict,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
DictionaryTablePolicy::DoubleFast => unsafe {
|
DictionaryTablePolicy::DoubleFast => unsafe {
|
||||||
(state.fill_double_hash_table)(
|
(state.fill_double_hash_table)(
|
||||||
@@ -6467,31 +6474,26 @@ mod tests {
|
|||||||
for (index, byte) in input.iter_mut().enumerate() {
|
for (index, byte) in input.iter_mut().enumerate() {
|
||||||
*byte = (index as u8).wrapping_mul(37).wrapping_add(11);
|
*byte = (index as u8).wrapping_mul(37).wrapping_add(11);
|
||||||
}
|
}
|
||||||
|
let hash_table_ptr = hash_table.as_mut_ptr();
|
||||||
|
let base_ptr = input.as_ptr();
|
||||||
|
let next_to_update = 17u32;
|
||||||
|
let min_match = 4u32;
|
||||||
let projection = ZSTD_rust_loadDictionaryContentFastTableState {
|
let projection = ZSTD_rust_loadDictionaryContentFastTableState {
|
||||||
hash_table: hash_table.as_mut_ptr(),
|
hash_table: &hash_table_ptr,
|
||||||
base: input.as_ptr(),
|
base: &base_ptr,
|
||||||
next_to_update: 17,
|
next_to_update: &next_to_update,
|
||||||
hash_log,
|
hash_log: &hash_log,
|
||||||
min_match: 4,
|
min_match: &min_match,
|
||||||
full_table_load: 1,
|
full_table_load: 1,
|
||||||
for_cdict: 1,
|
for_cdict: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ZSTD_rust_fillHashTable(
|
fill_fast_dictionary_table(&projection, input.as_ptr().wrapping_add(input.len()).cast())
|
||||||
projection.hash_table,
|
}
|
||||||
projection.base,
|
|
||||||
projection.next_to_update,
|
|
||||||
input.as_ptr().wrapping_add(input.len()).cast(),
|
|
||||||
projection.hash_log,
|
|
||||||
projection.min_match,
|
|
||||||
projection.full_table_load,
|
|
||||||
projection.for_cdict,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(hash_table.iter().any(|&entry| entry != 0));
|
assert!(hash_table.iter().any(|&entry| entry != 0));
|
||||||
assert_eq!(projection.next_to_update, 17);
|
assert_eq!(unsafe { *projection.next_to_update }, 17);
|
||||||
assert_eq!(projection.full_table_load, 1);
|
assert_eq!(projection.full_table_load, 1);
|
||||||
assert_eq!(projection.for_cdict, 1);
|
assert_eq!(projection.for_cdict, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user