Coverage Report

Created: 2026-08-13 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/estoraged_fuzzer.cc
Line
Count
Source
1
// Copyright 2026 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
////////////////////////////////////////////////////////////////////////////////
16
17
#include <cstdint>
18
#include <cstddef>
19
#include <string>
20
#include <vector>
21
#include <fstream>
22
#include <unistd.h>
23
#include <cstring>
24
#include <algorithm>
25
#include <filesystem>
26
27
#include "pattern.hpp"
28
#include "zero.hpp"
29
#include "cryptErase.hpp"
30
#include "util.hpp"
31
32
// Implement StubCryptsetup
33
class StubCryptsetup : public estoraged::CryptsetupInterface {
34
public:
35
    int format_val = 0;
36
    int keyslot_add_val = 0;
37
    int load_val = 0;
38
    int keyslot_change_val = 0;
39
    int activate_val = 0;
40
    int deactivate_val = 0;
41
    int destroy_val = 0;
42
    int max_slots_val = 8;
43
    crypt_keyslot_info slot_status_val = CRYPT_SLOT_ACTIVE;
44
45
0
    int cryptFormat(struct crypt_device*, const char*, const char*, const char*, const char*, const char*, size_t, void*) override { return format_val; }
46
0
    int cryptKeyslotAddByVolumeKey(struct crypt_device*, int, const char*, size_t, const char*, size_t) override { return keyslot_add_val; }
47
47
    int cryptLoad(struct crypt_device*, const char*, void*) override { return load_val; }
48
0
    int cryptKeyslotChangeByPassphrase(struct crypt_device*, int, int, const char*, size_t, const char*, size_t) override { return keyslot_change_val; }
49
0
    int cryptActivateByPassphrase(struct crypt_device*, const char*, int, const char*, size_t, uint32_t) override { return activate_val; }
50
0
    int cryptDeactivate(struct crypt_device*, const char*) override { return deactivate_val; }
51
98
    int cryptKeyslotDestroy(struct crypt_device*, int keyslot) override { return destroy_val; }
52
35
    int cryptKeySlotMax(const char*) override { return max_slots_val; }
53
119
    crypt_keyslot_info cryptKeySlotStatus(struct crypt_device*, int) override { return slot_status_val; }
54
0
    std::string cryptGetDir() override { return "/tmp"; }
55
};
56
57
// Implement a simple Fd that reads from fuzzer input
58
class FuzzerFd : public stdplus::fd::Fd {
59
public:
60
746
    FuzzerFd(const uint8_t* data, size_t size) : data_(data), size_(size), offset_(0) {}
61
62
13.7k
    std::span<const std::byte> write(std::span<const std::byte> data) override {
63
13.7k
        return data;
64
13.7k
    }
65
66
5.70k
    std::span<std::byte> read(std::span<std::byte> buf) override {
67
5.70k
        if (offset_ >= size_) {
68
4.45k
            return buf.subspan(0, 0); // EOF
69
4.45k
        }
70
1.25k
        size_t to_read = std::min(buf.size(), size_  - offset_);
71
1.25k
        std::memcpy(buf.data(), data_ + offset_, to_read);
72
1.25k
        offset_ += to_read;
73
1.25k
        return buf.subspan(0, to_read);
74
5.70k
    }
75
76
0
    int ioctl(unsigned long request, void* data) override {
77
0
        if (request == BLKGETSIZE64) {
78
0
            *reinterpret_cast<uint64_t*>(data) = 1024 * 1024; // 1MB
79
0
        }
80
0
        return 0;
81
0
    }
82
83
private:
84
    const uint8_t* data_;
85
    size_t size_;
86
    size_t offset_;
87
};
88
89
// Stub the C functions that CryptHandle calls to avoid real I/O.
90
extern "C" {
91
struct crypt_device;
92
47
int crypt_init(struct crypt_device** cd, const char*) {
93
47
    *cd = reinterpret_cast<struct crypt_device*>(1); // dummy non-null
94
47
    return 0;
95
47
}
96
47
void crypt_free(struct crypt_device*) {
97
    // do nothing
98
47
}
99
}
100
101
491
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
102
491
    if (size < 1) return 0;
