Coverage Report

Created: 2025-12-08 07:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea-fuzzer/pgmock.cc
Line
Count
Source
1
// Copyright (C) 2025 Ada Logcis Ltd.
2
//
3
// This Source Code Form is subject to the terms of the Mozilla Public
4
// License, v. 2.0. If a copy of the MPL was not distributed with this
5
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
////////////////////////////////////////////////////////////////////////////////
7
#include <fuzzer/FuzzedDataProvider.h>
8
9
#include <cstdlib>
10
#include <cstring>
11
#include <cstddef>
12
#include <cstdint>
13
14
struct pg_conn {};
15
using  PGconn = pg_conn;
16
17
struct pg_result {
18
    int   status;
19
    int   ntuples;
20
    int   nfields;
21
    char** field_names;
22
    char** values;
23
};
24
using PGresult = pg_result;
25
26
enum {
27
    PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK = 1,
28
    PGRES_TUPLES_OK = 2, PGRES_FATAL_ERROR = 7
29
};
30
31
static thread_local FuzzedDataProvider* g_fdp = nullptr;
32
33
5.20k
extern "C" void pgmock_load_bytes(const uint8_t* data, size_t size) {
34
5.20k
    delete g_fdp;
35
5.20k
    g_fdp = new FuzzedDataProvider(data, size);
36
5.20k
}
37
38
// Helper to duplcate string and drop const for return
39
219k
static char* dupstr(const char* s) {
40
219k
    if (!s) {
41
0
        return nullptr;
42
0
    }
43
44
219k
    size_t n = std::strlen(s) + 1;
45
219k
    char* p = static_cast<char*>(std::malloc(n));
46
219k
    if (p) {
47
219k
        std::memcpy(p, s, n);
48
219k
    }
49
50
219k
    return p;
51
219k
}
52
53
// Helper to make a fuzz row result
54
20.7k
static PGresult* make_fuzz_result() {
55
20.7k
    int nfields = g_fdp->ConsumeIntegralInRange<int>(2, 8);
56
57
20.7k
    PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
58
20.7k
    r->status = PGRES_TUPLES_OK;
59
20.7k
    r->ntuples = 1;
60
20.7k
    r->nfields = nfields;
61
62
20.7k
    r->field_names = static_cast<char**>(std::calloc(nfields, sizeof(char*)));
63
20.7k
    r->values = static_cast<char**>(std::calloc((size_t)nfields, sizeof(char*)));
64
65
84.0k
    for (int i = 0; i < nfields; ++i) {
66
63.3k
        r->field_names[i] = dupstr(g_fdp->ConsumeRandomLengthString(32).c_str());
67
68
        // Provide random type of data return
69
63.3k
        int kind = g_fdp->ConsumeIntegralInRange<int>(0, 3);
70
63.3k
        if (kind == 0) {
71
42.5k
            unsigned v = g_fdp->ConsumeIntegralInRange<unsigned>(0, 9999u);
72
42.5k
            char buf[32];
73
42.5k
            std::snprintf(buf, sizeof(buf), "%u", v);
74
42.5k
            r->values[i] = dupstr(buf);
75
42.5k
        } else if (kind == 1) {
76
5.91k
            r->values[i] = dupstr(g_fdp->ConsumeBool() ? "1" : "0");
77
14.8k
        } else if (kind == 2) {
78
            // IPv4 loopback as text
79
6.36k
            r->values[i] = dupstr("127.0.0.1");
80
8.52k
        } else {
81
            // IPv6 loopback as text
82
8.52k
            r->values[i] = dupstr("::1");
83
8.52k
        }
84
63.3k
    }
85
20.7k
    return r;
86
20.7k
}
87
88
// Helper to make fixed version query for kea
89
23.2k
static PGresult* make_version_result() {
90
23.2k
    PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
91
23.2k
    r->status  = PGRES_TUPLES_OK;
92
23.2k
    r->ntuples = 1;
93
23.2k
    r->nfields = 2;
94
95
23.2k
    r->field_names = static_cast<char**>(std::calloc(2, sizeof(char*)));
96
23.2k
    r->field_names[0] = dupstr("version");
97
23.2k
    r->field_names[1] = dupstr("minor");
98
99
23.2k
    r->values = static_cast<char**>(std::calloc(2, sizeof(char*)));
100
23.2k
    r->values[0] = dupstr("31");
101
23.2k
    r->values[1] = dupstr("0");
102
103
23.2k
    return r;
104
23.2k
}
105
106
// Helper to make success reply to update or delete query
107
309k
static PGresult* make_command_ok_result() {
108
309k
    PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
109
309k
    r->status  = PGRES_COMMAND_OK;
110
309k
    r->ntuples = 0;
111
309k
    r->nfields = 0;
112
309k
    r->field_names = nullptr;
113
309k
    r->values = nullptr;
114
115
309k
    return r;
116
309k
}
117
118
// List of mock functions
119
extern "C" {
120
15.0k
    PGconn* PQconnectdb(const char*) {
121
15.0k
        return static_cast<PGconn*>(std::calloc(1, sizeof(PGconn)));
122
15.0k
    }
123
124
30.0k
    int PQstatus(const PGconn* c) {
125
30.0k
        return c ? 0 : 1;
126
30.0k
    }
127
128
15.0k
    void PQfinish(PGconn* c) {
129
15.0k
        std::free(c);
130
15.0k
    }
131
132
17.7k
    char* PQerrorMessage(const PGconn*) {
133
17.7k
        return const_cast<char*>("");
134
17.7k
    }
135
136
26.7k
    PGresult* PQexec(PGconn* , const char* query) {
137
26.7k
        if (g_fdp->ConsumeBool()) {
138
14.9k
            return make_version_result();
139
14.9k
        }
140
11.7k
        return make_fuzz_result();
141
26.7k
    }
142
143
    PGresult* PQexecParams(PGconn*, const char* cmd, int, const void*,
144
0
                           const char* const*, const int*, const int*, int) {
145
0
        if (g_fdp->ConsumeBool()) {
146
0
            return make_version_result();
147
0
        }
148
0
        return make_fuzz_result();
149
0
    }
150
151
309k
    PGresult* PQprepare(PGconn*, const char*, const char*, int, const unsigned int*) {
152
309k
        return make_command_ok_result();
153
309k
    }
154
155
    PGresult* PQexecPrepared(PGconn*, const char* name, int, const char* const*,
156
17.3k
                             const int*, const int*, int) {
157
17.3k
        if (g_fdp->ConsumeBool()) {
158
8.37k
            return make_version_result();
159
8.37k
        }
160
8.93k
        return make_fuzz_result();
161
17.3k
    }
162
163
353k
    int PQresultStatus(const PGresult* r) {
164
353k
        return r ? r->status : PGRES_FATAL_ERROR;
165
353k
     }
166
167
353k
    int PQntuples(const PGresult* r) {
168
353k
        return r ? r->ntuples : 0;
169
353k
    }
170
171
353k
    int PQnfields(const PGresult* r) {
172
353k
        return r ? r->nfields : 0;
173
353k
    }
174
175
2.18k
    char* PQfname(const PGresult* r, int i) {
176
2.18k
        if (r && i >= 0 && i < r->nfields) {
177
2.18k
            return r->field_names[i];
178
2.18k
        }
179
0
        return const_cast<char*>("");
180
2.18k
    }
181
182
28.3k
    char* PQgetvalue(const PGresult* r, int row, int col) {
183
28.3k
        if (r && row == 0 && col >= 0 && col < r->nfields) {
184
28.3k
            return r->values[col];
185
28.3k
        }
186
0
        return const_cast<char*>("");
187
28.3k
    }
188
189
0
    int PQgetlength(const PGresult* r, int, int col) {
190
0
        if (r && col >= 0 && col < r->nfields && r->values && r->values[col]) {
191
0
            return static_cast<int>(std::strlen(r->values[col]));
192
0
        }
193
0
        return 0;
194
0
    }
195
196
0
    int PQgetisnull(const PGresult*, int, int) {
197
0
        return 0;
198
0
    }
199
200
0
    int PQbinaryTuples(const PGresult*) {
201
0
        return 0;
202
0
    }
203
204
0
    int PQfformat(const PGresult*, int) {
205
0
        return 0;
206
0
    }
207
208
0
    int PQfsize(const PGresult*, int) {
209
0
        return -1;
210
0
    }
211
212
353k
    void PQclear(PGresult* r) {
213
353k
        if (!r) {
214
0
            return;
215
0
        }
216
217
353k
        if (r->field_names) {
218
153k
            for (int i=0; i<r->nfields; ++i) {
219
109k
                if (r->field_names[i]) {
220
109k
                    std::free(r->field_names[i]);
221
109k
                }
222
109k
            }
223
44.0k
            std::free(r->field_names);
224
44.0k
            r->field_names = nullptr;
225
44.0k
        }
226
227
353k
        const int count = (r->ntuples > 0 && r->nfields > 0)
228
353k
                           ? r->ntuples * r->nfields : 0;
229
353k
        if (r->values) {
230
153k
            for (int i = 0; i < count; ++i) {
231
109k
                if (r->values[i]) {
232
109k
                    std::free(r->values[i]);
233
109k
                }
234
109k
            }
235
44.0k
            std::free(r->values);
236
44.0k
            r->values = nullptr;
237
44.0k
        }
238
239
353k
        std::free(r);
240
353k
    }
241
242
3.05k
    const char* PQcmdTuples(const PGresult*) {
243
3.05k
        return "0";
244
3.05k
    }
245
246
0
    char* PQresultErrorField(const PGresult*, int) {
247
0
        return const_cast<char*>("");
248
0
    }
249
250
0
    unsigned char* PQunescapeBytea(const unsigned char*, size_t* to_length) {
251
0
        if (to_length) {
252
0
            *to_length = 0;
253
0
        }
254
255
0
        unsigned char* p = static_cast<unsigned char*>(std::malloc(1));
256
0
        if (p) {
257
0
            p[0] = 0;
258
0
        }
259
260
0
        return p;
261
0
    }
262
263
0
    void PQfreemem(void* p) {
264
0
        std::free(p);
265
0
    }
266
}