Coverage Report

Created: 2026-07-16 07:04

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
42
    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
64
    int cryptKeyslotDestroy(struct crypt_device*, int keyslot) override { return destroy_val; }
52
27
    int cryptKeySlotMax(const char*) override { return max_slots_val; }
53
83
    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
748
    FuzzerFd(const uint8_t* data, size_t size) : data_(data), size_(size), offset_(0) {}
61
62
14.7k
    std::span<const std::byte> write(std::span<const std::byte> data) override {
63
14.7k
        return data;
64
14.7k
    }
65
66
5.92k
    std::span<std::byte> read(std::span<std::byte> buf) override {
67
5.92k
        if (offset_ >= size_) {
68
4.73k
            return buf.subspan(0, 0); // EOF
69
4.73k
        }
70
1.18k
        size_t to_read = std::min(buf.size(), size_  - offset_);
71
1.18k
        std::memcpy(buf.data(), data_ + offset_, to_read);
72
1.18k
        offset_ += to_read;
73
1.18k
        return buf.subspan(0, to_read);
74
5.92k
    }
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
42
int crypt_init(struct crypt_device** cd, const char*) {
93
42
    *cd = reinterpret_cast<struct crypt_device*>(1); // dummy non-null
94
42
    return 0;
95
42
}
96
42
void crypt_free(struct crypt_device*) {
97
    // do nothing
98
42
}
99
}
100
101
490
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
102
490
    if (size < 1) return 0;
103
104
490
    uint8_t action = data[0];
105
490
    const uint8_t* fuzz_data = data + 1;
106
490
    size_t fuzz_size = size - 1;
107
108
490
    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
430
    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
320
    else if (action % 4 == 2) {
150
        // Fuzz Zero
151
274
        if (fuzz_size < 8) return 0;
152
269
        uint64_t drive_size = *reinterpret_cast<const uint64_t*>(fuzz_data) % (1024 * 1024); // cap at 1MB
153
269
        fuzz_data += 8;
154
269
        fuzz_size -= 8;
155
156
269
        estoraged::Zero zero("/dev/null");
157
269
        FuzzerFd fd(fuzz_data, fuzz_size);
158
269
        try {
159
269
            zero.writeZero(drive_size, fd);
160
269
        } catch (...) {}
161
162
269
        FuzzerFd fd2(fuzz_data, fuzz_size);
163
269
        try {
164
269
            zero.verifyZero(drive_size, fd2);
165
269
        } catch (...) {}
166
269
    }
167
46
    else if (action % 4 == 3) {
168
        // Fuzz CryptErase
169
46
        if (fuzz_size < 4) return 0;
170
42
        std::unique_ptr<StubCryptsetup> stub = std::make_unique<StubCryptsetup>();
171
42
        stub->load_val = fuzz_data[0] % 2 == 0 ? 0 : -1;
172
42
        stub->max_slots_val = (fuzz_data[1] % 10) - 1;
173
42
        stub->destroy_val = fuzz_data[2] % 2 == 0 ? 0 : -1;
174
        
175
42
        uint8_t status_byte = fuzz_data[3] % 3;
176
42
        if (status_byte == 0) stub->slot_status_val = CRYPT_SLOT_ACTIVE;
177
19
        else if (status_byte == 1) stub->slot_status_val = CRYPT_SLOT_ACTIVE_LAST;
178
7
        else stub->slot_status_val = CRYPT_SLOT_INACTIVE;
179
180
42
        estoraged::CryptErase cryptErase("/dev/null", std::move(stub));
181
42
        try {
182
42
            cryptErase.doErase();
183
42
        } catch (...) {}
184
42
    }
185
186
476
    return 0;
187
490
}