-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OSS-Fuzz: OSS-Fuzz fuzzing integration
Signed-off-by: Arthur Chan <[email protected]>
- Loading branch information
1 parent
a1fd011
commit ff2b133
Showing
5 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
target | ||
corpus | ||
artifacts | ||
coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
[package] | ||
name = "tar-fuzz" | ||
version = "0.0.0" | ||
publish = false | ||
edition = "2018" | ||
|
||
[package.metadata] | ||
cargo-fuzz = true | ||
|
||
[dependencies] | ||
libfuzzer-sys = "0.4" | ||
tempfile = "3.3" | ||
|
||
[dependencies.tar] | ||
path = ".." | ||
|
||
[[bin]] | ||
name = "archive" | ||
path = "fuzz_targets/archive.rs" | ||
test = false | ||
doc = false | ||
bench = false | ||
|
||
[[bin]] | ||
name = "builder" | ||
path = "fuzz_targets/builder.rs" | ||
test = false | ||
doc = false | ||
bench = false | ||
|
||
[[bin]] | ||
name = "tar" | ||
path = "fuzz_targets/tar.rs" | ||
test = false | ||
doc = false | ||
bench = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#![no_main] | ||
|
||
use libfuzzer_sys::fuzz_target; | ||
|
||
use std::fs::{File, OpenOptions}; | ||
use std::io::{Cursor, Write, Read}; | ||
use tar::{Archive, Builder, EntryType, Header}; | ||
use tempfile::tempdir; | ||
use std::convert::TryInto; | ||
use std::str; | ||
|
||
fuzz_target!(|data: &[u8]| { | ||
// Skip this iteration when data is not enough | ||
if data.len() < 10 { | ||
return; | ||
} | ||
|
||
// Create temp file and dir | ||
let temp_dir = tempdir().unwrap(); | ||
let file_name = match str::from_utf8(&data[0..data.len().min(10)]) { | ||
Ok(name) => name.to_string(), | ||
Err(_) => "default_file_name".to_string(), | ||
}; | ||
let dir_name = match str::from_utf8(&data[data.len().min(10)..data.len().min(20)]) { | ||
Ok(name) => name.to_string(), | ||
Err(_) => "default_dir_name".to_string(), | ||
}; | ||
let temp_file_path = temp_dir.path().join(format!("{}_file.tar", file_name)); | ||
|
||
// Initialise builder and cursor | ||
let mut builder = Builder::new(Vec::new()); | ||
let mut cursor = Cursor::new(data.to_vec()); | ||
|
||
// Choose an etnry type | ||
let entry_type_byte = data[0]; | ||
let entry_type = match entry_type_byte % 5 { | ||
0 => EntryType::Regular, | ||
1 => EntryType::Directory, | ||
2 => EntryType::Symlink, | ||
3 => EntryType::hard_link(), | ||
_ => EntryType::character_special(), | ||
}; | ||
|
||
// Initilaise header | ||
let mut header = Header::new_gnu(); | ||
let file_size = u64::from_le_bytes( | ||
data.get(1..9) | ||
.unwrap_or(&[0; 8]) | ||
.try_into() | ||
.unwrap_or([0; 8]), | ||
); | ||
header.set_size(file_size); | ||
header.set_entry_type(entry_type); | ||
header.set_cksum(); | ||
|
||
// Prepare sample tar file | ||
let tar_file_path = format!("{}/{}", dir_name, file_name); | ||
let _ = builder.append_data(&mut header, tar_file_path.clone(), &mut cursor).ok(); | ||
cursor.set_position(0); | ||
for i in 1..5 { | ||
let start = i * 10 % data.len(); | ||
let end = std::cmp::min(start + 10, data.len()); | ||
let entry_data = &data[start..end]; | ||
let entry_name = match str::from_utf8(&entry_data) { | ||
Ok(name) => name.to_string(), | ||
Err(_) => format!("entry_{}", i), | ||
}; | ||
|
||
let mut entry_header = Header::new_gnu(); | ||
entry_header.set_size(entry_data.len() as u64); | ||
entry_header.set_entry_type(entry_type); | ||
entry_header.set_cksum(); | ||
|
||
let mut entry_cursor = Cursor::new(entry_data.to_vec()); | ||
let _ = builder.append_data(&mut entry_header, entry_name, &mut entry_cursor).ok(); | ||
} | ||
|
||
// Prepare malformed tar header | ||
if data.len() > 512 { | ||
let corrupt_header_data = &data[data.len() - 512..]; | ||
let corrupt_header = Header::from_byte_slice(corrupt_header_data); | ||
let mut corrupt_cursor = Cursor::new(data.to_vec()); | ||
let corrupt_entry_name = "corrupt_entry.txt"; | ||
let _ = builder.append_data(&mut corrupt_header.clone(), corrupt_entry_name, &mut corrupt_cursor).ok(); | ||
} | ||
|
||
if let Ok(mut tar_file) = File::create(&temp_file_path) { | ||
if let Ok(tar_data) = builder.into_inner() { | ||
let _ = tar_file.write_all(&tar_data); | ||
} | ||
} | ||
|
||
// Fuzz archive and builder unpack with malformed tar archvie | ||
if let Ok(mut tar_file) = OpenOptions::new().read(true).open(&temp_file_path) { | ||
let mut tar_data = Vec::new(); | ||
let _ = tar_file.read_to_end(&mut tar_data); | ||
let mut tar_cursor = Cursor::new(tar_data); | ||
let mut archive = Archive::new(&mut tar_cursor); | ||
let _ = archive.unpack(temp_dir.path()).ok(); | ||
} | ||
|
||
// Fuzz archive and builder | ||
for i in 0..3 { | ||
let name_data = &data[i * 5 % data.len()..(i * 5 + 5) % data.len()]; | ||
let name = match str::from_utf8(name_data) { | ||
Ok(n) => n.to_string(), | ||
Err(_) => format!("random_name_{}", i), | ||
}; | ||
let path = temp_dir.path().join(name); | ||
if i % 2 == 0 { | ||
// Create a file | ||
if let Ok(mut file) = File::create(&path) { | ||
let _ = file.write_all(data); | ||
} | ||
} else { | ||
// Create a directory | ||
let _ = std::fs::create_dir(&path); | ||
} | ||
} | ||
|
||
// Fuzz unpacking | ||
let mut data_cursor = Cursor::new(data.to_vec()); | ||
let mut data_archive = Archive::new(&mut data_cursor); | ||
let _ = data_archive.unpack(temp_dir.path()).ok(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#![no_main] | ||
|
||
use libfuzzer_sys::fuzz_target; | ||
|
||
use std::io::Cursor; | ||
use tar::Builder; | ||
use tempfile::{tempdir, tempfile}; | ||
|
||
fuzz_target!(|data: &[u8]| { | ||
// Initialization | ||
let random_bool = data.first().map(|&b| b % 2 == 0).unwrap_or(false); | ||
let temp_dir = tempdir().expect(""); | ||
|
||
// Create a temporary file for testing | ||
if let Ok(temp_file) = tempfile() { | ||
let mut builder = Builder::new(temp_file); | ||
|
||
// Randomly choose a function target from builder to fuzz | ||
match data.first().map(|&b| b % 8) { | ||
Some(0) => { | ||
builder.mode(if random_bool { tar::HeaderMode::Deterministic } else { tar::HeaderMode::Complete }); | ||
} | ||
Some(1) => { | ||
if let Ok(mut file) = tempfile() { | ||
let _ = builder.append_file("testfile.txt", &mut file); | ||
} | ||
} | ||
Some(2) => { | ||
let _ = builder.append_data(&mut tar::Header::new_old(), "randomfile", Cursor::new(data)); | ||
} | ||
Some(3) => { | ||
if let Ok(mut file) = tempfile() { | ||
let _ = builder.append_data(&mut tar::Header::new_old(), "testwrite.txt", &mut file); | ||
} | ||
} | ||
Some(4) => { | ||
let link_path = temp_dir.path().join("testlink"); | ||
let _ = builder.append_link(&mut tar::Header::new_old(), "testlink.txt", &link_path); | ||
} | ||
Some(5) => { | ||
let _ = builder.append_path(temp_dir.path()); | ||
} | ||
Some(6) => { | ||
let link_path = temp_dir.path().join("testlink_with_path"); | ||
let _ = builder.append_link(&mut tar::Header::new_old(), temp_dir.path(), &link_path); | ||
} | ||
Some(7) => { | ||
let _ = builder.append_dir_all("testdir", temp_dir.path()); | ||
} | ||
_ => {} | ||
} | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#![no_main] | ||
|
||
use libfuzzer_sys::fuzz_target; | ||
|
||
use tar::{Builder, Header, Archive, EntryType}; | ||
use std::io::{Cursor, Read, Write, Seek}; | ||
use tempfile::{tempdir, NamedTempFile}; | ||
|
||
fuzz_target!(|data: &[u8]| { | ||
// Setup temporary directory and path | ||
let temp_dir = tempdir().unwrap(); | ||
let archive_data = Cursor::new(data); | ||
let mut builder = Builder::new(Cursor::new(Vec::new())); | ||
let mut header = Header::new_gnu(); | ||
|
||
// Set header metadata | ||
header.set_size(data.len() as u64); | ||
header.set_cksum(); | ||
header.set_entry_type(EntryType::file()); | ||
|
||
// Append data and a temp file to tar | ||
let _ = builder.append_data(&mut header, "fuzzed/file", archive_data); | ||
let mut temp_file = NamedTempFile::new().unwrap(); | ||
let _ = temp_file.write_all(data); | ||
let _ = builder.append_file("fuzzed/file2", temp_file.as_file_mut()).ok(); | ||
|
||
#[cfg(unix)] | ||
let _ = builder.append_link(&mut header, "symlink/path", "target/path").ok(); | ||
|
||
let _ = builder.finish(); | ||
|
||
// Fuzzing Archive and Entry logic | ||
let mut archive = Archive::new(Cursor::new(data)); | ||
if let Ok(mut entries) = archive.entries() { | ||
while let Some(Ok(mut entry)) = entries.next() { | ||
let _ = entry.path().map(|p| p.to_owned()); | ||
let _ = entry.link_name().map(|l| l.map(|ln| ln.to_owned())); | ||
let _ = entry.size(); | ||
let _ = entry.header(); | ||
let _ = entry.raw_header_position(); | ||
let _ = entry.raw_file_position(); | ||
|
||
match entry.header().entry_type() { | ||
EntryType::Regular => { /* Do nothing */ } | ||
EntryType::Directory => { | ||
let _ = entry.unpack_in(temp_dir.path()).ok(); | ||
} | ||
EntryType::Symlink => { | ||
let _ = entry.unpack_in(temp_dir.path()).ok(); | ||
} | ||
EntryType::Link => { | ||
let _ = entry.unpack_in(temp_dir.path()).ok(); | ||
} | ||
EntryType::Fifo => { /* Do nothing */ } | ||
_ => { /* Do nothing */ } | ||
} | ||
|
||
let mut buffer = Vec::new(); | ||
let _ = entry.read_to_end(&mut buffer).ok(); | ||
entry.set_mask(0o755); | ||
entry.set_unpack_xattrs(true); | ||
entry.set_preserve_permissions(true); | ||
entry.set_preserve_mtime(true); | ||
|
||
// Fuzz unpack | ||
let dst_path = temp_dir.path().join("unpacked_file"); | ||
let _ = entry.unpack(&dst_path).ok(); | ||
let _ = entry.unpack_in(temp_dir.path()).ok(); | ||
|
||
// Fuzz PaxExtensions | ||
if let Ok(Some(pax_extensions)) = entry.pax_extensions() { | ||
for ext in pax_extensions { | ||
let _ = ext.ok(); | ||
} | ||
} | ||
|
||
// Fuzzing file search with tar entry position | ||
if entry.size() > 0 { | ||
let mut data_cursor = Cursor::new(data); | ||
let _ = data_cursor.seek(std::io::SeekFrom::Start(entry.raw_file_position())).ok(); | ||
let _ = data_cursor.read(&mut buffer).ok(); | ||
} | ||
} | ||
} | ||
}); |