Skip to content

Commit a3f8a2a

Browse files
committed
Harden cleanup and SIMD admission docs
1 parent f402871 commit a3f8a2a

5 files changed

Lines changed: 57 additions & 4 deletions

File tree

docs/SIMD_ACTIVATION_CHECKLIST.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,24 @@ Architecture-specific baseline requirements:
4242
- wasm `simd128`: document the runtime's register-retention limitations and
4343
provide generated-code evidence for the selected wasm toolchain/runtime.
4444

45-
Current prototypes are exempt only because they construct zero vectors and do
46-
not load caller bytes into SIMD registers. That exemption ends the moment a
47-
prototype starts processing caller input with vector registers.
45+
For NEON specifically, "used registers" cannot be guessed from source-level
46+
local variable count. Admission evidence must include optimized generated
47+
assembly for the exact target and feature bundle. If the compiler allocates
48+
caller-derived values to callee-saved vector registers, the admission package
49+
must also account for any compiler-generated spill/restore slots and prove that
50+
those slots are wiped or never contain caller-derived data.
51+
52+
For wasm `simd128`, source-level register cleanup is not sufficient evidence.
53+
The wasm runtime/JIT owns final register allocation and may rewrite the
54+
generated code. A wasm backend cannot be admitted until the selected runtime
55+
and deployment profile have generated-code/JIT evidence, or the release notes
56+
scope wasm SIMD out of any register-retention claim.
57+
58+
Current prototypes are exempt from admission only because they are test-only and
59+
non-dispatchable. The x86 and AArch64 prototypes already load caller bytes into
60+
vector registers and therefore carry best-effort cleanup plus unsafe-inventory
61+
documentation as evidence. That evidence is not enough for active dispatch
62+
until the full admission package is complete.
4863

4964
## Source Changes
5065

docs/SIMD_ENCODE_ADMISSION_DRAFT.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ Each encode backend considered for admission must provide:
4040
- hardware evidence from a CPU that actually supports the backend
4141
- benchmark output with scalar baseline on the same machine
4242

43+
Architecture-specific admission blockers:
44+
45+
- AArch64 NEON must include generated assembly that identifies every
46+
caller-derived vector register and any callee-saved vector spill/restore slot
47+
that may carry caller data.
48+
- wasm `simd128` must include generated-code/JIT evidence for the selected wasm
49+
runtime, or the release must explicitly keep wasm SIMD candidate-only and make
50+
no runtime register-retention claim.
51+
4352
## Runtime Report Expectations
4453

4554
Before active encode dispatch can ship, runtime reporting must make the

docs/UNSAFE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,11 @@ Safety argument:
859859
- This is best-effort register-retention reduction for test evidence, not a
860860
guarantee that historical register, stack, cache, or microarchitectural
861861
copies do not exist.
862+
- This helper currently clears the registers used by the reviewed prototype
863+
sequence. It is not an admission claim for arbitrary future NEON code. Before
864+
NEON dispatch can become active, generated assembly must prove which vector
865+
registers carry caller-derived data and whether any callee-saved vector
866+
register spill/restore slots contain such data.
862867

863868
### `encode_12_bytes_wasm_simd128`
864869

@@ -894,6 +899,8 @@ Limitations:
894899
- This is compile and codegen evidence only. Wasm engines include a runtime/JIT
895900
optimization layer outside Rust's compiler boundary, so this prototype does
896901
not claim runtime timing, register-retention, or JIT zeroization guarantees.
902+
wasm `simd128` admission requires runtime/JIT-specific evidence or an
903+
explicit release scope that makes no register-retention claim.
897904

898905
### `encode_12_bytes_wasm_standard_family`
899906

src/alphabet.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,15 @@ impl Alphabet for UrlSafe {
302302
/// This alphabet is commonly used by bcrypt hash strings. It is provided as an
303303
/// alphabet/profile building block; `base64-ng` does not parse or verify full
304304
/// bcrypt password-hash records.
305+
///
306+
/// # Security
307+
///
308+
/// The strict [`Alphabet::decode`] implementation delegates to
309+
/// [`decode_alphabet_byte`]. That helper scans the full alphabet, but it is a
310+
/// `const fn` and does not use the additional optimizer barriers used by the
311+
/// [`ct`](crate::ct) module. Do not use strict `Engine<Bcrypt, _>` decode as a
312+
/// token, key, or password-hash verifier. Use [`crate::ct::CtEngine`] with this
313+
/// alphabet for secret-bearing comparison workflows.
305314
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
306315
pub struct Bcrypt;
307316

@@ -318,6 +327,15 @@ impl Alphabet for Bcrypt {
318327
///
319328
/// This alphabet is provided as an explicit legacy interoperability profile.
320329
/// `base64-ng` does not parse or verify complete password-hash records.
330+
///
331+
/// # Security
332+
///
333+
/// The strict [`Alphabet::decode`] implementation delegates to
334+
/// [`decode_alphabet_byte`]. That helper scans the full alphabet, but it is a
335+
/// `const fn` and does not use the additional optimizer barriers used by the
336+
/// [`ct`](crate::ct) module. Do not use strict `Engine<Crypt, _>` decode as a
337+
/// token, key, or password-hash verifier. Use [`crate::ct::CtEngine`] with this
338+
/// alphabet for secret-bearing comparison workflows.
321339
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
322340
pub struct Crypt;
323341

src/cleanup.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ fn wipe_barrier(ptr: *mut u8, len: usize) {
9494

9595
pub(crate) fn wipe_tail(bytes: &mut [u8], start: usize) {
9696
debug_assert!(start <= bytes.len(), "wipe_tail start exceeds slice length");
97-
let start = start.min(bytes.len());
97+
if start > bytes.len() {
98+
wipe_bytes(bytes);
99+
return;
100+
}
101+
98102
if start < bytes.len() {
99103
wipe_bytes(&mut bytes[start..]);
100104
}

0 commit comments

Comments
 (0)