Coverage Report

Created: 2026-10-16 07:02

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