Coverage Report

Created: 2026-06-28 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tar-rs/fuzz/fuzz_targets/tar.rs
Line
Count
Source
1
// Copyright 2024 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#![no_main]
16
17
use arbitrary::{Arbitrary, Unstructured};
18
use libfuzzer_sys::fuzz_target;
19
use std::io::{Cursor, Read, Seek, Write};
20
use tar::{Archive, Builder, EntryType, Header};
21
use tempfile::{tempdir, NamedTempFile};
22
23
// Define FuzzInput for arbitrary crate
24
#[derive(Debug)]
25
struct FuzzInput {
26
    data: Vec<u8>,
27
    file_name: String,
28
    link_path: String,
29
    target_path: String,
30
    entry_type: u8,
31
    metadata_size: u64,
32
}
33
34
// Implement Arbitrary for FuzzInput
35
impl<'a> Arbitrary<'a> for FuzzInput {
36
2.81k
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
37
        Ok(FuzzInput {
38
2.81k
            data: u.arbitrary()?,
39
2.81k
            file_name: u.arbitrary::<&str>()?.to_string(),
40
2.81k
            link_path: u.arbitrary::<&str>()?.to_string(),
41
2.81k
            target_path: u.arbitrary::<&str>()?.to_string(),
42
2.81k
            entry_type: u.arbitrary()?,
43
2.81k
            metadata_size: u.int_in_range(0..=1000)?,
44
        })
45
2.81k
    }
46
}
47
48
fuzz_target!(|data: &[u8]| {
49
    // Prepare FuzzInput by Arbitrary crate
50
    let mut unstructured = Unstructured::new(data);
51
    let input: FuzzInput = match FuzzInput::arbitrary(&mut unstructured) {
52
        Ok(val) => val,
53
        Err(_) => return,
54
    };
55
56
    // Setup temporary directory and initialize builder
57
    let temp_dir = match tempdir() {
58
        Ok(dir) => dir,
59
        Err(_) => return,
60
    };
61
    let archive_data = Cursor::new(&input.data);
62
    let mut builder = Builder::new(Cursor::new(Vec::new()));
63
    let mut header = Header::new_gnu();
64
65
    // Set random header metadata
66
    header.set_size(input.metadata_size.min(input.data.len() as u64));
67
    header.set_cksum();
68
    let entry_type = match input.entry_type % 5 {
69
        0 => EntryType::Regular,
70
        1 => EntryType::Directory,
71
        2 => EntryType::Symlink,
72
        3 => EntryType::Link,
73
        _ => EntryType::Fifo,
74
    };
75
    header.set_entry_type(entry_type);
76
77
    // Append data
78
    let _ = builder.append_data(&mut header, &input.file_name, archive_data);
79
    if let Ok(mut temp_file) = NamedTempFile::new() {
80
        let _ = temp_file.write_all(&input.data);
81
        let _ = builder.append_file("fuzzed/file2", temp_file.as_file_mut()).ok();
82
    }
83
84
    #[cfg(unix)]
85
    let _ = builder.append_link(&mut header, &input.link_path, &input.target_path).ok();
86
    let _ = builder.finish();
87
88
    // Fuzzing Archive and Entry logic
89
    let mut archive = Archive::new(Cursor::new(&input.data));
90
    if let Ok(mut entries) = archive.entries() {
91
        while let Some(Ok(mut entry)) = entries.next() {
92
13.2k
            let _ = entry.path().map(|p| p.to_owned());
93
13.2k
            let _ = entry.link_name().map(|l| l.map(|ln| ln.to_owned()));
94
            let _ = entry.size();
95
            let _ = entry.header();
96
            let _ = entry.raw_header_position();
97
            let _ = entry.raw_file_position();
98
99
            // Randomly choose entry actions based on entry type
100
            match entry.header().entry_type() {
101
                EntryType::Regular => { /* Do nothing */ }
102
                EntryType::Directory | EntryType::Symlink | EntryType::Link => {
103
                    let _ = entry.unpack_in(temp_dir.path()).ok();
104
                }
105
                EntryType::Fifo => { /* Do nothing */ }
106
                _ => { /* Do nothing */ }
107
            }
108
109
            // Randomly read contents and adjust permissions and attributes
110
            let mut buffer = Vec::new();
111
            let _ = entry.read_to_end(&mut buffer).ok();
112
            entry.set_mask(0o755);
113
            entry.set_unpack_xattrs(true);
114
            entry.set_preserve_permissions(true);
115
            entry.set_preserve_mtime(true);
116
117
            // Fuzz unpack to randomized destination path
118
            let dst_path = temp_dir.path().join(&input.file_name);
119
            let _ = entry.unpack(&dst_path).ok();
120
            let _ = entry.unpack_in(temp_dir.path()).ok();
121
122
            // Fuzz PaxExtensions
123
            if let Ok(Some(pax_extensions)) = entry.pax_extensions() {
124
                for ext in pax_extensions {
125
                    let _ = ext.ok();
126
                }
127
            }
128
129
            // Randomized file search with tar entry position
130
            if entry.size() > 0 {
131
                let mut data_cursor = Cursor::new(&input.data);
132
                let _ = data_cursor.seek(std::io::SeekFrom::Start(entry.raw_file_position())).ok();
133
                let _ = data_cursor.read(&mut buffer).ok();
134
            }
135
        }
136
    }
137
});