Coverage Report

Created: 2025-11-16 07:29

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.21k
extern "C" void pgmock_load_bytes(const uint8_t* data, size_t size) {
34
5.21k
    delete g_fdp;
35
5.21k
    g_fdp = new FuzzedDataProvider(data, size);
36
5.21k
}
37
38
// Helper to duplcate string and drop const for return
39
218k
static char* dupstr(const char* s) {
40
218k
    if (!s) {
41
0
        return nullptr;
42
0
    }
43
44
218k
    size_t n = std::strlen(s) + 1;
45
218k
    char* p = static_cast<char*>(std::malloc(n));
46
218k
    if (p) {
47
218k
        std::memcpy(p, s, n);
48
218k
    }
49
50
218k
    return p;
51
218k
}
52
53
// Helper to make a fuzz row result
54
22.2k
static PGresult* make_fuzz_result() {
55
22.2k
    int nfields = g_fdp->ConsumeIntegralInRange<int>(2, 8);
56
57
22.2k
    PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
58
22.2k
    r->status = PGRES_TUPLES_OK;
59
22.2k
    r->ntuples = 1;
60
22.2k
    r->nfields = nfields;
61
62
22.2k
    r->field_names = static_cast<char**>(std::calloc(nfields, sizeof(char*)));
63
22.2k
    r->values = static_cast<char**>(std::calloc((size_t)nfields, sizeof(char*)));
64
65
90.2k
    for (int i = 0; i < nfields; ++i) {
66
67.9k
        r->field_names[i] = dupstr(g_fdp->ConsumeRandomLengthString(32).c_str());
67
68
        // Provide random type of data return
69
67.9k
        int kind = g_fdp->ConsumeIntegralInRange<int>(0, 3);
70
67.9k
        if (kind == 0) {
71
45.6k
            unsigned v = g_fdp->ConsumeIntegralInRange<unsigned>(0, 9999u);
72
45.6k
            char buf[32];
73
45.6k
            std::snprintf(buf, sizeof(buf), "%u", v);
74
45.6k
            r->values[i] = dupstr(buf);
75
45.6k
        } else if (kind == 1) {
76
7.00k
            r->values[i] = dupstr(g_fdp->ConsumeBool() ? "1" : "0");
77
15.2k
        } else if (kind == 2) {
78
            // IPv4 loopback as text
79
6.46k
            r->values[i] = dupstr("127.0.0.1");
80
8.82k
        } else {
81
            // IPv6 loopback as text
82
8.82k
            r->values[i] = dupstr("::1");
83
8.82k
        }
84
67.9k
    }
85
22.2k
    return r;
86
22.2k
}
87
88
// Helper to make fixed version query for kea
89
20.7k
static PGresult* make_version_result() {
90
20.7k
    PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
91
20.7k
    r->status  = PGRES_TUPLES_OK;
92
20.7k
    r->ntuples = 1;
93
20.7k
    r->nfields = 2;
94
95
20.7k
    r->field_names = static_cast<char**>(std::calloc(2, sizeof(char*)));
96
20.7k
    r->field_names[0] = dupstr("version");
97
20.7k
    r->field_names[1] = dupstr("minor");
98
99
20.7k
    r->values = static_cast<char**>(std::calloc(2, sizeof(char*)));
100
20.7k
    r->values[0] = dupstr("31");
101
20.7k
    r->values[1] = dupstr("0");
102
103
20.7k
    return r;
104
20.7k
}
105
106
// Helper to make success reply to update or delete query
107
298k
static PGresult* make_command_ok_result() {
108
298k
    PGresult* r = static_cast<PGresult*>(std::calloc(1, sizeof(PGresult)));
109
298k
    r->status  = PGRES_COMMAND_OK;
110
298k
    r->ntuples = 0;
111
298k
    r->nfields = 0;
112
298k
    r->field_names = nullptr;
113
298k
    r->values = nullptr;
114
115
298k
    return r;
116
298k
}
117
118
// List of mock functions
119
extern "C" {
120
14.7k
    PGconn* PQconnectdb(const char*) {
121
14.7k
        return static_cast<PGconn*>(std::calloc(1, sizeof(PGconn)));
122
14.7k
    }
123
124
29.4k
    int PQstatus(const PGconn* c) {
125
29.4k
        return c ? 0 : 1;
126
29.4k
    }
127
128
14.7k
    void PQfinish(PGconn* c) {
129
14.7k
        std::free(c);
130
14.7k
    }
131
132
17.3k
    char* PQerrorMessage(const PGconn*) {
133
17.3k
        return const_cast<char*>("");
134
17.3k
    }
135
136
26.1k
    PGresult* PQexec(PGconn* , const char* query) {
137
26.1k
        if (g_fdp->ConsumeBool()) {
138
13.6k
            return make_version_result();
139
13.6k
        }
140
12.4k
        return make_fuzz_result();
141
26.1k
    }
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
298k
    PGresult* PQprepare(PGconn*, const char*, const char*, int, const unsigned int*) {
152
298k
        return make_command_ok_result();
153
298k
    }
154
155
    PGresult* PQexecPrepared(PGconn*, const char* name, int, const char* const*,
156
16.8k
                             const int*, const int*, int) {
157
16.8k
        if (g_fdp->ConsumeBool()) {
158
7.09k
            return make_version_result();
159
7.09k
        }
160
9.80k
        return make_fuzz_result();
161
16.8k
    }
162
163
341k
    int PQresultStatus(const PGresult* r) {
164
341k
        return r ? r->status : PGRES_FATAL_ERROR;
165
341k
     }
166
167
341k
    int PQntuples(const PGresult* r) {
168
341k
        return r ? r->ntuples : 0;
169
341k
    }
170
171
341k
    int PQnfields(const PGresult* r) {
172
341k
        return r ? r->nfields : 0;
173
341k
    }
174
175
2.09k
    char* PQfname(const PGresult* r, int i) {
176
2.09k
        if (r && i >= 0 && i < r->nfields) {
177
2.09k
            return r->field_names[i];
178
2.09k
        }
179
0
        return const_cast<char*>("");
180
2.09k
    }
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
341k
    void PQclear(PGresult* r) {
213
341k
        if (!r) {
214
0
            return;
215
0
        }
216
217
341k
        if (r->field_names) {
218
152k
            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
43.0k
            std::free(r->field_names);
224
43.0k
            r->field_names = nullptr;
225
43.0k
        }
226
227
341k
        const int count = (r->ntuples > 0 && r->nfields > 0)
228
341k
                           ? r->ntuples * r->nfields : 0;
229
341k
        if (r->values) {
230
152k
            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
43.0k
            std::free(r->values);
236
43.0k
            r->values = nullptr;
237
43.0k
        }
238
239
341k
        std::free(r);
240
341k
    }
241
242
2.89k
    const char* PQcmdTuples(const PGresult*) {
243
2.89k
        return "0";
244
2.89k
    }
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
}