Coverage Report

Created: 2026-07-14 08:11

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