{"id":"GHSA-3rjw-m598-pq24","revision":null,"records":[{"revision":"4608ae78e7a64593b86e1e1b9ebb28c8","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-50185","aliases":[],"title":"RustCrypto Cmov/CmovEq on aarch64 can produce wrong results if high-bits of registers are set","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. From 0.1.1 until 0.5.4, the aarch64 implementations of Cmov and CmovEq in cmov/src/backends/aarch64.rs assume high bits are zero-extended when loading values smaller than a register, so set high bits such as [8..] in a Cmov selector or [16..] of self or other in the u16 and i16 CmovEq implementations can cause left.cmovz(&right, condition) to produce incorrect output. This issue is fixed in version 0.5.4.","state":0,"publishedAt":"2026-07-17T18:35:02.078+00:00","modifiedAt":"2026-07-20T19:24:54.587+00:00","affected":[{"ecosystem":"Vendor product","name":"utils","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"RustCrypto\",\n                    \"product\": \"utils\",\n                    \"versions\": [\n                        {\n                            \"version\": \">= 0.1.1, < 0.5.4\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 4.0","vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","score":2,"severity":"LOW"}],"references":["https://github.com/RustCrypto/utils/security/advisories/GHSA-3rjw-m598-pq24","https://github.com/RustCrypto/utils/commit/dba6c355c9f241e3726d5ec2a68f9f3b519f6063","https://github.com/RustCrypto/utils/releases/tag/cmov-v0.5.4"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-50185","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-50185","identifiers":["CVE-2026-50185"]},"retained":false,"historicalAffected":[]},{"revision":"69f30bcbcef243acb7d810904eac329a","observedAt":"2026-10-25T12:17:38.331+00:00","statement":{"source":"nvd","id":"CVE-2026-50185","aliases":[],"title":"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. From 0.1.1 until 0.5.4, the aarch64 implementations of Cmov and C…","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. From 0.1.1 until 0.5.4, the aarch64 implementations of Cmov and CmovEq in cmov/src/backends/aarch64.rs assume high bits are zero-extended when loading values smaller than a register, so set high bits such as [8..] in a Cmov selector or [16..] of self or other in the u16 and i16 CmovEq implementations can cause left.cmovz(&right, condition) to produce incorrect output. This issue is fixed in version 0.5.4.","state":0,"publishedAt":"2026-07-17T19:17:16.37+00:00","modifiedAt":"2026-10-24T20:10:00.137+00:00","affected":[],"metrics":[{"system":"CVSS 4.0 · security-advisories@github.com · Secondary","vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/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","score":2,"severity":"LOW"},{"system":"CVSS 3.1 · nvd@nist.gov · Primary","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N","score":3.3,"severity":"LOW"}],"references":["https://github.com/RustCrypto/utils/commit/dba6c355c9f241e3726d5ec2a68f9f3b519f6063","https://github.com/RustCrypto/utils/releases/tag/cmov-v0.5.4","https://github.com/RustCrypto/utils/security/advisories/GHSA-3rjw-m598-pq24"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-50185","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-758"],"productCriteria":["cpe:2.3:a:rustcrypto:cmov:*:*:*:*:*:rust:*:*"],"sourceDates":{"published":"2026-07-17T19:17:16.370","lastModified":"2026-10-24T20:10:00.137"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-50185","identifiers":["CVE-2026-50185"]},"retained":false,"historicalAffected":[]},{"revision":"9948bc88476a4848852c27ca442033b6","observedAt":"2026-10-24T13:19:54.901+00:00","statement":{"source":"osv","id":"GHSA-3rjw-m598-pq24","aliases":["CVE-2026-50185"],"title":"Cmov/CmovEq on aarch64 can produce wrong results if high-bits of registers are set ","description":"### Summary\n\nThe aarch64 implementations of `Cmov` and `CmovEq` seem to assume that the high bits when loading a value of size smaller than a register into a register are zero-extended. However, this is not the case and these bits are unspecified. This can result in a `left.cmovz(&right, condition)` not moving `right` into `left`, even if `condition == 0`. \n\n### Details\n\nThe Rust reference for inline assembly states that:\n> If a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs [..]. [Reference](https://doc.rust-lang.org/reference/inline-assembly.html#r-asm.register-operands.smaller-value)\n\nIf the high bits `[8..]` of the selector loaded into a register in the `Cmov` implementation or the high bits `[16..]` of `self` or `other` for CmovEq (specifically the implementation for `u16` and `i16`) are set, the inline asm compares will produce a different result than the Rust code expects based on the narrow types.\n\nIn other words, the following assert fails, even though `condition as u8` is zero:\n```rust\nlet condition: u32 = black_box(1 << 8);\nlet mut left = 1;\nlet right = 2;\nleft.cmovz(&right, condition as u8);\nassert_eq!(left, right);\n```\nBecause the ninth bit is set in the original variable, this bit is also set when the truncated condition is loaded into the input register for the `cmp`, causing the `csel` to select the wrong value.\n\nThe problematic code is located in `cmov/src/backends/aarch64.rs` [here for Cmov](https://github.com/RustCrypto/utils/blob/dad5e3b9e66d929e86144fe7c8f25371892e35f3/cmov/src/backends/aarch64.rs#L4-L19) and [here for CmovEq](https://github.com/RustCrypto/utils/blob/dad5e3b9e66d929e86144fe7c8f25371892e35f3/cmov/src/backends/aarch64.rs#L60-L72).\n\nThe following function:\n```rust\n#[unsafe(no_mangle)]\npub fn cmovz_wrong_output(left: &mut i32, right: i32, condition: u32) {\n    left.cmovz(&right, condition as u8);\n}\n```\nproduces the assembly:\n```asm\ncmovz_wrong_output:\n\t.cfi_startproc\n\tldr w8, [x0]\n\t//APP\n\tcmp w2, #0\n\tcsel w8, w1, w8, eq\n\t//NO_APP\n\tstr w8, [x0]\n\tret\n```\n\nwhich compares the 32-bits of the condition value against 0, instead of the intended 8.\n\nSimilarly, the following function using `cmoveq`\n```rust\n#[unsafe(no_mangle)]\npub fn cmoveq_wrong_output(left: u32, right: u32, input: u8, output: &mut u8) {\n    (left as u16).cmoveq(&(right as u16), input, output);\n}\n```\ncompiles to:\n```asm\ncmoveq_wrong_output:\n\t.cfi_startproc\n\tldrb w8, [x3]\n\tand w9, w2, #0xff\n\t//APP\n\teor w10, w0, w1\n\tcmp w10, #0\n\tcsel w8, w9, w8, eq\n\t//NO_APP\n\tstrb w8, [x3]\n\tret\n```\nwhere 32 bits of left and right are compared instead of 16. The same happens for `i16`.\n\nFor CmovEq, it seems the `u8/i8` impls are not affected, as they are calling `u16::from` in [the implementation](https://github.com/RustCrypto/utils/blob/dad5e3b9e66d929e86144fe7c8f25371892e35f3/cmov/src/lib.rs#L119) which causes the upper bits to be masked out.\n\n### PoC\n\nThe following two test cases fail on `aarch64-unknown-linux-gnu` when compiled with `--release` (the cmovz one even in debug) emulated with qemu.\n\n```\n> rustc --version\nrustc 1.94.0 (4a4ef493e 2026-03-02)\n```\n\n```rust\n#[cfg(test)]\nmod tests {\n    use core::hint::black_box;\n\n    use cmov::{Cmov, CmovEq};\n\n    #[test]\n    fn cmovz_wrong_output() {\n        // The black box is necessary here, as otherwise the compiler will \n        // provide a constant 0 to the csel\n        let condition: u32 = black_box(1 << 8);\n        let mut left = 1;\n        let right = 2;\n        // I added this debug_assert as a sanity check, but funnily it causes\n        // the wrong cmov behavior in debug as well (as opposed to only in release mode without the debug_assert)\n        debug_assert_eq!(0, condition as u8);\n        left.cmovz(&right, condition as u8);\n        assert_eq!(left, right);\n    }\n\n    #[test]\n    fn cmoveq_wrong_output() {\n        let input = 1;\n        let mut output = 0;\n        let left: u32 = black_box(1 << 16);\n        let right: u32 = black_box(1 << 17);\n        // asserting in release mode here would hide the bug, the debug_assert_eq is\n        // a sanity check that these values SHOULD be equal\n        debug_assert_eq!(left as u16, right as u16);\n        (left as u16).cmoveq(&(right as u16), input, &mut output);\n        assert_eq!(input, output);\n    }\n}\n```\n\n### Impact\n\nUnder specific circumstances, this issue can cause `Cmov/CmovEq` to produce incorrect output on `aarch64`. However, whether this bug can actually manifest depends on the surrounding code that calls the relevant impls. In the PoC, a narrowing cast is required, which masks out the set bits from Rust's point of view, but which are then used in the inline assembly. \n\n### Additional Finding\n\nPR [#1299](https://github.com/RustCrypto/utils/pull/1299) fixed a different bug in the aarch64 backend but introduced a small error. The `csel!` macro expect a `cmp` expression as its first argument which is never used. The compare is always `\"cmp {0:w}, 0\",` even for `csel64!`, which intends to use `cmp {0:x}, 0`. Given the bug described above, this oversight actually reduces its impact slightly, as only the bits in position`[8..32]` can cause issues,  even for those impls that use `csel64!`.","state":0,"publishedAt":"2026-07-02T17:18:11+00:00","modifiedAt":"2026-09-10T03:51:10.2503909+00:00","affected":[{"ecosystem":"crates.io","name":"cmov","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0.1.1"},{"kind":"fixed","version":"0.5.4"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V4","vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:P","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/RustCrypto/utils/security/advisories/GHSA-3rjw-m598-pq24","https://github.com/RustCrypto/utils"],"sourceUrl":"https://github.com/advisories/GHSA-3rjw-m598-pq24","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-3rjw-m598-pq24","identifiers":["GHSA-3rjw-m598-pq24","CVE-2026-50185"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-10-27T11:11:56.6678062+00:00","complete":true,"records":1758,"lastAttemptAt":"2026-10-27T11:11:56.6678062+00:00","lastSuccessAt":"2026-10-27T11:11:56.8243728+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:11:56.8243749+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-187d2b72c94ceb8c4417f1032ec3109a3a3a7b730abadb561d8cdd2a96e04d27-9f7402126dc5529a1b08865d811f13ed8c445867d9a45d41761dcc621011842e","complete":true,"records":414919,"lastAttemptAt":"2026-10-27T11:09:37.0832785+00:00","lastSuccessAt":"2026-10-27T11:09:37.0305451+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:09:37.0832785+00:00","snapshotPublishedAt":"2026-10-27T10:30:23+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-43321610382942b8ad8c85917eab1546","complete":false,"records":4735,"lastAttemptAt":"2026-10-27T11:12:26.0342942+00:00","lastSuccessAt":"2026-10-24T23:18:49.862+00:00","error":"InvalidDataException","lastVerifiedAt":"2026-10-24T23:18:49.862+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":0,"expectedIdentifiers":13480},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-10-27T11:11:56.6678062+00:00","complete":true,"records":1093,"lastAttemptAt":"2026-10-27T11:11:56.6678062+00:00","lastSuccessAt":"2026-10-27T11:11:56.8252029+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:11:56.8252036+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-c6c2b354cca8400a8af7dee419d3a5e5","complete":false,"records":13419,"lastAttemptAt":"2026-10-27T11:12:23.9234765+00:00","lastSuccessAt":"2026-10-27T11:12:23.919+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:12:23.919+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13477,"expectedIdentifiers":13480},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-c3121b113ecd88f1c92ccfa0ec9757ddc8386e4c12afd2597450b815744f800a","complete":true,"records":9779,"lastAttemptAt":"2026-10-27T11:11:51.9998316+00:00","lastSuccessAt":"2026-10-26T15:37:14.3083004+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:11:51.9998316+00:00","snapshotPublishedAt":"2026-10-26T14:48:41+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-bad5d31e7f6a5e3d338e11d8789069b63620a403f927730769688b42f768e4f6","complete":true,"records":1965,"lastAttemptAt":"2026-10-27T11:09:37.2374562+00:00","lastSuccessAt":"2026-10-26T16:35:43.1460623+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:09:37.2374562+00:00","snapshotPublishedAt":"2026-10-26T16:18:29+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-112103ad2640f03b274c0e731c3f4398f99b844297f2e68d520aeec5561d6df2","complete":true,"records":2997,"lastAttemptAt":"2026-10-27T11:11:56.6249674+00:00","lastSuccessAt":"2026-10-27T11:11:56.4872351+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:11:56.6249674+00:00","snapshotPublishedAt":"2026-10-27T10:33:05+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-004e8284b6ea6365b51ff4b74ec6be79d1edd4a5cab2e56b5baf5b4428e96986","complete":true,"records":230813,"lastAttemptAt":"2026-10-27T11:11:51.8474073+00:00","lastSuccessAt":"2026-10-27T11:11:50.4927963+00:00","error":null,"lastVerifiedAt":"2026-10-27T11:11:51.8474073+00:00","snapshotPublishedAt":"2026-10-27T11:03:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null}]}