High firefox UAF 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the Audio/Video: Playback component
ComponentToolkit
Bug ClassUAF
Tracker2014827
Fix commit5020c06022c0 (firefox) +89/-43
CISA KEVNot listed
CreditedEvyatar Ben Asher, Keane Lucas, Nicholas Carlini, Newton Cheng, Daniel Freeman, Alex Gaynor, and Joel Weinberger using Claude from Anthropic
Disclosed2026-02-24

Files Changed

  • .cargo/config.toml.in
  • Cargo.lock
  • third_party/rust/mp4parse_capi/.cargo-checksum.json
  • third_party/rust/mp4parse_capi/src/lib.rs
  • toolkit/library/rust/shared/Cargo.toml
diff --git a/.cargo/config.toml.in b/.cargo/config.toml.in
index 34708c9332a..93084490749 100644
--- a/.cargo/config.toml.in
+++ b/.cargo/config.toml.in
@@ -100,9 +100,9 @@ git = "https://github.com/mozilla/midir.git"
 rev = "54759f681f3b523e120d047adec2bb1a97152017"
 replace-with = "vendored-sources"
 
-[source."git+https://github.com/mozilla/mp4parse-rust?rev=f955be5d2a04a631c0f1777d6f35370ea1a99e2d"]
+[source."git+https://github.com/mozilla/mp4parse-rust?rev=26788821a443614d88470fe71f51f66720696096"]
 git = "https://github.com/mozilla/mp4parse-rust"
-rev = "f955be5d2a04a631c0f1777d6f35370ea1a99e2d"
+rev = "26788821a443614d88470fe71f51f66720696096"
 replace-with = "vendored-sources"
 
 [source."git+https://github.com/mozilla/neqo?tag=v0.22.2"]
