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

cargo fuzz harness and fixes for bugs found #51

Merged
merged 2 commits into from
Jul 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ default = ["ahash"]
merging = []
reordering = []
async = []
arbitrary = ["arbitrary/derive"]

[dependencies]
arbitrary = { version = "1.1.3", optional = true }
ahash = { version = "0.7.6", optional = true }
log = { version = "0.4.16", optional = true }

Expand Down
4 changes: 4 additions & 0 deletions fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
35 changes: 35 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "tobj-fuzz"
version = "0.0.0"
publish = false
edition = "2018"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"
ahash = "*"

[dependencies.tobj]
path = ".."
features = ["arbitrary"]

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[[bin]]
name = "load_obj"
path = "fuzz_targets/load_obj.rs"
test = false
doc = false

[[bin]]
name = "load_mtl"
path = "fuzz_targets/load_mtl.rs"
test = false
doc = false

[profile.release]
debug = true
7 changes: 7 additions & 0 deletions fuzz/fuzz_targets/load_mtl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
let mut cursor = std::io::Cursor::new(data);
let _ = tobj::load_mtl_buf(&mut cursor);
});
28 changes: 28 additions & 0 deletions fuzz/fuzz_targets/load_obj.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#![no_main]
use std::{io::Cursor, os::unix::prelude::OsStrExt, path::Path};

use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: (&[u8], [&[u8]; 4], tobj::LoadOptions)| {
let (obj, mtls, options) = data;

let mtl_loader = move |path: &Path| {
let index: Option<usize> = path
.as_os_str()
.as_bytes()
.first()
.map(|c| (*c & 0b11).into());

if let Some(index) = index {
// load an mtl from one of the mtl bufs
let mut cursor = Cursor::new(mtls[index]);
tobj::load_mtl_buf(&mut cursor)
} else {
// path was empty, just give a default Ok
Ok((Vec::new(), ahash::AHashMap::new()))
}
};

let mut cursor = Cursor::new(obj);
let _ = tobj::load_obj_buf(&mut cursor, &options, mtl_loader);
});
22 changes: 13 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ impl Default for Mesh {
/// * [`GPU_LOAD_OPTIONS`] – if you display meshes on the GPU/in realtime.
///
/// * [`OFFLINE_RENDERING_LOAD_OPTIONS`] – if you're rendering meshes with e.g. an offline path tracer or the like.
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, Copy)]
pub struct LoadOptions {
/// Merge identical positions.
Expand Down Expand Up @@ -651,6 +652,7 @@ pub enum LoadError {
FaceParseError,
MaterialParseError,
InvalidObjectName,
InvalidPolygon,
FaceVertexOutOfBounds,
FaceTexCoordOutOfBounds,
FaceNormalOutOfBounds,
Expand All @@ -671,6 +673,7 @@ impl fmt::Display for LoadError {
LoadError::FaceParseError => "face parse error",
LoadError::MaterialParseError => "material parse error",
LoadError::InvalidObjectName => "invalid object name",
LoadError::InvalidPolygon => "invalid polygon",
LoadError::FaceVertexOutOfBounds => "face vertex index out of bounds",
LoadError::FaceTexCoordOutOfBounds => "face texcoord index out of bounds",
LoadError::FaceNormalOutOfBounds => "face normal index out of bounds",
Expand Down Expand Up @@ -730,12 +733,12 @@ impl VertexIndices {
match isize::from_str(i.1) {
Ok(x) => {
// Handle relative indices
indices[i.0] = if x < 0 {
*indices.get_mut(i.0)? = if x < 0 {
match i.0 {
0 => (pos_sz as isize + x) as _,
1 => (tex_sz as isize + x) as _,
2 => (norm_sz as isize + x) as _,
_ => panic!("Invalid number of elements for a face (> 3)!"),
_ => return None, // Invalid number of elements for a face
}
} else {
(x - 1) as _
Expand Down Expand Up @@ -835,7 +838,7 @@ fn add_vertex(
Some(&i) => mesh.indices.push(i),
None => {
let v = vert.v as usize;
if v * 3 + 2 >= pos.len() {
if v.saturating_mul(3).saturating_add(2) >= pos.len() {
return Err(LoadError::FaceVertexOutOfBounds);
}
// Add the vertex to the mesh
Expand Down Expand Up @@ -946,8 +949,8 @@ fn export_faces(
}
Face::Polygon(ref indices) => {
if load_options.triangulate {
let a = &indices[0];
let mut b = &indices[1];
let a = indices.get(0).ok_or(LoadError::InvalidPolygon)?;
let mut b = indices.get(1).ok_or(LoadError::InvalidPolygon)?;
for c in indices.iter().skip(2) {
add_vertex(&mut mesh, &mut index_map, a, pos, v_color, texcoord, normal)?;
add_vertex(&mut mesh, &mut index_map, b, pos, v_color, texcoord, normal)?;
Expand Down Expand Up @@ -994,7 +997,7 @@ fn add_vertex_multi_index(
None => {
let vertex = vert.v as usize;

if vertex * 3 + 2 >= pos.len() {
if vertex.saturating_mul(3).saturating_add(2) >= pos.len() {
return Err(LoadError::FaceVertexOutOfBounds);
}

Expand Down Expand Up @@ -1342,8 +1345,8 @@ fn export_faces_multi_index(
}
Face::Polygon(ref indices) => {
if load_options.triangulate {
let a = &indices[0];
let mut b = &indices[1];
let a = indices.get(0).ok_or(LoadError::InvalidPolygon)?;
let mut b = indices.get(1).ok_or(LoadError::InvalidPolygon)?;
for c in indices.iter().skip(2) {
add_vertex_multi_index(
&mut mesh,
Expand Down Expand Up @@ -1749,7 +1752,8 @@ where
));
tmp_faces.clear();
}
name = line[1..].trim().to_owned();
let size = line.chars().nth(0).unwrap().len_utf8();
name = line[size..].trim().to_owned();
if name.is_empty() {
name = "unnamed_object".to_owned();
}
Expand Down