CVE-2024-39697 (published 2024-07-09T15:15Z)

phonenumber is a library for parsing, formatting and validating international phone numbers. Since 0.3.4, the phonenumber parsing code may panic due to a panic-guarded out-of-bounds access on the phonenumber string. In a typical deployment of rust-phonenumber, this may get triggered by feeding a maliciously crafted phonenumber, e.g. over the network, specifically strings of the form `+dwPAA;phone-context=AA`, where the "number" part potentially parses as a number larger than 2^56. This vulnerability is fixed in 0.3.6.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/whisperfish/rust-phonenumber
commit_details: repo_url: https://github.com/whisperfish/rust-phonenumber
author: Ruben De Smet
author_date: 2024-07-09 15:27:34+02:00
msg: Merge branch 'proptest' for CVE-2024-39697
repository: repo_url: https://github.com/whisperfish/rust-phonenumber
repo_name: whisperfish/rust-phonenumber
description: Library for parsing, formatting and validating international phone numbers.
repo_language: Rust
owner: whisperfish
repo_url: https://github.com/whisperfish/rust-phonenumber
commit_details: repo_url: https://github.com/whisperfish/rust-phonenumber
author: Ruben De Smet
author_date: 2023-04-11 21:23:23+02:00
msg: Fit NationalNumber in 64 bits
repository: repo_url: https://github.com/whisperfish/rust-phonenumber
repo_name: whisperfish/rust-phonenumber
description: Library for parsing, formatting and validating international phone numbers.
repo_language: Rust
owner: whisperfish
file_changes: filename: national_number.rs
change_type: ModificationType.MODIFY
diff: @@ -19,7 +19,25 @@ use std::fmt;
 #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Hash, Debug)]
 pub struct NationalNumber {
     pub(crate) value: u64,
+}
+
+impl NationalNumber {
+    pub fn new(value: u64, zeros: u8) -> Self {
+        // E.164 specifies a maximum of 15 decimals, which corresponds to slightly over 48.9 bits.
+        // 56 bits ought to cut it here.
+        assert!(value < (1 << 56), "number too long");
+        Self {
+            value: ((zeros as u64) << 56) | value,
+        }
+    }
 
+    /// The number without any leading zeroes.
+    pub fn value(&self) -> u64 {
+        self.value & 0x00ffffffffffffff
+    }
+
+    /// The number of leading zeroes.
+    ///
     /// In some countries, the national (significant) number starts with one or
     /// more "0"s without this being a national prefix or trunk code of some
     /// kind.  For example, the leading zero in the national (significant) number
@@ -36,18 +54,8 @@ pub struct NationalNumber {
     ///
     /// Clients who use the parsing or conversion functionality of the i18n phone
     /// number libraries will have these fields set if necessary automatically.
-    pub(crate) zeros: u8,
-}
-
-impl NationalNumber {
-    /// The number without any leading zeroes.
-    pub fn value(&self) -> u64 {
-        self.value
-    }
-
-    /// The number of leading zeroes.
     pub fn zeros(&self) -> u8 {
-        self.zeros
+        (self.value >> 56) as u8
     }
 }
 
@@ -59,10 +67,10 @@ impl From<NationalNumber> for u64 {
 
 impl fmt::Display for NationalNumber {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        for _ in 0..self.zeros {
+        for _ in 0..self.zeros() {
             write!(f, "0")?;
         }
 
-        write!(f, "{}", self.value)
+        write!(f, "{}", self.value())
     }
 }

programming_language: Rust
filename: mod.rs
change_type: ModificationType.MODIFY
diff: @@ -84,10 +84,10 @@ pub fn parse_with<S: AsRef<str>>(
             source: number.country,
         },
 
-        national: NationalNumber {
-            value: number.national.parse()?,
-            zeros: number.national.chars().take_while(|&c| c == '0').count() as u8,
-        },
+        national: NationalNumber::new(
+            number.national.parse()?,
+            number.national.chars().take_while(|&c| c == '0').count() as u8,
+        ),
 
         extension: number.extension.map(|s| Extension(s.into_owned())),
         carrier: number.carrier.map(|s| Carrier(s.into_owned())),
@@ -109,10 +109,7 @@ mod test {
                 source: country::Source::Default,
             },
 
-            national: NationalNumber {
-                value: 33316005,
-                zeros: 0,
-            },
+            national: NationalNumber::new(33316005, 0),
 
             extension: None,
             carrier: None,
@@ -200,10 +197,7 @@ mod test {
                 source: country::Source::Number,
             },
 
-            national: NationalNumber {
-                value: 64123456,
-                zeros: 0,
-            },
+            national: NationalNumber::new(64123456, 0),
 
             extension: None,
             carrier: None,
@@ -221,10 +215,7 @@ mod test {
                     source: country::Source::Default,
                 },
 
-                national: NationalNumber {
-                    value: 30123456,
-                    zeros: 0,
-                },
+                national: NationalNumber::new(30123456, 0),
 
                 extension: None,
                 carrier: None,
@@ -239,10 +230,7 @@ mod test {
                     source: country::Source::Plus,
                 },
 
-                national: NationalNumber {
-                    value: 2345,
-                    zeros: 0,
-                },
+                national: NationalNumber::new(2345, 0,),
 
                 extension: None,
                 carrier: None,
@@ -257,10 +245,7 @@ mod test {
                     source: country::Source::Default,
                 },
 
-                national: NationalNumber {
-                    value: 12,
-                    zeros: 0,
-                },
+                national: NationalNumber::new(12, 0,),
 
                 extension: None,
                 carrier: None,
@@ -275,10 +260,7 @@ mod test {
                     source: country::Source::Default,
                 },
 
-                national: NationalNumber {
-                    value: 3121286979,
-                    zeros: 0,
-                },
+                national: NationalNumber::new(3121286979, 0),
 
                 extension: None,
                 carrier: Some("12".into()),

programming_language: Rust