Coverage Report

Created: 2026-07-25 06:48

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