diff --git a/Cargo.lock b/Cargo.lock
index a2a0fd9e5a2..da40b0974b0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4735,7 +4735,7 @@ dependencies = [
 [[package]]
 name = "mp4parse"
 version = "0.17.0"
-source = "git+https://github.com/mozilla/mp4parse-rust?rev=f955be5d2a04a631c0f1777d6f35370ea1a99e2d#f955be5d2a04a631c0f1777d6f35370ea1a99e2d"
+source = "git+https://github.com/mozilla/mp4parse-rust?rev=26788821a443614d88470fe71f51f66720696096#26788821a443614d88470fe71f51f66720696096"
 dependencies = [
  "bitreader",
  "byteorder",
@@ -4752,7 +4752,7 @@ version = "0.1.0"
 [[package]]
 name = "mp4parse_capi"
 version = "0.17.0"
-source = "git+https://github.com/mozilla/mp4parse-rust?rev=f955be5d2a04a631c0f1777d6f35370ea1a99e2d#f955be5d2a04a631c0f1777d6f35370ea1a99e2d"
+source = "git+https://github.com/mozilla/mp4parse-rust?rev=26788821a443614d88470fe71f51f66720696096#26788821a443614d88470fe71f51f66720696096"
 dependencies = [
  "byteorder",
  "fallible_collections",
diff --git a/third_party/rust/mp4parse_capi/.cargo-checksum.json b/third_party/rust/mp4parse_capi/.cargo-checksum.json
index bac60e6127f..4001a7e470f 100644
--- a/third_party/rust/mp4parse_capi/.cargo-checksum.json
+++ b/third_party/rust/mp4parse_capi/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"6378d72e211e2a9c9b03e2557149060d95860bc09dd9ad08aad2c412a00f106d","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"f776ed4bbb7b58a5684402a9c5c28dfe1fa02b6b184139b2c2c49384cc1e3723","cbindgen.toml":"62066cd34285ab9e7f1cc5db8950a51e9e080f5a85bd55ad43d7022e4eae2758","examples/dump.rs":"d2bcc45ac18c0f9c6f80c87519a481952dd6a4a49219d64e9e1215b043601756","src/lib.rs":"97595df451aeffc66d424c93897cf87299cb83dc61ac1bc11c2dac0144a7262d","tests/test_avis.rs":"f01df914abcb18b562e74c39e15a0fa53159dbe93e9bd8698fab30d792e74645","tests/test_chunk_out_of_range.rs":"4039d0db0ee5973787e4ca14cea510fd958ae5d21856a79240a5e7b826caa18d","tests/test_encryption.rs":"f62131a36b0516caf9e2c48f8aea060d300b0f5c8a32bc54d31cbc97aa25b4e6","tests/test_fragment.rs":"d3f805cc2107481ee9a989818af3addbb3ea1faf7422ea7f4416591d03031318","tests/test_rotation.rs":"23fa4898eca2e17255bc1ba2f538707a6554fb4644bb75f80548ae56a7cd2d44","tests/test_sample_table.rs":"6a0095c155a3618b2338d7252101ff16adaa020f511abdac410548b417aee11b","tests/test_workaround_stsc.rs":"1d17a394f55e1524c30888bfe1e57e2b0457444b79c23eb91b02d2edf859c9ad","tests/test_xhe_aac.rs":"358d2b2845e47e4753d994834dd3fd59c4420e4549302e5d2d3f5bb4f4772e6d"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"6378d72e211e2a9c9b03e2557149060d95860bc09dd9ad08aad2c412a00f106d","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"f776ed4bbb7b58a5684402a9c5c28dfe1fa02b6b184139b2c2c49384cc1e3723","cbindgen.toml":"62066cd34285ab9e7f1cc5db8950a51e9e080f5a85bd55ad43d7022e4eae2758","examples/dump.rs":"d2bcc45ac18c0f9c6f80c87519a481952dd6a4a49219d64e9e1215b043601756","src/lib.rs":"33f7b24462db7113f6d06630567df2e72ee69851ecc67042a444317dc1de9008","tests/test_avis.rs":"f01df914abcb18b562e74c39e15a0fa53159dbe93e9bd8698fab30d792e74645","tests/test_chunk_out_of_range.rs":"4039d0db0ee5973787e4ca14cea510fd958ae5d21856a79240a5e7b826caa18d","tests/test_encryption.rs":"f62131a36b0516caf9e2c48f8aea060d300b0f5c8a32bc54d31cbc97aa25b4e6","tests/test_fragment.rs":"d3f805cc2107481ee9a989818af3addbb3ea1faf7422ea7f4416591d03031318","tests/test_rotation.rs":"23fa4898eca2e17255bc1ba2f538707a6554fb4644bb75f80548ae56a7cd2d44","tests/test_sample_table.rs":"6a0095c155a3618b2338d7252101ff16adaa020f511abdac410548b417aee11b","tests/test_workaround_stsc.rs":"1d17a394f55e1524c30888bfe1e57e2b0457444b79c23eb91b02d2edf859c9ad","tests/test_xhe_aac.rs":"358d2b2845e47e4753d994834dd3fd59c4420e4549302e5d2d3f5bb4f4772e6d"},"package":null}
\ No newline at end of file
diff --git a/third_party/rust/mp4parse_capi/src/lib.rs b/third_party/rust/mp4parse_capi/src/lib.rs
index 6acd268c2a1..7183e111f71 100644
--- a/third_party/rust/mp4parse_capi/src/lib.rs
+++ b/third_party/rust/mp4parse_capi/src/lib.rs
@@ -168,12 +168,20 @@ impl Default for Mp4parseByteData {
 impl Mp4parseByteData {
     fn set_data(&mut self, data: &[u8]) {
         self.length = data.len();
-        self.data = data.as_ptr();
+        self.data = if data.is_empty() {
+            std::ptr::null()
+        } else {
+            data.as_ptr()
+        };
     }
 
     fn set_indices(&mut self, data: &[Indice]) {
         self.length = data.len();
-        self.indices = data.as_ptr();
+        self.indices = if data.is_empty() {
+            std::ptr::null()
+        } else {
+            data.as_ptr()
+        };
     }
 }
 
@@ -288,8 +296,8 @@ pub struct Mp4parseFragmentInfo {
 #[derive(Default)]
 pub struct Mp4parseParser {
     context: MediaContext,
-    opus_header: TryHashMap<u32, TryVec<u8>>,
-    pssh_data: TryVec<u8>,
+    opus_header: TryHashMap<(u32, usize), TryVec<u8>>,
+    pssh_data: Option<TryVec<u8>>,
     sample_table: TryHashMap<u32, TryVec<Indice>>,
     // Store a mapping from track index (not id) to associated sample
     // descriptions. Because each track has a variable number of sample
@@ -697,6 +705,16 @@ fn get_track_audio_info(
     track_index: u32,
     info: &mut Mp4parseTrackAudioInfo,
 ) -> Result<(), Mp4parseStatus> {
+    if let Some(sample_info) = parser.audio_track_sample_descriptions.get(&track_index) {
+        info.sample_info_count = sample_info.len() as u32;
+        info.sample_info = if sample_info.is_empty() {
+            std::ptr::null()
+        } else {
+            sample_info.as_ptr()
+        };
+        return Ok(());
+    }
+
     let Mp4parseParser {
         context,
         opus_header,
@@ -724,7 +742,7 @@ fn get_track_audio_info(
     }
 
     let mut audio_sample_infos = TryVec::with_capacity(stsd.descriptions.len())?;
-    for description in stsd.descriptions.iter() {
+    for (desc_i, description) in stsd.descriptions.iter().enumerate() {
         let mut sample_info = Mp4parseTrackAudioSampleInfo::default();
         let audio = match description {
             SampleEntry::Audio(a) => a,
@@ -770,10 +788,10 @@ fn get_track_audio_info(
                 if esds.codec_esds.len() > u32::MAX as usize {
                     return Err(Mp4parseStatus::Invalid);
                 }
-                sample_info.extra_data.length = esds.codec_esds.len();
-                sample_info.extra_data.data = esds.codec_esds.as_ptr();
-                sample_info.codec_specific_config.length = esds.decoder_specific_data.len();
-                sample_info.codec_specific_config.data = esds.decoder_specific_data.as_ptr();
+                sample_info.extra_data.set_data(&esds.codec_esds);
+                sample_info
+                    .codec_specific_config
+                    .set_data(&esds.decoder_specific_data);
                 if let Some(rate) = esds.audio_sample_rate {
                     sample_info.sample_rate = rate;
                 }
@@ -794,8 +812,7 @@ fn get_track_audio_info(
                 if streaminfo.block_type != 0 || streaminfo.data.len() != 34 {
                     return Err(Mp4parseStatus::Invalid);
                 }
-                sample_info.codec_specific_config.length = streaminfo.data.len();
-                sample_info.codec_specific_config.data = streaminfo.data.as_ptr();
+                sample_info.codec_specific_config.set_data(&streaminfo.data);
             }
             AudioCodecSpecific::OpusSpecificBox(ref opus) => {
                 let mut v = TryVec::new();
@@ -804,20 +821,18 @@ fn get_track_audio_info(
                         return Err(Mp4parseStatus::Invalid);
                     }
                     Ok(_) => {
-                        opus_header.insert(track_index, v)?;
-                        if let Some(v) = opus_header.get(&track_index) {
+                        opus_header.insert((track_index, desc_i), v)?;
+                        if let Some(v) = opus_header.get(&(track_index, desc_i)) {
                             if v.len() > u32::MAX as usize {
                                 return Err(Mp4parseStatus::Invalid);
                             }
-                            sample_info.codec_specific_config.length = v.len();
-                            sample_info.codec_specific_config.data = v.as_ptr();
+                            sample_info.codec_specific_config.set_data(v);
                         }
                     }
                 }
             }
             AudioCodecSpecific::ALACSpecificBox(ref alac) => {
-                sample_info.codec_specific_config.length = alac.data.len();
-                sample_info.codec_specific_config.data = alac.data.as_ptr();
+                sample_info.codec_specific_config.set_data(&alac.data);
             }
             AudioCodecSpecific::MP3 | AudioCodecSpecific::LPCM => (),
             #[cfg(feature = "3gpp")]
@@ -874,7 +889,11 @@ fn get_track_audio_info(
                 return Err(Mp4parseStatus::Invalid);
             }
             info.sample_info_count = sample_info.len() as u32;
-            info.sample_info = sample_info.as_ptr();
+            info.sample_info = if sample_info.is_empty() {
+                std::ptr::null()
+            } else {
+                sample_info.as_ptr()
+            };
         }
         None => return Err(Mp4parseStatus::Invalid), // Shouldn't happen, we just inserted the info!
     }
@@ -943,6 +962,26 @@ fn mp4parse_get_track_video_info_safe(
         return Err(Mp4parseStatus::Invalid);
     }
 
+    if let Some(ref stsd) = track.stsd {
+        for description in stsd.descriptions.iter() {
+            if let SampleEntry::Video(video) = description {
+                if let Some(ratio) = video.pixel_aspect_ratio {
+                    info.pixel_aspect_ratio = ratio;
+                }
+            }
+        }
+    }
+
+    if let Some(sample_info) = parser.video_track_sample_descriptions.get(&track_index) {
+        info.sample_info_count = sample_info.len() as u32;
+        info.sample_info = if sample_info.is_empty() {
+            std::ptr::null()
+        } else {
+            sample_info.as_ptr()
+        };
+        return Ok(());
+    }
+
     // Handle track.stsd
     let stsd = match track.stsd {
Loading diff…