Skip to content

Commit 6f737f1

Browse files
committed
Harden SIMD decode unsafe inventory
1 parent 6111e83 commit 6f737f1

3 files changed

Lines changed: 85 additions & 30 deletions

File tree

docs/UNSAFE.md

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -673,18 +673,65 @@ Safety argument:
673673
six-bit Base64 value.
674674
- The helper is private to the Standard-family SSSE3/SSE4.1 encode path.
675675

676+
### `decode_slice_ssse3_sse41`, `decode_slice_avx2`, and `decode_slice_avx512`
677+
678+
Location: `src/simd/x86/decode.rs`
679+
680+
Status: admitted std x86/x86_64 strict decode dispatch wrappers for Standard
681+
and URL-safe alphabet families. They are reachable only when the `simd` and
682+
`std` features are enabled and runtime CPU feature probing has selected the
683+
matching backend.
684+
685+
Purpose:
686+
687+
- Carve full encoded input blocks into fixed-size array references for the
688+
target-feature decode block functions.
689+
- Preserve scalar public error shape by validating the complete input before
690+
any SIMD block output is copied to caller-visible buffers.
691+
- Fall back from AVX-512 to AVX2, from AVX2 to SSSE3/SSE4.1, and from
692+
SSSE3/SSE4.1 to scalar for shorter tails or unsupported surfaces.
693+
694+
Preconditions:
695+
696+
- Runtime dispatch has selected only a backend whose CPU features are present.
697+
- The input block loop guard proves that each carved block is fully within the
698+
original input slice.
699+
- The output capacity has been checked against the scalar validated decoded
700+
length before any block output is copied.
701+
702+
Unsafe operation:
703+
704+
- Each wrapper uses `input.as_ptr().add(read).cast::<[u8; N]>()` and
705+
dereferences the result to pass a fixed-size block reference to the matching
706+
target-feature decoder.
707+
708+
Safety argument:
709+
710+
- `read + N <= input.len()` is checked before every raw-pointer block carve.
711+
- `read` advances by exactly `N`, so the pointer remains within the same input
712+
allocation and never crosses the slice boundary.
713+
- The wrapper never constructs a mutable alias to input memory.
714+
- Output writes go through private stack staging buffers first. Bytes are
715+
copied to caller output only after whole-input scalar validation and block
716+
equality checks inside the target-feature decoder.
717+
- Any unexpected block-level error wipes the local decoded staging buffer and
718+
rebases the error index to the original input. Tail fallback errors are also
719+
rebased to the original input offset.
720+
- Unsupported alphabets, short inputs, tails, wrapped decode, legacy decode,
721+
in-place decode, CT secret decode, `no_std`, and wasm decode stay scalar.
722+
676723
### `decode_16_bytes_ssse3_sse41`
677724

678725
Location: `src/simd/x86/decode.rs`
679726

680-
Status: non-dispatchable std x86/x86_64 SSSE3/SSE4.1 decode prototype for
681-
Standard and URL-safe alphabet families. It is reachable only from SIMD tests
682-
and backend evidence capture. Runtime decode dispatch remains scalar.
727+
Status: admitted std x86/x86_64 SSSE3/SSE4.1 strict decode block for Standard
728+
and URL-safe alphabet families. It is reachable through strict decode dispatch
729+
for full 16-byte encoded blocks after whole-input scalar validation.
683730

684731
Purpose:
685732

686-
- Establish the first fixed-block SIMD decode admission boundary without
687-
changing public decode behavior.
733+
- Provide the fixed-block SSSE3/SSE4.1 decode primitive for the admitted strict
734+
decode boundary without changing scalar public error behavior.
688735
- Exercise SSSE3/SSE4.1 6-bit-value packing for a 16-byte encoded block that
689736
decodes to at most 12 bytes.
690737
- Verify error agreement, padding behavior, canonical trailing-bit rejection,
@@ -724,23 +771,22 @@ Safety argument:
724771
an unconditional release-mode equality check proves the vector-packed prefix
725772
matches the scalar-validation prefix. It wipes local value, packed, and
726773
scalar-validation staging buffers before returning.
727-
- Runtime decode dispatch does not call this function. Future admission must
728-
update this inventory, generated-code evidence, fuzzing, backend reporting,
729-
benchmarks, and release notes in the same commit that makes any decode SIMD
730-
path reachable.
774+
- The public dispatch wrapper validates the complete input with scalar before
775+
calling this block function and rebases any unexpected block error to the
776+
original input offset.
731777

732778
### `decode_64_bytes_avx512`
733779

734780
Location: `src/simd/x86/decode.rs`
735781

736-
Status: non-dispatchable std x86/x86_64 AVX-512 VBMI decode prototype for
737-
Standard and URL-safe alphabet families. It is reachable only from SIMD tests
738-
and backend evidence capture. Runtime decode dispatch remains scalar.
782+
Status: admitted std x86/x86_64 AVX-512 VBMI strict decode block for Standard
783+
and URL-safe alphabet families. It is reachable through strict decode dispatch
784+
for full 64-byte encoded blocks after whole-input scalar validation.
739785

