{"id":"CVE-2026-23519","aliases":["EUVD-2026-2731","GHSA-2gqc-6j2q-83qp","RUSTSEC-2026-0003"],"status":"published","title":"RustCrypto cmov: thumbv6m-none-eabi compiler emits non-constant time assembly when using cmovnz","description":"RustCrypto CMOV provides conditional move CPU intrinsics which are guaranteed on major platforms to execute in constant-time and not be rewritten as branches by the compiler. Prior to 0.4.4, the thumbv6m-none-eabi (Cortex M0, M0+ and M1) compiler emits non-constant time assembly when using cmovnz (portable version). This vulnerability is fixed in 0.4.4.","severity":{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00558,"percentile":0.46928,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-203","CWE-208"],"published":"2026-01-14T12:00:00Z","modified":"2026-10-28T02:31:13Z","fixes":[{"ecosystem":"crates.io","package":"cmov","fixed_in":["0.4.4"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"crates.io","package":"cmov","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.4.4"}]}]},{"source":"osv","ecosystem":"crates.io","package":"cmov","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.4.4"}]}]},{"source":"osv","ecosystem":"crates.io","package":"cmov","ranges":[{"type":"SEMVER","events":[{"introduced":"0.0.0-0"},{"fixed":"0.4.4"}]}]}],"products":[{"vendor":"rustcrypto","product":"cmov"},{"vendor":"rustcrypto","product":"utils"}],"references":[{"url":"https://github.com/RustCrypto/utils/security/advisories/GHSA-2gqc-6j2q-83qp","tags":["x_refsource_CONFIRM","exploit","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://github.com/RustCrypto/utils/commit/55977257e7c82a309d5e8abfdd380a774f0f9778","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://rustsec.org/advisories/RUSTSEC-2026-0003.html","tags":["web","advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-23519","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-2gqc-6j2q-83qp"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/23xxx/CVE-2026-23519.json","tags":["advisory"]},{"url":"https://github.com/RustCrypto/utils","tags":["package"]},{"url":"https://crates.io/crates/cmov","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-23519","title":"RustCrypto cmov: thumbv6m-none-eabi compiler emits non-constant time assembly when using cmovnz","description":"RustCrypto CMOV provides conditional move CPU intrinsics which are guaranteed on major platforms to execute in constant-time and not be rewritten as branches by the compiler. Prior to 0.4.4, the thumbv6m-none-eabi (Cortex M0, M0+ and M1) compiler emits non-constant time assembly when using cmovnz (portable version). This vulnerability is fixed in 0.4.4.","status":"published","published":"2026-01-15T19:13:54.44Z","modified":"2026-01-15T19:57:48.741Z","cvss":[{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","level":"high","assessor":"cna"}],"cwes":["CWE-208"],"updated_at":"2026-11-02T13:42:15.603999Z"}],"euvd":[{"id":"EUVD-2026-2731","description":"RustCrypto CMOV provides conditional move CPU intrinsics which are guaranteed on major platforms to execute in constant-time and not be rewritten as branches by the compiler. Prior to 0.4.4, the thumbv6m-none-eabi (Cortex M0, M0+ and M1) compiler emits non-constant time assembly when using cmovnz (portable version). This vulnerability is fixed in 0.4.4.","status":"published","published":"2026-01-15T19:13:54Z","modified":"2026-01-15T19:57:48Z","cvss":[{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T19:48:51.36047Z"}],"ghsa":[{"id":"GHSA-2gqc-6j2q-83qp","title":"RustCrypto Utilities cmov: `thumbv6m-none-eabi` compiler emits non-constant time assembly when using `cmovnz`","description":"### Summary\n\n`thumbv6m-none-eabi` (Cortex M0, M0+ and M1) compiler emits non-constant time assembly when using `cmovnz` (portable version). I did not found any other target with the same behaviour but I did not go through all targets supported by Rust. \n\n### Details\n\nIt seems that, [during `mask` computation](https://github.com/RustCrypto/utils/blob/9e555db060c80f4669d804f448a524a37d201b32/cmov/src/portable.rs#L78), an LLVM optimisation pass is detecting that [`bitnz`](https://github.com/RustCrypto/utils/blob/9e555db060c80f4669d804f448a524a37d201b32/cmov/src/portable.rs#L13) is returning 0 or 1, that can be interpreted as a boolean. This intermediate value is not masked by a call to `black_box` and thus the subsequent [`.wrapping_sub(1)`](https://github.com/RustCrypto/utils/blob/9e555db060c80f4669d804f448a524a37d201b32/cmov/src/portable.rs#L78C1-L78C84) can be interpreted as a conditional bitwise conditional not.\n\n### PoC\n\nThis is an attempt at having a minimal faulty code. In a library crate with an up-to-date `cmov` as only dependency, the content of `src/lib.rs` is:\n\n```rust\n#![no_std]\nuse cmov::Cmov;\n\n#[inline(never)]\npub fn test_ct_cmov(a: \u0026mut u8, b: u8, c: u8) {\n    a.cmovnz(\u0026b, c);\n}\n```\n\n\nThe resulting assembly emitted (shown using `cargo asm --release --target thumbv6m-none-eabi` that uses [`cargo-show-asm`](https://crates.io/crates/cargo-show-asm)):\n\n\u003cdetails\u003e\n\u003csummary\u003eCollapsed assembly\u003c/summary\u003e\n\n```asm\n.section .text.not_ct::test_ct_cmov,\"ax\",%progbits\n\t.globl\tnot_ct::test_ct_cmov\n\t.p2align\t1\n\t.type\tnot_ct::test_ct_cmov,%function\n\t.code\t16\n\t.thumb_func\nnot_ct::test_ct_cmov:\n\t.fnstart\n\t.cfi_sections .debug_frame\n\t.cfi_startproc\n\t.save\t{r7, lr}\n\tpush {r7, lr}\n\t.cfi_def_cfa_offset 8\n\t.cfi_offset lr, -4\n\t.cfi_offset r7, -8\n\t.setfp\tr7, sp\n\tadd r7, sp, #0\n\t.cfi_def_cfa_register r7\n\t.pad\t#8\n\tsub sp, #8\n\tmovs r3, #0\n\tlsls r2, r2, #24\n\tbne .LBB0_2\n\tmvns r3, r3\n.LBB0_2:\n\tldrb r2, [r0]\n\tstr r3, [sp, #4]\n\tstr r3, [sp]\n\tmov r3, sp\n\t@APP\n\t@NO_APP\n\tldr r3, [sp]\n\tbics r1, r3\n\tands r2, r3\n\tadds r1, r2, r1\n\tstrb r1, [r0]\n\tadd sp, #8\n\tpop {r7, pc}\n```\n\n\u003c/details\u003e\n\nThe non-constant time assembly is:\n\n```asm\n    bne  .LBB0_2\n    mvns r3, r3\n.LBB0_2:\n```\n\n### Impact\n\nThe exact impact is unclear, especially since `cmov` clearly warns users that the portable version is best-effort.","status":"published","published":"2026-01-15T18:17:15Z","modified":"2026-01-23T21:46:11Z","cvss":[{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","level":"high"}],"cwes":["CWE-203","CWE-208"],"updated_at":"2026-11-02T14:39:19.525691Z"}],"nvd":[{"id":"CVE-2026-23519","description":"RustCrypto CMOV provides conditional move CPU intrinsics which are guaranteed on major platforms to execute in constant-time and not be rewritten as branches by the compiler. Prior to 0.4.4, the thumbv6m-none-eabi (Cortex M0, M0+ and M1) compiler emits non-constant time assembly when using cmovnz (portable version). This vulnerability is fixed in 0.4.4.","status":"published","published":"2026-01-15T20:16:05.313Z","modified":"2026-06-17T10:21:42.97Z","cvss":[{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"primary"}],"cwes":["CWE-208","CWE-203"],"updated_at":"2026-11-02T14:12:04.066712Z"}],"osv":[{"id":"CVE-2026-23519","title":"RustCrypto cmov: thumbv6m-none-eabi compiler emits non-constant time assembly when using cmovnz","description":"RustCrypto CMOV provides conditional move CPU intrinsics which are guaranteed on major platforms to execute in constant-time and not be rewritten as branches by the compiler. Prior to 0.4.4, the thumbv6m-none-eabi (Cortex M0, M0+ and M1) compiler emits non-constant time assembly when using cmovnz (portable version). This vulnerability is fixed in 0.4.4.","status":"published","published":"2026-01-15T19:13:54.44Z","modified":"2026-10-28T02:31:13.278621354Z","cvss":[{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","level":"high"}],"cwes":["CWE-208"],"updated_at":"2026-11-02T15:44:59.079784Z"},{"id":"GHSA-2gqc-6j2q-83qp","title":"RustCrypto Utilities cmov: `thumbv6m-none-eabi` compiler emits non-constant time assembly when using `cmovnz`","description":"### Summary\n\n`thumbv6m-none-eabi` (Cortex M0, M0+ and M1) compiler emits non-constant time assembly when using `cmovnz` (portable version). I did not found any other target with the same behaviour but I did not go through all targets supported by Rust. \n\n### Details\n\nIt seems that, [during `mask` computation](https://github.com/RustCrypto/utils/blob/9e555db060c80f4669d804f448a524a37d201b32/cmov/src/portable.rs#L78), an LLVM optimisation pass is detecting that [`bitnz`](https://github.com/RustCrypto/utils/blob/9e555db060c80f4669d804f448a524a37d201b32/cmov/src/portable.rs#L13) is returning 0 or 1, that can be interpreted as a boolean. This intermediate value is not masked by a call to `black_box` and thus the subsequent [`.wrapping_sub(1)`](https://github.com/RustCrypto/utils/blob/9e555db060c80f4669d804f448a524a37d201b32/cmov/src/portable.rs#L78C1-L78C84) can be interpreted as a conditional bitwise conditional not.\n\n### PoC\n\nThis is an attempt at having a minimal faulty code. In a library crate with an up-to-date `cmov` as only dependency, the content of `src/lib.rs` is:\n\n```rust\n#![no_std]\nuse cmov::Cmov;\n\n#[inline(never)]\npub fn test_ct_cmov(a: \u0026mut u8, b: u8, c: u8) {\n    a.cmovnz(\u0026b, c);\n}\n```\n\n\nThe resulting assembly emitted (shown using `cargo asm --release --target thumbv6m-none-eabi` that uses [`cargo-show-asm`](https://crates.io/crates/cargo-show-asm)):\n\n\u003cdetails\u003e\n\u003csummary\u003eCollapsed assembly\u003c/summary\u003e\n\n```asm\n.section .text.not_ct::test_ct_cmov,\"ax\",%progbits\n\t.globl\tnot_ct::test_ct_cmov\n\t.p2align\t1\n\t.type\tnot_ct::test_ct_cmov,%function\n\t.code\t16\n\t.thumb_func\nnot_ct::test_ct_cmov:\n\t.fnstart\n\t.cfi_sections .debug_frame\n\t.cfi_startproc\n\t.save\t{r7, lr}\n\tpush {r7, lr}\n\t.cfi_def_cfa_offset 8\n\t.cfi_offset lr, -4\n\t.cfi_offset r7, -8\n\t.setfp\tr7, sp\n\tadd r7, sp, #0\n\t.cfi_def_cfa_register r7\n\t.pad\t#8\n\tsub sp, #8\n\tmovs r3, #0\n\tlsls r2, r2, #24\n\tbne .LBB0_2\n\tmvns r3, r3\n.LBB0_2:\n\tldrb r2, [r0]\n\tstr r3, [sp, #4]\n\tstr r3, [sp]\n\tmov r3, sp\n\t@APP\n\t@NO_APP\n\tldr r3, [sp]\n\tbics r1, r3\n\tands r2, r3\n\tadds r1, r2, r1\n\tstrb r1, [r0]\n\tadd sp, #8\n\tpop {r7, pc}\n```\n\n\u003c/details\u003e\n\nThe non-constant time assembly is:\n\n```asm\n    bne  .LBB0_2\n    mvns r3, r3\n.LBB0_2:\n```\n\n### Impact\n\nThe exact impact is unclear, especially since `cmov` clearly warns users that the portable version is best-effort.","status":"published","published":"2026-01-15T18:17:15Z","modified":"2026-09-10T03:50:32.352645465Z","cvss":[{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","level":"high"}],"cwes":["CWE-203","CWE-208"],"updated_at":"2026-11-02T15:46:34.338541Z"},{"id":"RUSTSEC-2026-0003","title":"Non-constant-time code generation on ARM32 targets","description":"## Summary\n\nWhile the `cmov` crate has a special backend for `aarch64` which uses special\nCSEL instructions, on 32-bit ARM it uses a portable pure Rust fallback\nimplementation. This implementation uses a combination of bitwise arithmetic\nand `core::hint::black_box` to attempt to coerce constant-time code generation\nout of the optimizer, but the implementation in v0.4.3 and earlier failed to\ndo this on 32-bit ARM targets.\n\n## Impact\n\nBranch instructions inserted by the LLVM optimizer on 32-bit targets can be\nleveraged using various microarchitectural sidechannels like cache timing\nattacks to learn secret information that `cmov` is designed to protect.\n\n## Details\n\nThe following assembly was emitted when using `Cmov::cmovnz`, a function which\nimplements a conditional move when a provided value is non-zero:\n\n```asm\n    bne  .LBB0_2\n    mvns r3, r3\n```\n\nThis includes a branch instruction `bne`: Branch if Not Equal.\n\n## PoC\n\nThe following code reproduces the issue:\n\n```rust\n#![no_std]\nuse cmov::Cmov;\n\n#[inline(never)]\npub fn test_ct_cmov(a: \u0026mut u8, b: u8, c: u8) {\n    a.cmovnz(\u0026b, c);\n}\n```\n\n## Resolution\n\n`cmov` v0.4.4 includes a portable `black_box`-based tactical mitigation for the\nissue which coerced the compiler into producing the expected codegen, and\nadditionally v0.4.5 added an `asm!` reimplementation of the problematic mask\ngeneration function for ARM32 targets which should guarantee that particular\nfunction never contains a branch on such targets.","status":"published","published":"2026-01-14T12:00:00Z","modified":"2026-10-23T08:41:35.268663457Z","cvss":[{"version":"4.0","score":8.9,"vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T15:53:40.430679Z"}]}}
