From 79aa69acd1f5702d652cdddc5f9852efe093d4b1 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 16 Jan 2026 11:27:34 +1000 Subject: [PATCH 1/5] tdf: decoders: handle read overflow Fix faults when read overflows occur (TDF X length is specified as N, but the decoder knows X has length > N). Raise an error in that case. This is not expected under normal operation, but can occur when the incorrect block size is specified. Signed-off-by: Jordan Yates --- scripts/tdf_decoder.rs.jinja | 9 +++++++++ tdf/src/decoders.rs | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/scripts/tdf_decoder.rs.jinja b/scripts/tdf_decoder.rs.jinja index fee27bf..2c0130f 100644 --- a/scripts/tdf_decoder.rs.jinja +++ b/scripts/tdf_decoder.rs.jinja @@ -72,6 +72,15 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> }; let cursor_end = cursor.position(); let cursor_read = cursor_end - cursor_start; + + if (size as u64) < cursor_read { + // Hande read overflow (more data read than specified) + return Result::Err(Error::new( + ErrorKind::InvalidData, + "Read overflow, corrupt data/metadata", + )); + } + let underflow = size as u64 - cursor_read; // Handle read underflow (more data specified than expected) diff --git a/tdf/src/decoders.rs b/tdf/src/decoders.rs index 82f68e7..d91de03 100644 --- a/tdf/src/decoders.rs +++ b/tdf/src/decoders.rs @@ -642,6 +642,15 @@ pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> }; let cursor_end = cursor.position(); let cursor_read = cursor_end - cursor_start; + + if (size as u64) < cursor_read { + // Hande read overflow (more data read than specified) + return Result::Err(Error::new( + ErrorKind::InvalidData, + "Read overflow, corrupt data/metadata", + )); + } + let underflow = size as u64 - cursor_read; // Handle read underflow (more data specified than expected) From f087a4698fa0d55d6afa12b971af4e5d764d123d Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 16 Jan 2026 10:18:38 +1000 Subject: [PATCH 2/5] blocks: lib: block size from top level Take the block size from the top level run functions, instead of hardcoding it at the lowest levels. Signed-off-by: Jordan Yates --- blocks/src/lib.rs | 2 +- src/lib.rs | 14 ++++++++++---- src/main_cli.rs | 3 ++- src/main_gui.rs | 1 + 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/blocks/src/lib.rs b/blocks/src/lib.rs index 326b423..84c5b36 100644 --- a/blocks/src/lib.rs +++ b/blocks/src/lib.rs @@ -1,7 +1,7 @@ use byteorder::{LittleEndian, ReadBytesExt}; use tdf::TdfOutput; -pub const BLOCK_SIZE: usize = 512; +pub const DEFAULT_BLOCK_SIZE: usize = 512; #[derive(Hash, Copy, Clone, PartialEq, Eq)] pub enum BlockTypes { diff --git a/src/lib.rs b/src/lib.rs index d1288ef..486f5e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,6 +163,7 @@ pub struct DecodeWorkerArgs { pub output_unix_time: bool, pub start_block: usize, pub num_blocks: usize, + pub block_size: usize, } #[derive(Clone)] @@ -189,15 +190,18 @@ pub fn worker_run_decode(mut args: DecodeWorkerArgsReporter // Open file let file = File::open(args.decode_args.input_file.clone()).unwrap(); let mmap = unsafe { Mmap::map(&file).unwrap() }; - let mmap_start = blocks::BLOCK_SIZE * args.decode_args.start_block; + let mmap_start = args.decode_args.block_size * args.decode_args.start_block; let mmap_end = - blocks::BLOCK_SIZE * (args.decode_args.start_block + args.decode_args.num_blocks); + args.decode_args.block_size * (args.decode_args.start_block + args.decode_args.num_blocks); // Range of the file for this worker let mmap_slice = &mmap[mmap_start..mmap_end]; // Iterate over the blocks - for (index, block) in mmap_slice.chunks_exact(blocks::BLOCK_SIZE).enumerate() { + for (index, block) in mmap_slice + .chunks_exact(args.decode_args.block_size) + .enumerate() + { match blocks::decode_block(&mut csv_writer, block) { Ok(block_type) => *block_counter.entry(block_type).or_default() += 1, Err(_) => *block_counter.entry(blocks::BlockTypes::ERROR).or_default() += 1, @@ -237,6 +241,7 @@ pub fn worker_run_decode(mut args: DecodeWorkerArgsReporter pub struct RunArgs { pub device_id: u64, + pub block_size: usize, pub input_files: Vec, pub output_folder: PathBuf, pub output_prefix: String, @@ -275,7 +280,7 @@ pub fn run( (f, s) }; - let num_blocks = size / blocks::BLOCK_SIZE; + let num_blocks = size / args.block_size; let max_workers = (num_blocks / 100) + 1; let num_workers = std::cmp::min(max_workers, num_cpus::get()); let blocks_per_worker = num_blocks / num_workers; @@ -299,6 +304,7 @@ pub fn run( output_unix_time: args.output_unix_time, start_block: idx * blocks_per_worker, num_blocks: num, + block_size: args.block_size, }, block_stats: stats_block.clone(), tdf_stats: stats_tdf.clone(), diff --git a/src/main_cli.rs b/src/main_cli.rs index 06c5e50..2caf677 100644 --- a/src/main_cli.rs +++ b/src/main_cli.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; #[macro_use] extern crate prettytable; -use prettytable::{format, Table}; +use prettytable::{Table, format}; #[derive(Clone)] pub struct IndicatifProgress { @@ -101,6 +101,7 @@ fn main() -> io::Result<()> { let mut run_args = infuse_decoder::RunArgs { device_id: *device_id, + block_size: blocks::DEFAULT_BLOCK_SIZE, input_files: files.clone(), output_folder: args.output.clone(), output_prefix: output_prefix, diff --git a/src/main_gui.rs b/src/main_gui.rs index 85c3bbc..78b3868 100644 --- a/src/main_gui.rs +++ b/src/main_gui.rs @@ -283,6 +283,7 @@ fn start_button(app: &mut MyApp, _ctx: &egui::Context, ui: &mut egui::Ui) { let mut run_args = infuse_decoder::RunArgs { device_id: app.device_id, + block_size: blocks::DEFAULT_BLOCK_SIZE, input_files: files, output_folder: app.output_folder.clone(), output_prefix: app.output_prefix.clone(), From 5403b5d750a9bdfb3378f3199c3a4b1298c753ee Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 16 Jan 2026 10:43:33 +1000 Subject: [PATCH 3/5] main_cli: configurable block size from command line Make the block size configurable from the command line, defaulting to the standard value of 512. Signed-off-by: Jordan Yates --- src/args.rs | 20 ++++++++++++++++++++ src/lib.rs | 1 + src/main_cli.rs | 4 +++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/args.rs diff --git a/src/args.rs b/src/args.rs new file mode 100644 index 0000000..be779ae --- /dev/null +++ b/src/args.rs @@ -0,0 +1,20 @@ +use clap::ValueEnum; +use std::fmt; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)] +#[repr(usize)] +pub enum BlockSizeOptions { + #[value(name = "512")] + B512 = 512, + #[value(name = "4096")] + B4096 = 4096, +} + +impl fmt::Display for BlockSizeOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BlockSizeOptions::B512 => write!(f, "512"), + BlockSizeOptions::B4096 => write!(f, "4096"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 486f5e1..cf77b0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ use std::thread; use tdf::TdfOutput; +pub mod args; pub mod fs_util; pub trait ProgressReporter { diff --git a/src/main_cli.rs b/src/main_cli.rs index 2caf677..c9a7620 100644 --- a/src/main_cli.rs +++ b/src/main_cli.rs @@ -59,6 +59,8 @@ struct Cli { /// Verbose CLI output #[arg(short, long)] verbose: bool, + #[arg(long, default_value_t = infuse_decoder::args::BlockSizeOptions::B512)] + block_size: infuse_decoder::args::BlockSizeOptions, } fn main() -> io::Result<()> { @@ -101,7 +103,7 @@ fn main() -> io::Result<()> { let mut run_args = infuse_decoder::RunArgs { device_id: *device_id, - block_size: blocks::DEFAULT_BLOCK_SIZE, + block_size: args.block_size as usize, input_files: files.clone(), output_folder: args.output.clone(), output_prefix: output_prefix, From 2e50e121755a8be00e097deac8d0faff57bc145d Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 16 Jan 2026 11:26:48 +1000 Subject: [PATCH 4/5] main_gui: configurable block size from GUI Add a dropdown menu for selecting the appropriate block size, defaulting to the most common value (512). Signed-off-by: Jordan Yates --- src/main_gui.rs | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/main_gui.rs b/src/main_gui.rs index 78b3868..6ba27db 100644 --- a/src/main_gui.rs +++ b/src/main_gui.rs @@ -10,6 +10,8 @@ use egui_extras::{Column, TableBuilder}; use image::GenericImageView; use rfd::FileDialog; +use infuse_decoder::args::BlockSizeOptions; + #[derive(PartialEq)] enum TimeOutput { UNIX, @@ -77,6 +79,7 @@ impl infuse_decoder::ProgressReporter for SliderState { struct MyApp { time_mode: TimeOutput, device_id: u64, + block_size: BlockSizeOptions, input_path: Option, input_files: Option>>, output_folder: PathBuf, @@ -121,6 +124,7 @@ impl Default for MyApp { Self { time_mode: TimeOutput::UTC, device_id: 0, + block_size: BlockSizeOptions::B512, input_path: None, input_files: None, output_folder: default_out.unwrap(), @@ -236,18 +240,31 @@ fn core_options(app: &mut MyApp, _ctx: &egui::Context, ui: &mut egui::Ui) { } fn decode_options(app: &mut MyApp, _ctx: &egui::Context, ui: &mut egui::Ui) { - ui.vertical(|ui| { - ui.label("Time Output Format"); - ui.radio_value( - &mut app.time_mode, - TimeOutput::UTC, - "UTC (2020-01-01T00:00:00.000000Z)", - ); - ui.radio_value( - &mut app.time_mode, - TimeOutput::UNIX, - "UNIX (1577800800.000000)", - ); + ui.horizontal(|ui| { + ui.vertical(|ui| { + ui.label("Time Output Format"); + ui.radio_value( + &mut app.time_mode, + TimeOutput::UTC, + "UTC (2020-01-01T00:00:00.000000Z)", + ); + ui.radio_value( + &mut app.time_mode, + TimeOutput::UNIX, + "UNIX (1577800800.000000)", + ); + }); + ui.separator(); + ui.vertical(|ui| { + ui.label("Block Size"); + egui::ComboBox::from_id_salt("Block Size") + .selected_text(format!("{:}", app.block_size)) + .show_ui(ui, |ui| { + ui.selectable_value(&mut app.block_size, BlockSizeOptions::B512, "512"); + ui.selectable_value(&mut app.block_size, BlockSizeOptions::B4096, "4096"); + }); + }); + ui.separator(); }); } @@ -283,7 +300,7 @@ fn start_button(app: &mut MyApp, _ctx: &egui::Context, ui: &mut egui::Ui) { let mut run_args = infuse_decoder::RunArgs { device_id: app.device_id, - block_size: blocks::DEFAULT_BLOCK_SIZE, + block_size: app.block_size as usize, input_files: files, output_folder: app.output_folder.clone(), output_prefix: app.output_prefix.clone(), From b43f8ad880311ac4a565f5ef0d66e03fd21d54bd Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 16 Jan 2026 11:34:41 +1000 Subject: [PATCH 5/5] Cargo.toml: `v1.6.0` - Fix GUI crash on certain types of invalid data - Block size for decoding can now be configured Signed-off-by: Jordan Yates --- CHANGELOG.md | 5 +++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f051359..1025d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org). +## [1.6.0] - 2026-01-16 + + - Fix GUI crash on certain types of invalid data + - Block size for decoding can now be configured + ## [1.5.0] - 2025-12-18 - Fix bug that caused a variable number of blocks at the end of a file to not be decoded diff --git a/Cargo.lock b/Cargo.lock index dade723..8c1780e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2082,7 +2082,7 @@ dependencies = [ [[package]] name = "infuse_decoder" -version = "1.5.0" +version = "1.6.0" dependencies = [ "blocks", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index aff99ec..5186b68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "infuse_decoder" -version = "1.5.0" +version = "1.6.0" edition = "2024" [[bin]]