Coverage Report

Created: 2026-09-14 06:23

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
44
    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
103
    int cryptKeyslotDestroy(struct crypt_device*, int keyslot) override { return destroy_val; }
52
40
    int cryptKeySlotMax(const char*) override { return max_slots_val; }
53
138
    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
752
    FuzzerFd(const uint8_t* data, size_t size) : data_(data), size_(size), offset_(0) {}
61
62
16.3k
    std::span<const std::byte> write(std::span<const std::byte> data) override {
63
16.3k
        return data;
64
16.3k
    }
65
66
6.11k
    std::span<std::byte> read(std::span<std::byte> buf) override {
67
6.11k
        if (offset_ >= size_) {
68
4.87k
            return buf.subspan(0, 0); // EOF
69
4.87k
        }
70
1.24k
        size_t to_read = std::min(buf.size(), size_  - offset_);
71
1.24k
        std::memcpy(buf.data(), data_ + offset_, to_read);
72
1.24k
        offset_ += to_read;
73
1.24k
        return buf.subspan(0, to_read);
74
6.11k
    }
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
44
int crypt_init(struct crypt_device** cd, const char*) {
93
44
    *cd = reinterpret_cast<struct crypt_device*>(1); // dummy non-null
94
44
    return 0;
95
44
}
96
44
void crypt_free(struct crypt_device*) {
97
    // do nothing
98
44
}
99
}
100
101
494
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
102
494
    if (size < 1) return 0;
103
104
494
    uint8_t action = data[0];
105
494
    const uint8_t* fuzz_data = data + 1;
106
494
    size_t fuzz_size = size - 1;
107
108
494
    if (action % 4 == 0) {
109
        // Fuzz findPredictedMediaLifeLeftPercent
110
60
        char temp_path[] = "/tmp/estoraged_fuzz_XXXXXX";
111
60
        int fd = mkstemp(temp_path);
112
60
        if (fd == -1) return 0;
113
60
        close(fd);
114
115
60
        std::string dir_path = std::string(temp_path) + "_dir";
116
60
        std::filesystem::create_directory(dir_path);
117
60
        std::string file_path = dir_path + "/life_time";
118
119
60
        std::ofstream out(file_path, std::ios::binary);
120
60
        if (out) {
121
60
            out.write(reinterpret_cast<const char*>(fuzz_data), fuzz_size);
122
60
            out.close();
123
60
            try {
124
60
                (void)estoraged::util::findPredictedMediaLifeLeftPercent(dir_path);
125
60
            } catch (...) {}
126
60
        }
127
128
60
        std::filesystem::remove_all(dir_path);
129
60
        unlink(temp_path);
130
60
    }
131
434
    else if (action % 4 == 1) {
132
        // Fuzz Pattern
133
107
        if (fuzz_size < 8) return 0;
134
102
        uint64_t drive_size = *reinterpret_cast<const uint64_t*>(fuzz_data) % (1024 * 1024); // cap at 1MB
135
102
        fuzz_data += 8;
136
102
        fuzz_size -= 8;
137
138
102
        estoraged::Pattern pattern("/dev/null");
139
102
        FuzzerFd fd(fuzz_data, fuzz_size);
140
102
        try {
141
102
            pattern.writePattern(drive_size, fd);
142
102
        } catch (...) {}
143
        
144
102
        FuzzerFd fd2(fuzz_data, fuzz_size);
145
102
        try {
146
102
            pattern.verifyPattern(drive_size, fd2);
147
102
        } catch (...) {}
148
102
    }
149
327
    else if (action % 4 == 2) {
150
        // Fuzz Zero
151
279
        if (fuzz_size < 8) return 0;
152
274
        uint64_t drive_size = *reinterpret_cast<const uint64_t*>(fuzz_data) % (1024 * 1024); // cap at 1MB
153
274
        fuzz_data += 8;
154
274
        fuzz_size -= 8;
155
156
274
        estoraged::Zero zero("/dev/null");
157
274
        FuzzerFd fd(fuzz_data, fuzz_size);
158
274
        try {
159
274
            zero.writeZero(drive_size, fd);
160
274
        } catch (...) {}
161
162
274
        FuzzerFd fd2(fuzz_data, fuzz_size);
163
274
        try {
164
274
            zero.verifyZero(drive_size, fd2);
165
274
        } catch (...) {}
166
274
    }
167
48
    else if (action % 4 == 3) {
168
        // Fuzz CryptErase
169
48
        if (fuzz_size < 4) return 0;
170
44
        std::unique_ptr<StubCryptsetup> stub = std::make_unique<StubCryptsetup>();
171
44
        stub->load_val = fuzz_data[0] % 2 == 0 ? 0 : -1;
172
44
        stub->max_slots_val = (fuzz_data[1] % 10) - 1;
173
44
        stub->destroy_val = fuzz_data[2] % 2 == 0 ? 0 : -1;
174
        
175
44
        uint8_t status_byte = fuzz_data[3] % 3;
176
44
        if (status_byte == 0) stub->slot_status_val = CRYPT_SLOT_ACTIVE;
177
21
        else if (status_byte == 1) stub->slot_status_val = CRYPT_SLOT_ACTIVE_LAST;
178
10
        else stub->slot_status_val = CRYPT_SLOT_INACTIVE;
179
180
44
        estoraged::CryptErase cryptErase("/dev/null", std::move(stub));
181
44
        try {
182
44
            cryptErase.doErase();
183
44
        } catch (...) {}
184
44
    }
185
186
480
    return 0;
187
494
}