Skip to content

chacha20: fix use of SSE4.1 intrinsic in SSE2 backend - #580

Merged
newpavlov merged 4 commits into
masterfrom
chacha20/fix_sse2
Aug 27, 2026
Merged

newpavlov merged 4 commits into
masterfrom
chacha20/fix_sse2

Conversation

@newpavlov

@newpavlov newpavlov commented Aug 27, 2026 •

Copy link
Copy Markdown
Member

Closes #579

@newpavlov
newpavlov requested a review from tarcieri August 27, 2026 15:25
Comment thread chacha20/src/backends/sse2.rs Outdated
if size_of::<V::Counter>() == 8 {
state[13] = _mm_extract_epi32(backend.v[3], 1) as u32;
}
_mm_storeu_si128(state_ptr.add(3), backend.v[3]);

@newpavlov newpavlov Aug 27, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unnecessarily writes 64-96 bits which stay unmodified, but it results in a simpler code (especially considering that _mm_cvtsi128_si64 is not available on i686 targets) and it even may be miniscully more efficient since we directly write XMM register data.

@newpavlov

newpavlov commented Aug 27, 2026 •

Copy link
Copy Markdown
Member Author

@tarcieri
It seems that we don't have sufficient testing for the legacy cipher variants and RNG. Writing only 32 counter bits for them does not get caught by current tests, so it's probably worth to fix in a future PR.

