From 0e72641a96d782854f939c5c49439c697162de92 Mon Sep 17 00:00:00 2001 From: choksi2212 Date: Sun, 18 Oct 2026 14:37:23 +0530 Subject: [PATCH] der: return an error instead of panicking on truncated binary REAL values The binary-encoding branch of the REAL (f64) decoder (X.690 section 8.5.7) reads the exponent octets at fixed offsets in the value content without checking that the content is long enough to hold them: the one-octet exponent form indexes bytes[1] and the two-octet exponent form indexes bytes[1] and bytes[2]. A REAL value whose content is the single octet 0x80 therefore reaches bytes[1] on a length-1 slice and panics with 'index out of bounds' instead of returning Err, taking down any caller that decodes f64 values (or structs containing them) from untrusted input. Confirm the content length before reading the exponent octets, in both exponent forms, and return a Real value error otherwise. Fixes #2482 Signed-off-by: choksi2212 --- der/src/asn1/real.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/der/src/asn1/real.rs b/der/src/asn1/real.rs index 7169434de..08f3f1860 100644 --- a/der/src/asn1/real.rs +++ b/der/src/asn1/real.rs @@ -42,11 +42,19 @@ impl<'a> DecodeValue<'a> for f64 { let exponent = match mnth_bits_to_u8::<1, 0>(bytes) { 0 => { mantissa_start = 2; + // Section 8.5.7.4: the exponent octet must follow the control octet + if bytes.len() < 2 { + return Err(reader.error(Tag::Real.value_error())); + } let ebytes = (i16::from_be_bytes([0x0, bytes[1]])).to_be_bytes(); u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ebytes[0], ebytes[1]]) } 1 => { mantissa_start = 3; + // Section 8.5.7.4: both exponent octets must follow the control octet + if bytes.len() < 3 { + return Err(reader.error(Tag::Real.value_error())); + } let ebytes = (i16::from_be_bytes([bytes[1], bytes[2]])).to_be_bytes(); u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ebytes[0], ebytes[1]]) } @@ -889,4 +897,22 @@ mod tests { ); } } + + #[test] + fn decode_truncated_binary_encoding_is_an_error() { + // GHSA-27m6-7r55-vwr2: the binary-encoding branch reads the exponent + // octets at a fixed offset in the value content without checking that + // the content is long enough to hold them. Truncated content must be + // an error, not a panic. + // + // One content octet in the one-octet exponent form (bits 1-0 = 00): + // reading `bytes[1]` is out of bounds. + assert!(f64::from_der(&[0x09, 0x01, 0x80]).is_err()); + // One content octet in the two-octet exponent form (bits 1-0 = 01): + // reading `bytes[1]` is out of bounds. + assert!(f64::from_der(&[0x09, 0x01, 0x81]).is_err()); + // Two content octets in the two-octet exponent form: + // reading `bytes[2]` is out of bounds. + assert!(f64::from_der(&[0x09, 0x02, 0x81, 0x00]).is_err()); + } }