Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

validation improvements, fixes #301 #331

Draft
wants to merge 8 commits into
base: dev-0.6
Choose a base branch
from
3 changes: 2 additions & 1 deletion symphonia-codec-aac/src/aac/cpe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ impl ChannelPair {

if common_window {
// Decode the common ICS info block into the first channel.
self.ics0.info.decode(bs)?;
// do not call self.ics0.info.decode() as it will skip required validations present in self.ics0.decode_info()
self.ics0.decode_info(bs)?;

// Mid-side stereo mask decoding.
self.ms_mask_present = bs.read_bits_leq32(2)? as u8;
Expand Down
14 changes: 13 additions & 1 deletion symphonia-codec-aac/src/aac/ics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl IcsInfo {
}
}

/// this method should be called from Ics::decode_info() which will perform additional validations for max_sfb
pub fn decode<B: ReadBitsLtr>(&mut self, bs: &mut B) -> Result<()> {
self.prev_window_sequence = self.window_sequence;
self.prev_window_shape = self.window_shape;
Expand Down Expand Up @@ -291,6 +292,16 @@ impl Ics {
self.sfb_cb[g][sfb] == INTENSITY_HCB
}

pub fn decode_info<B: ReadBitsLtr>(&mut self, bs: &mut B) -> Result<()> {
self.info.decode(bs)?;

// validate info.max_sfb - it should not be bigger than bands array len - 1
if self.info.max_sfb + 1 > self.get_bands().len() {
return decode_error("aac: ics info max_sfb is too big for the bands size");
}
Ok(())
}

fn decode_scale_factor_data<B: ReadBitsLtr>(&mut self, bs: &mut B) -> Result<()> {
let mut noise_pcm_flag = true;
let mut scf_intensity = -INTENSITY_SCALE_MIN;
Expand Down Expand Up @@ -407,7 +418,8 @@ impl Ics {

// If a common window is used, a common ICS info was decoded previously.
if !common_window {
self.info.decode(bs)?;
// do not call self.info.decode() as it will skip required validations present in the decode_info()
self.decode_info(bs)?;
}

self.decode_section_data(bs)?;
Expand Down
48 changes: 31 additions & 17 deletions symphonia-codec-aac/src/adts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ impl FormatReader for AdtsReader<'_> {

fn approximate_frame_count(mut source: &mut MediaSourceStream<'_>) -> Result<Option<u64>> {
let original_pos = source.pos();
let total_len = match source.byte_len() {
let remaining_len = match source.byte_len() {
Some(len) => len - original_pos,
_ => return Ok(None),
};
Expand Down Expand Up @@ -428,34 +428,48 @@ fn approximate_frame_count(mut source: &mut MediaSourceStream<'_>) -> Result<Opt
else {
// The number of points to sample within the stream.
const NUM_SAMPLE_POINTS: u64 = 4;
const NUM_FRAMES: u32 = 100;

let step = (total_len - original_pos) / NUM_SAMPLE_POINTS;
let step = remaining_len / NUM_SAMPLE_POINTS;

// Skip the first sample point (start of file) since it is an outlier.
for new_pos in (original_pos..total_len - step).step_by(step as usize).skip(1) {
let res = source.seek(SeekFrom::Start(new_pos));
if res.is_err() {
break;
}
// file can be small enough and not have enough NUM_FRAMES, but we can still read at least one header
if step > 0 {
for new_pos in (original_pos..(original_pos + remaining_len)).step_by(step as usize) {
let mut cur_pos = new_pos;
if source.seek(SeekFrom::Start(cur_pos)).is_err() {
break;
}

for _ in 0..NUM_FRAMES {
let header = match AdtsHeader::read(&mut source) {
Ok(header) => header,
_ => break,
};

for _ in 0..=100 {
let header = match AdtsHeader::read(&mut source) {
Ok(header) => header,
_ => break,
};
parsed_n_frames += 1;
n_bytes += u64::from(header.frame_len);

parsed_n_frames += 1;
n_bytes += u64::from(header.frame_len);
// skip frame to avoid meeting sync word in the audio data and for quick sync()
cur_pos += u64::from(header.frame_len);
if source.seek(SeekFrom::Start(cur_pos)).is_err() {
break;
}
}

// if reading NUM_FRAMES frames overflow the next step position then break
if cur_pos > new_pos + step {
break;
}
}
}

let _ = source.seek(SeekFrom::Start(original_pos))?;
}

debug!("adts: parsed {} of {} bytes to approximate duration", n_bytes, total_len);
debug!("adts: parsed {} of {} bytes to approximate duration", n_bytes, remaining_len);

match parsed_n_frames {
0 => Ok(None),
_ => Ok(Some(total_len / (n_bytes / parsed_n_frames) * SAMPLES_PER_AAC_PACKET)),
_ => Ok(Some(remaining_len / (n_bytes / parsed_n_frames) * SAMPLES_PER_AAC_PACKET)),
}
}
4 changes: 1 addition & 3 deletions symphonia-format-isomp4/src/atoms/avcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ use symphonia_core::errors::{decode_error, Result};
use symphonia_core::io::ReadBytes;

use crate::atoms::stsd::VisualSampleEntry;
use crate::atoms::{Atom, AtomHeader};

const MAX_ATOM_SIZE: u64 = 1024;
use crate::atoms::{Atom, AtomHeader, MAX_ATOM_SIZE};

#[allow(dead_code)]
#[derive(Debug)]
Expand Down
12 changes: 11 additions & 1 deletion symphonia-format-isomp4/src/atoms/co64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use symphonia_core::errors::Result;
use symphonia_core::errors::{decode_error, Result};
use symphonia_core::io::ReadBytes;

use crate::atoms::{Atom, AtomHeader};
Expand All @@ -21,7 +21,17 @@ impl Atom for Co64Atom {
fn read<B: ReadBytes>(reader: &mut B, mut header: AtomHeader) -> Result<Self> {
let (_, _) = header.read_extended_header(reader)?;

// minimum data size is 4 bytes
let len = match header.data_len() {
Some(len) if len >= 4 => len as u32,
Some(_) => return decode_error("isomp4 (co64): atom size is less than 16 bytes"),
None => return decode_error("isomp4 (co64): expected atom size to be known"),
};

let entry_count = reader.read_be_u32()?;
if entry_count != (len - 4) / 8 {
return decode_error("isomp4 (co64): invalid entry count");
}

// TODO: Apply a limit.
let mut chunk_offsets = Vec::with_capacity(entry_count as usize);
Expand Down
15 changes: 8 additions & 7 deletions symphonia-format-isomp4/src/atoms/dac3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use symphonia_core::codecs::audio::well_known::CODEC_ID_AC3;
use symphonia_core::errors::{Error, Result};
use symphonia_core::errors::{decode_error, Result};
use symphonia_core::io::ReadBytes;

use crate::atoms::stsd::AudioSampleEntry;
use crate::atoms::{Atom, AtomHeader};
use crate::atoms::{Atom, AtomHeader, MAX_ATOM_SIZE};

#[allow(dead_code)]
#[derive(Debug)]
Expand All @@ -21,12 +21,13 @@ pub struct Dac3Atom {

impl Atom for Dac3Atom {
fn read<B: ReadBytes>(reader: &mut B, header: AtomHeader) -> Result<Self> {
// AC3SpecificBox should have length
let len = header
.data_len()
.ok_or(Error::DecodeError("isomp4 (dac3): expected atom size to be known"))?;
let len = match header.data_len() {
Some(len) if len <= MAX_ATOM_SIZE => len as usize,
Some(_) => return decode_error("isomp4 (dac3): atom size is greater than 1kb"),
None => return decode_error("isomp4 (dac3): expected atom size to be known"),
};

let extra_data = reader.read_boxed_slice_exact(len as usize)?;
let extra_data = reader.read_boxed_slice_exact(len)?;

Ok(Dac3Atom { extra_data })
}
Expand Down
15 changes: 8 additions & 7 deletions symphonia-format-isomp4/src/atoms/dec3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use symphonia_core::codecs::audio::well_known::CODEC_ID_EAC3;
use symphonia_core::errors::{Error, Result};
use symphonia_core::errors::{decode_error, Result};
use symphonia_core::io::ReadBytes;

use crate::atoms::stsd::AudioSampleEntry;
use crate::atoms::{Atom, AtomHeader};
use crate::atoms::{Atom, AtomHeader, MAX_ATOM_SIZE};

#[allow(dead_code)]
#[derive(Debug)]
Expand All @@ -21,12 +21,13 @@ pub struct Dec3Atom {

impl Atom for Dec3Atom {
fn read<B: ReadBytes>(reader: &mut B, header: AtomHeader) -> Result<Self> {
// EAC3SpecificBox should have length
let len = header
.data_len()
.ok_or(Error::DecodeError("isomp4 (dec3): expected atom size to be known"))?;
let len = match header.data_len() {
Some(len) if len <= MAX_ATOM_SIZE => len as usize,
Some(_) => return decode_error("isomp4 (dec3): atom size is greater than 1kb"),
None => return decode_error("isomp4 (dec3): expected atom size to be known"),
};

let extra_data = reader.read_boxed_slice_exact(len as usize)?;
let extra_data = reader.read_boxed_slice_exact(len)?;

Ok(Dec3Atom { extra_data })
}
Expand Down
4 changes: 1 addition & 3 deletions symphonia-format-isomp4/src/atoms/dovi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ use symphonia_core::io::ReadBytes;
use crate::atoms::stsd::VisualSampleEntry;
use crate::atoms::{Atom, AtomHeader};

const DOVI_CONFIG_SIZE: u64 = 24;

#[allow(dead_code)]
#[derive(Debug)]
pub struct DoviAtom {
Expand All @@ -28,7 +26,7 @@ impl Atom for DoviAtom {
// https://professional.dolby.com/siteassets/content-creation/dolby-vision-for-content-creators/dolby_vision_bitstreams_within_the_iso_base_media_file_format_dec2017.pdf
// It should be 24 bytes
let len = match header.data_len() {
Some(len @ DOVI_CONFIG_SIZE) => len as usize,
Some(len @ 24) => len as usize,
Some(_) => return decode_error("isomp4 (dvcC/dvvC): atom size is not 24 bytes"),
None => return decode_error("isomp4 (dvcC/dvvC): expected atom size to be known"),
};
Expand Down
15 changes: 13 additions & 2 deletions symphonia-format-isomp4/src/atoms/elst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,20 @@ impl Atom for ElstAtom {
fn read<B: ReadBytes>(reader: &mut B, mut header: AtomHeader) -> Result<Self> {
let (version, _) = header.read_extended_header(reader)?;

// TODO: Apply a limit.
// minimum data size is 4 bytes
let len = match header.data_len() {
Some(len) if len >= 4 => len as u32,
Some(_) => return decode_error("isomp4 (elst): atom size is less than 16 bytes"),
None => return decode_error("isomp4 (elst): expected atom size to be known"),
};

let entry_count = reader.read_be_u32()?;
let entry_len = if version == 0 { 12 } else { 20 };
if entry_count != (len - 4) / entry_len {
return decode_error("isomp4 (elst): invalid entry count");
}

// TODO: Apply a limit.
let mut entries = Vec::new();

for _ in 0..entry_count {
Expand All @@ -47,7 +58,7 @@ impl Atom for ElstAtom {
reader.read_be_u64()?,
bits::sign_extend_leq64_to_i64(reader.read_be_u64()?, 64),
),
_ => return decode_error("isomp4: invalid tkhd version"),
_ => return decode_error("isomp4 (elst): invalid version"),
};

let media_rate_int = bits::sign_extend_leq16_to_i16(reader.read_be_u16()?, 16);
Expand Down
69 changes: 40 additions & 29 deletions symphonia-format-isomp4/src/atoms/esds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn read_descriptor_header<B: ReadBytes>(reader: &mut B) -> Result<(u8, u32)> {
#[derive(Debug)]
pub struct EsdsAtom {
/// Elementary stream descriptor.
descriptor: ESDescriptor,
descriptor: Option<ESDescriptor>,
}

impl Atom for EsdsAtom {
Expand Down Expand Up @@ -76,28 +76,30 @@ impl Atom for EsdsAtom {
// Ignore remainder of the atom.
scoped.ignore()?;

Ok(EsdsAtom { descriptor: descriptor.unwrap() })
Ok(EsdsAtom { descriptor })
}
}

impl EsdsAtom {
/// If the elementary stream descriptor describes an audio stream, populate the provided
/// audio sample entry.
pub fn fill_audio_sample_entry(&self, entry: &mut AudioSampleEntry) -> Result<()> {
match get_codec_id_from_object_type(self.descriptor.dec_config.object_type_indication) {
Some(CodecId::Audio(id)) => {
// Object type indication identified an audio codec.
entry.codec_id = id;
}
Some(_) => {
// Object type indication identified a non-audio codec. This is unexpected.
return decode_error("isomp4 (esds): expected an audio codec type");
if let Some(desc) = &self.descriptor {
match get_codec_id_from_object_type(desc.dec_config.object_type_indication) {
Some(CodecId::Audio(id)) => {
// Object type indication identified an audio codec.
entry.codec_id = id;
}
Some(_) => {
// Object type indication identified a non-audio codec. This is unexpected.
return decode_error("isomp4 (esds): expected an audio codec type");
}
None => {}
}
None => {}
}

if let Some(ds_config) = &self.descriptor.dec_config.dec_specific_info {
entry.extra_data = Some(ds_config.extra_data.clone());
if let Some(ds_config) = &desc.dec_config.dec_specific_info {
entry.extra_data = Some(ds_config.extra_data.clone());
}
}

Ok(())
Expand All @@ -106,23 +108,25 @@ impl EsdsAtom {
/// If the elementary stream descriptor describes an video stream, populate the provided
/// video sample entry.
pub fn fill_video_sample_entry(&self, entry: &mut VisualSampleEntry) -> Result<()> {
match get_codec_id_from_object_type(self.descriptor.dec_config.object_type_indication) {
Some(CodecId::Video(id)) => {
// Object type indication identified an video codec.
entry.codec_id = id;
}
Some(_) => {
// Object type indication identified a non-video codec. This is unexpected.
return decode_error("isomp4 (esds): expected a video codec type");
if let Some(desc) = &self.descriptor {
match get_codec_id_from_object_type(desc.dec_config.object_type_indication) {
Some(CodecId::Video(id)) => {
// Object type indication identified an video codec.
entry.codec_id = id;
}
Some(_) => {
// Object type indication identified a non-video codec. This is unexpected.
return decode_error("isomp4 (esds): expected a video codec type");
}
None => {}
}
None => {}
}

if let Some(ds_config) = &self.descriptor.dec_config.dec_specific_info {
entry.extra_data.push(VideoExtraData {
id: VIDEO_EXTRA_DATA_ID_NULL,
data: ds_config.extra_data.clone(),
});
if let Some(ds_config) = &desc.dec_config.dec_specific_info {
entry.extra_data.push(VideoExtraData {
id: VIDEO_EXTRA_DATA_ID_NULL,
data: ds_config.extra_data.clone(),
});
}
}

Ok(())
Expand Down Expand Up @@ -239,6 +243,8 @@ pub struct ESDescriptor {

impl ObjectDescriptor for ESDescriptor {
fn read<B: ReadBytes>(reader: &mut B, len: u32) -> Result<Self> {
let pos = reader.pos();

let es_id = reader.read_be_u16()?;
let es_flags = reader.read_u8()?;

Expand All @@ -261,6 +267,11 @@ impl ObjectDescriptor for ESDescriptor {
let mut dec_config = None;
let mut sl_config = None;

// len should be bigger than what have been read
if reader.pos() - pos > len as u64 {
return decode_error("isomp4: es descriptor len is wrong");
}

let mut scoped = ScopedStream::new(reader, u64::from(len) - 3);

// Multiple descriptors follow, but only the decoder configuration descriptor is useful.
Expand Down
Loading
Loading