740786
Purpose:
741787

742-
- Extend fixed-block SIMD decode evidence to the AVX-512 VBMI backend shape
743-
without changing public decode behavior.
788+
- Provide the fixed-block AVX-512 VBMI decode primitive for the admitted strict
789+
decode boundary without changing scalar public error behavior.
744790
- Exercise AVX-512 6-bit-value packing for a 64-byte encoded block that
745791
decodes to at most 48 bytes.
746792
- Verify VBMI lane compaction, error agreement, padding behavior, canonical
@@ -789,23 +835,22 @@ Safety argument:
789835
an unconditional release-mode equality check proves the vector-packed prefix
790836
matches the scalar-validation prefix. It wipes local value, packed, and
791837
scalar-validation staging buffers before returning.
792-
- Runtime decode dispatch does not call this function. Future admission must
793-
update this inventory, generated-code evidence, fuzzing, backend reporting,
794-
benchmarks, and release notes in the same commit that makes any decode SIMD
795-
path reachable.
838+
- The public dispatch wrapper validates the complete input with scalar before
839+
calling this block function and rebases any unexpected block error to the
840+
original input offset.
796841

797842
### `decode_32_bytes_avx2`
798843

799844
Location: `src/simd/x86/decode.rs`
800845

801-
Status: non-dispatchable std x86/x86_64 AVX2 decode prototype for Standard and
802-
URL-safe alphabet families. It is reachable only from SIMD tests and backend
803-
evidence capture. Runtime decode dispatch remains scalar.
846+
Status: admitted std x86/x86_64 AVX2 strict decode block for Standard and
847+
URL-safe alphabet families. It is reachable through strict decode dispatch for
848+
full 32-byte encoded blocks after whole-input scalar validation.
804849

805850
Purpose:
806851

807-
- Extend fixed-block SIMD decode evidence from one SSSE3/SSE4.1 lane to the
808-
two-lane AVX2 shape without changing public decode behavior.
852+
- Provide the fixed-block AVX2 decode primitive for the admitted strict decode
853+
boundary without changing scalar public error behavior.
809854
- Exercise AVX2 6-bit-value packing for a 32-byte encoded block that decodes
810855
to at most 24 bytes.
811856
- Verify lane-boundary compaction, error agreement, padding behavior,
@@ -852,10 +897,9 @@ Safety argument:
852897
an unconditional release-mode equality check proves the vector-packed prefix
853898
matches the scalar-validation prefix. It wipes local value, packed, and
854899
scalar-validation staging buffers before returning.
855-
- Runtime decode dispatch does not call this function. Future admission must
856-
update this inventory, generated-code evidence, fuzzing, backend reporting,
857-
benchmarks, and release notes in the same commit that makes any decode SIMD
858-
path reachable.
900+
- The public dispatch wrapper validates the complete input with scalar before
901+
calling this block function and rebases any unexpected block error to the
902+
original input offset.
859903

860904
### `clear_xmm_registers_after_encode_block`
861905

scripts/validate-unsafe-boundary.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ for symbol in $unsafe_functions; do
9292
fi
9393
done
9494

95+
bare_unsafe_wrappers='decode_slice_ssse3_sse41 decode_slice_avx2 decode_slice_avx512'
96+
for symbol in $bare_unsafe_wrappers; do
97+
if ! grep -q "$symbol" docs/UNSAFE.md; then
98+
echo "unsafe boundary: docs/UNSAFE.md must document $symbol"
99+
exit 1
100+
fi
101+
done
102+
95103
if ! awk '
96104
/^[[:space:]]*unsafe[[:space:]]*\{/ {
97105
if (prev1 !~ /SAFETY:/ && prev2 !~ /SAFETY:/ && prev3 !~ /SAFETY:/ && prev4 !~ /SAFETY:/) {

src/simd/x86/decode.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ where
5757
write += written;
5858
}
5959

60-
let tail_written = scalar::decode_slice::<A, PAD>(&input[read..], &mut output[write..])?;
60+
let tail_written = scalar::decode_slice::<A, PAD>(&input[read..], &mut output[write..])
61+
.map_err(|error| error.with_index_offset(read))?;
6162
Ok(write + tail_written)
6263
}
6364

@@ -109,7 +110,8 @@ where
109110
write += written;
110111
}
111112

112-
let tail_written = decode_slice_ssse3_sse41::<A, PAD>(&input[read..], &mut output[write..])?;
113+
let tail_written = decode_slice_ssse3_sse41::<A, PAD>(&input[read..], &mut output[write..])
114+
.map_err(|error| error.with_index_offset(read))?;
113115
Ok(write + tail_written)
114116
}
115117

@@ -161,7 +163,8 @@ where
161163
write += written;
162164
}
163165

164-
let tail_written = decode_slice_avx2::<A, PAD>(&input[read..], &mut output[write..])?;
166+
let tail_written = decode_slice_avx2::<A, PAD>(&input[read..], &mut output[write..])
167+
.map_err(|error| error.with_index_offset(read))?;
165168
Ok(write + tail_written)
166169
}
167170

0 commit comments

Comments
 (0)