103
104
491
    uint8_t action = data[0];
105
491
    const uint8_t* fuzz_data = data + 1;
106
491
    size_t fuzz_size = size - 1;
107
108
491
    if (action % 4 == 0) {
109
        // Fuzz findPredictedMediaLifeLeftPercent
110
57
        char temp_path[] = "/tmp/estoraged_fuzz_XXXXXX";
111
57
        int fd = mkstemp(temp_path);
112
57
        if (fd == -1) return 0;
113
57
        close(fd);
114
115
57
        std::string dir_path = std::string(temp_path) + "_dir";
116
57
        std::filesystem::create_directory(dir_path);
117
57
        std::string file_path = dir_path + "/life_time";
118
119
57
        std::ofstream out(file_path, std::ios::binary);
120
57
        if (out) {
121
57
            out.write(reinterpret_cast<const char*>(fuzz_data), fuzz_size);
122
57
            out.close();
123
57
            try {
124
57
                (void)estoraged::util::findPredictedMediaLifeLeftPercent(dir_path);
125
57
            } catch (...) {}
126
57
        }
127
128
57
        std::filesystem::remove_all(dir_path);
129
57
        unlink(temp_path);
130
57
    }
131
434
    else if (action % 4 == 1) {
132
        // Fuzz Pattern
133
105
        if (fuzz_size < 8) return 0;
134
100
        uint64_t drive_size = *reinterpret_cast<const uint64_t*>(fuzz_data) % (1024 * 1024); // cap at 1MB
135
100
        fuzz_data += 8;
136
100
        fuzz_size -= 8;
137
138
100
        estoraged::Pattern pattern("/dev/null");
139
100
        FuzzerFd fd(fuzz_data, fuzz_size);
140
100
        try {
141
100
            pattern.writePattern(drive_size, fd);
142
100
        } catch (...) {}
143
        
144
100
        FuzzerFd fd2(fuzz_data, fuzz_size);
145
100
        try {
146
100
            pattern.verifyPattern(drive_size, fd2);
147
100
        } catch (...) {}
148
100
    }
149
329
    else if (action % 4 == 2) {
150
        // Fuzz Zero
151
278
        if (fuzz_size < 8) return 0;
152
273
        uint64_t drive_size = *reinterpret_cast<const uint64_t*>(fuzz_data) % (1024 * 1024); // cap at 1MB
153
273
        fuzz_data += 8;
154
273
        fuzz_size -= 8;
155
156
273
        estoraged::Zero zero("/dev/null");
157
273
        FuzzerFd fd(fuzz_data, fuzz_size);
158
273
        try {
159
273
            zero.writeZero(drive_size, fd);
160
273
        } catch (...) {}
161
162
273
        FuzzerFd fd2(fuzz_data, fuzz_size);
163
273
        try {
164
273
            zero.verifyZero(drive_size, fd2);
165
273
        } catch (...) {}
166
273
    }
167
51
    else if (action % 4 == 3) {
168
        // Fuzz CryptErase
169
51
        if (fuzz_size < 4) return 0;
170
47
        std::unique_ptr<StubCryptsetup> stub = std::make_unique<StubCryptsetup>();
171
47
        stub->load_val = fuzz_data[0] % 2 == 0 ? 0 : -1;
172
47
        stub->max_slots_val = (fuzz_data[1] % 10) - 1;
173
47
        stub->destroy_val = fuzz_data[2] % 2 == 0 ? 0 : -1;
174
        
175
47
        uint8_t status_byte = fuzz_data[3] % 3;
176
47
        if (status_byte == 0) stub->slot_status_val = CRYPT_SLOT_ACTIVE;
177
27
        else if (status_byte == 1) stub->slot_status_val = CRYPT_SLOT_ACTIVE_LAST;
178
14
        else stub->slot_status_val = CRYPT_SLOT_INACTIVE;
179
180
47
        estoraged::CryptErase cryptErase("/dev/null", std::move(stub));
181
47
        try {
182
47
            cryptErase.doErase();
183
47
        } catch (...) {}
184
47
    }
185
186
477
    return 0;
187
491
}