@newpavlov
newpavlov merged commit 075ccbd into master Aug 27, 2026
32 checks passed
@newpavlov
newpavlov deleted the chacha20/fix_sse2 branch August 27, 2026 17:41
newpavlov added a commit that referenced this pull request Aug 27, 2026
### Fixed
- Use of SSE4.1 intrinsic in SSE2 backend of RNG and legacy (64-bit
counter) variants ([#580])

[#580]: #580
@newpavlov

Copy link
Copy Markdown
Member Author

@tarcieri
I think it's probably worth to yank v0.10.0 and v0.10.1 as releases containing UB. WDYT?

@tarcieri

Copy link
Copy Markdown
Member

sure

newpavlov added a commit that referenced this pull request Aug 27, 2026
The yanked versions contain UB in SSE2 backend, see #580.
@elichai

elichai commented Aug 27, 2026

Copy link
Copy Markdown

Glad to see that RustCrypto/utils#1513 helped catch UB :)

@tarcieri

Copy link
Copy Markdown
Member

That much is definitely cool!

MarcusKainth added a commit to spate-etl/spate that referenced this pull request Aug 27, 2026
`chacha20` 0.10.0 and 0.10.1 were yanked from crates.io. The SSE2 backend of
the RNG and legacy 64-bit-counter variants used `_mm_extract_epi32`, an SSE4.1
intrinsic, so a CPU with SSE2 and without SSE4.1 met an illegal instruction
instead of a working fallback. 0.10.2 replaces it with `_mm_storeu_si128`,
which is SSE2 (RustCrypto/stream-ciphers#580).

Nothing about ciphertext, keys or nonces changes. The fault is portability, and
the affected path is reachable only on x86_64 hardware predating SSE4.1.

`deny.toml` sets `yanked = "deny"`, so the yank fails `make deny` on every
branch until the lockfile moves. The crate reaches the shipped tree through
`apache-avro` and `async-nats`, both by way of `rand` 0.10.2.

THIRD-PARTY.md carries the version, so it is regenerated here.

Changelog: none
IceTDrinker added a commit to zama-ai/tfhe-rs that referenced this pull request Aug 28, 2026
Hmbown pushed a commit to codewhale-hq/Codewhale that referenced this pull request Sep 8, 2026
Update only chacha20 0.10.1 to 0.10.2 in Cargo.lock. RustCrypto yanked
0.10.0/0.10.1 for an SSE4.1 intrinsic used in the SSE2 RNG/legacy backend,
which is undefined behavior when that target feature is unavailable.
The patch preserves MSRV 1.85, enabled dependency edges, and public features.

The lockfile route is oauth2 -> reqwest 0.12 -> optional QUIC -> quinn-proto
-> rand 0.10 -> chacha20. No chacha20 edge is active in this workspace's
all-features/all-targets cargo tree; this does not assert observed exploitation.
Bincode/yaml-rust maintenance warnings and existing audit policy are unchanged.

Validation: cargo update --offline --precise 0.10.2 succeeded; structural lock
comparison confirms exactly one version/checksum change, all other entries
and edges unchanged; locked offline all-features/all-targets dependency
resolution passed; cargo audit --no-fetch: 0 vulnerabilities, 0 yanked crates,
2 existing maintenance warnings; git diff --check passed.
No Rust build/tests or hosted CI run for this dormant lock-only dependency.
Core root has no npm test/check:web scripts. No new advisory ignores.

Upstream: RustCrypto/stream-ciphers#583
Fix: RustCrypto/stream-ciphers#580
Release: https://github.com/RustCrypto/stream-ciphers/releases/tag/chacha20-v0.10.2
clintcan added a commit to clintcan/macrdp that referenced this pull request Sep 12, 2026
The daily cargo-deny scan has failed since 2026-09-09 (runs 34350271112,
34475387063, 34597603310) on:

    error[yanked]: detected yanked crate (try `cargo update -p chacha20`)
    chacha20 0.10.1 — yanked version

deny.toml sets yanked = "deny", so a yanked crate in the lockfile reds the
scan by design. chacha20 reaches us transitively: rand v0.10.2 -> picky /
picky-krb / sspi -> ironrdp-connector -> ironrdp-acceptor/-async/-server.

NOT a vulnerability. The only RustSec advisory for this crate is
RUSTSEC-2019-0029 (ChaCha20 counter overflow), patched in >= 0.2.3 and
irrelevant here. Upstream yanked 0.10.1 (and 0.10.0) for a correctness bug
fixed in 0.10.2: an SSE4.1 intrinsic used in the SSE2 backend of the RNG and
legacy 64-bit-counter variants (RustCrypto/stream-ciphers#580). The failure
mode is an illegal instruction on an x86 CPU with SSE2 but without SSE4.1 —
not a keystream or crypto weakness. macrdp's practical exposure was nil:
Apple Silicon never compiles those x86 backends, and every Intel Mac has
SSE4.1. It is still right to move off a yanked version.

Single-package update, no other crate moved. Verified locally with the same
cargo-deny 0.19.9 that CI pins, both invocations the workflow runs:
  cargo deny check advisories bans sources                       -> ok
  cargo deny --manifest-path vendor/ironrdp-rdpeudp/Cargo.toml
    check --config deny.toml advisories bans sources             -> ok
Build clean; 206 tests pass, 0 failed.
coderdan added a commit to cipherstash/stack that referenced this pull request Oct 18, 2026
chacha20 0.10.0 and 0.10.1 were yanked upstream for undefined behaviour
(RustCrypto/stream-ciphers#579, fixed in RustCrypto/stream-ciphers#580). On an x86 CPU without
AVX2 the crate falls back to its SSE2 backend, and that backend's RNG
path calls `_mm_extract_epi32`, an SSE4.1 intrinsic, without checking
for SSE4.1. A CPU with SSE4.1 runs it and gets the right answer; one
without it (pre-2008 hardware, QEMU's default qemu64 model) dies with
SIGILL. The keystream and the random numbers were never wrong.

We reach it through `rand` 0.10, whose ThreadRng is ChaCha12, and
through vitaminc-random. Both are in the native binaries
@cipherstash/protect-ffi and @cipherstash/auth ship, hence the patch
changeset. The fuzz workspaces move too, so no lockfile here still
holds a yanked chacha20. The Go guests were already on 0.10.2.

Only chacha20's version and checksum change in each lockfile.

Claude-Session: https://claude.ai/code/session_01BZhkvKLpNtKuzj3HCfMXoL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

miri fails on chacha20 with incorrectly gated Intel intrinsic

3 participants