Coverage Report

Created: 2026-06-10 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/fuzzer/FuzzDriversStructured.c
Line
Count
Source
1
/* Copyright 2026 Ada Logics Ltd.
2
Licensed under the Apache License, Version 2.0 (the "License");
3
you may not use this file except in compliance with the License.
4
You may obtain a copy of the License at
5
     http://www.apache.org/licenses/LICENSE-2.0
6
Unless required by applicable law or agreed to in writing, software
7
distributed under the License is distributed on an "AS IS" BASIS,
8
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
See the License for the specific language governing permissions and
10
limitations under the License.
11
*/
12
13
/*
14
 * FuzzDriversStructured - Structured fuzzing harness for gpsd
15
 *
16
 * This harness performs protocol-aware fuzzing by constructing valid
17
 * protocol packets with correct checksums from fuzzer input data.
18
 * This enables deeper coverage of protocol-specific parsing code.
19
 */
20
21
#include "gpsd_config.h"
22
23
#include <errno.h>
24
#include <fcntl.h>
25
#include <stdint.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <sys/socket.h>
30
#include <sys/types.h>
31
#include <unistd.h>
32
33
#include "gpsd.h"
34
35
42.7k
#define kMinInputLength 4
36
6.83M
#define kMaxInputLength 8192
37
38
// Safety cap for satellites_visible - matches MAXCHANNELS in gps.h
39
#define MAX_SATS 184
40
41
// Protocol type selectors
42
569
#define PROTO_SIRF      0
43
3.38k
#define PROTO_UBX       1
44
144
#define PROTO_ZODIAC    2
45
213
#define PROTO_GEOSTAR   3
46
4.40k
#define PROTO_NAVCOM    4
47
3.06k
#define PROTO_NMEA      5
48
950
#define PROTO_RTCM3     6
49
6.48k
#define PROTO_TSIP      7
50
1.67k
#define PROTO_GREIS     8
51
463
#define PROTO_SKYTRAQ   9
52
21.3k
#define PROTO_COUNT     10
53
54
// Global session for reuse across fuzzer iterations
55
static struct gps_device_t session;
56
static struct gps_context_t context;
57
static int pipe_fds[2] = {-1, -1};
58
59
// Packet buffer for constructing protocol messages
60
static unsigned char packet_buf[kMaxInputLength + 256];
61
62
static void null_errout(const char *s)
63
6.46k
{
64
6.46k
    (void)s;
65
6.46k
}
66
67
// SiRF checksum: 15-bit sum of payload bytes
68
static uint16_t sirf_checksum(const uint8_t *payload, size_t len)
69
569
{
70
569
    uint32_t sum = 0;
71
43.5k
    for (size_t i = 0; i < len; i++) {
72
42.9k
        sum += payload[i];
73
42.9k
    }
74
569
    return (uint16_t)(sum & 0x7fff);
75
569
}
76
77
// UBX Fletcher-8 checksum
78
static void ubx_checksum(const uint8_t *data, size_t len, uint8_t *ck_a, uint8_t *ck_b)
79
3.38k
{
80
3.38k
    *ck_a = 0;
81
3.38k
    *ck_b = 0;
82
575k
    for (size_t i = 0; i < len; i++) {
83
572k
        *ck_a += data[i];
84
572k
        *ck_b += *ck_a;
85
572k
    }
86
3.38k
}
87
88
// Zodiac checksum: negated sum of 16-bit words
89
static uint16_t zodiac_checksum(const uint16_t *words, size_t count)
90
288
{
91
288
    uint16_t sum = 0;
92
3.25k
    for (size_t i = 0; i < count; i++) {
93
2.96k
        sum += words[i];
94
2.96k
    }
95
288
    return (uint16_t)(-sum);
96
288
}
97
98
// GeoStar checksum: XOR of 32-bit words
99
static uint32_t geostar_checksum(const uint8_t *data, size_t len)
100
213
{
101
213
    uint32_t cs = 0;
102
2.17k
    for (size_t i = 0; i + 3 < len; i += 4) {
103
1.96k
        uint32_t word = (uint32_t)data[i] |
104
1.96k
                       ((uint32_t)data[i+1] << 8) |
105
1.96k
                       ((uint32_t)data[i+2] << 16) |
106
1.96k
                       ((uint32_t)data[i+3] << 24);
107
1.96k
        cs ^= word;
108
1.96k
    }
109
213
    return cs;
110
213
}
111
112
// Navcom checksum: XOR of bytes
113
static uint8_t navcom_checksum(const uint8_t *data, size_t len)
114
4.40k
{
115
4.40k
    uint8_t cs = 0;
116
1.35M
    for (size_t i = 0; i < len; i++) {
117
1.34M
        cs ^= data[i];
118
1.34M
    }
119
4.40k
    return cs;
120
4.40k
}
121
122
// NMEA checksum: XOR of bytes between $ and *
123
static uint8_t nmea_checksum(const uint8_t *data, size_t len)
124
3.06k
{
125
3.06k
    uint8_t cs = 0;
126
213k
    for (size_t i = 0; i < len; i++) {
127
210k
        cs ^= data[i];
128
210k
    }
129
3.06k
    return cs;
130
3.06k
}
131
132
// Skytraq checksum: XOR of payload bytes
133
static uint8_t skytraq_checksum(const uint8_t *data, size_t len)
134
463
{
135
463
    uint8_t cs = 0;
136
53.4k
    for (size_t i = 0; i < len; i++) {
137
53.0k
        cs ^= data[i];
138
53.0k
    }
139
463
    return cs;
140
463
}
141
142
// Build a SiRF packet: 0xA0 0xA2 <len:2> <payload> <csum:2> 0xB0 0xB3
143
static size_t build_sirf_packet(const uint8_t *data, size_t len, uint8_t *out)
144
569
{
145
569
    if (len > 1023) len = 1023;  // SiRF max payload
146
147
569
    out[0] = 0xA0;
148
569
    out[1] = 0xA2;
149
569
    out[2] = (uint8_t)((len >> 8) & 0x07);  // Length high (3 bits)
150
569
    out[3] = (uint8_t)(len & 0xFF);          // Length low
151
569
    memcpy(out + 4, data, len);
152
153
569
    uint16_t cs = sirf_checksum(data, len);
154
569
    out[4 + len] = (uint8_t)(cs >> 8);
155
569
    out[5 + len] = (uint8_t)(cs & 0xFF);
156
569
    out[6 + len] = 0xB0;
157
569
    out[7 + len] = 0xB3;
158
159
569
    return 8 + len;
160
569
}
161
162
// Build a UBX packet: 0xB5 0x62 <class> <id> <len:2> <payload> <ck_a> <ck_b>
163
static size_t build_ubx_packet(const uint8_t *data, size_t len, uint8_t *out)
164
3.38k
{
165
3.38k
    if (len < 2) return 0;
166
3.38k
    if (len > 8192) len = 8192;
167
168
3.38k
    uint8_t msg_class = data[0];
169
3.38k
    uint8_t msg_id = data[1];
170
3.38k
    size_t payload_len = len - 2;
171
172
3.38k
    out[0] = 0xB5;
173
3.38k
    out[1] = 0x62;
174
3.38k
    out[2] = msg_class;
175
3.38k
    out[3] = msg_id;
176
3.38k
    out[4] = (uint8_t)(payload_len & 0xFF);
177
3.38k
    out[5] = (uint8_t)((payload_len >> 8) & 0xFF);
178
179
3.38k
    if (payload_len > 0) {
180
3.36k
        memcpy(out + 6, data + 2, payload_len);
181
3.36k
    }
182
183
3.38k
    uint8_t ck_a, ck_b;
184
3.38k
    ubx_checksum(out + 2, 4 + payload_len, &ck_a, &ck_b);
185
3.38k
    out[6 + payload_len] = ck_a;
186
3.38k
    out[7 + payload_len] = ck_b;
187
188
3.38k
    return 8 + payload_len;
189
3.38k
}
190
191
// Build a Zodiac packet: 0xFF 0x81 <id:2> <ndata:2> <flags:2> <hsum:2> <data> <dsum:2>
192
static size_t build_zodiac_packet(const uint8_t *data, size_t len, uint8_t *out)
193
144
{
194
144
    if (len < 2) return 0;
195
196
144
    uint16_t msg_id = data[0] | ((uint16_t)data[1] << 8);
197
144
    size_t ndata = (len - 2) / 2;  // Number of 16-bit words
198
144
    if (ndata > 100) ndata = 100;
199
200
144
    out[0] = 0xFF;
201
144
    out[1] = 0x81;
202
144
    out[2] = (uint8_t)(msg_id & 0xFF);
203
144
    out[3] = (uint8_t)((msg_id >> 8) & 0xFF);
204
144
    out[4] = (uint8_t)(ndata & 0xFF);
205
144
    out[5] = (uint8_t)((ndata >> 8) & 0xFF);
206
144
    out[6] = 0x00;  // flags
207
144
    out[7] = 0x00;
208
209
    // Header checksum (words 0-3)
210
144
    uint16_t hsum = zodiac_checksum((uint16_t*)out, 4);
211
144
    out[8] = (uint8_t)(hsum & 0xFF);
212
144
    out[9] = (uint8_t)((hsum >> 8) & 0xFF);
213
214
    // Copy data words
215
144
    size_t data_bytes = ndata * 2;
216
144
    if (len >= 2 + data_bytes) {
217
144
        memcpy(out + 10, data + 2, data_bytes);
218
144
    } else {
219
0
        memset(out + 10, 0, data_bytes);
220
0
        if (len > 2) {
221
0
            memcpy(out + 10, data + 2, len - 2);
222
0
        }
223
0
    }
224
225
    // Data checksum
226
144
    uint16_t dsum = zodiac_checksum((uint16_t*)(out + 10), ndata);
227
144
    out[10 + data_bytes] = (uint8_t)(dsum & 0xFF);
228
144
    out[11 + data_bytes] = (uint8_t)((dsum >> 8) & 0xFF);
229
230
144
    return 12 + data_bytes;
231
144
}
232
233
// Build a GeoStar packet: 'P' 'S' 'G' 'G' <id:2> <len:2> <data> <checksum:4>
234
static size_t build_geostar_packet(const uint8_t *data, size_t len, uint8_t *out)
235
213
{
236
213
    if (len < 2) return 0;
237
238
213
    uint16_t msg_id = data[0] | ((uint16_t)data[1] << 8);
239
213
    size_t nwords = (len - 2) / 4;  // Number of 32-bit words
240
213
    if (nwords > 100) nwords = 100;
241
242
213
    out[0] = 'P';
243
213
    out[1] = 'S';
244
213
    out[2] = 'G';
245
213
    out[3] = 'G';
246
213
    out[4] = (uint8_t)(msg_id & 0xFF);
247
213
    out[5] = (uint8_t)((msg_id >> 8) & 0xFF);
248
213
    out[6] = (uint8_t)(nwords & 0xFF);
249
213
    out[7] = (uint8_t)((nwords >> 8) & 0xFF);
250
251
213
    size_t data_bytes = nwords * 4;
252
213
    if (len >= 2 + data_bytes) {
253
213
        memcpy(out + 8, data + 2, data_bytes);
254
213
    } else {
255
0
        memset(out + 8, 0, data_bytes);
256
0
        if (len > 2) {
257
0
            memcpy(out + 8, data + 2, len - 2);
258
0
        }
259
0
    }
260
261
    // Checksum covers header + data
262
213
    size_t cs_len = 8 + data_bytes;
263
213
    uint32_t cs = geostar_checksum(out, cs_len);
264
213
    out[cs_len] = (uint8_t)(cs & 0xFF);
265
213
    out[cs_len + 1] = (uint8_t)((cs >> 8) & 0xFF);
266
213
    out[cs_len + 2] = (uint8_t)((cs >> 16) & 0xFF);
267
213
    out[cs_len + 3] = (uint8_t)((cs >> 24) & 0xFF);
268
269
213
    return cs_len + 4;
270
213
}
271
272
// Build a Navcom packet: 0x02 0x99 0x66 <id> <len:2> <data> <checksum> 0x03
273
static size_t build_navcom_packet(const uint8_t *data, size_t len, uint8_t *out)
274
4.40k
{
275
4.40k
    if (len < 1) return 0;
276
4.40k
    if (len > 1000) len = 1000;
277
278
4.40k
    uint8_t msg_id = data[0];
279
4.40k
    size_t payload_len = len - 1;
280
281
4.40k
    out[0] = 0x02;
282
4.40k
    out[1] = 0x99;
283
4.40k
    out[2] = 0x66;
284
4.40k
    out[3] = msg_id;
285
4.40k
    out[4] = (uint8_t)((payload_len >> 8) & 0xFF);
286
4.40k
    out[5] = (uint8_t)(payload_len & 0xFF);
287
288
4.40k
    if (payload_len > 0) {
289
4.40k
        memcpy(out + 6, data + 1, payload_len);
290
4.40k
    }
291
292
    // Checksum covers ID and payload
293
4.40k
    uint8_t cs = navcom_checksum(out + 3, 3 + payload_len);
294
4.40k
    out[6 + payload_len] = cs;
295
4.40k
    out[7 + payload_len] = 0x03;  // ETX
296
297
4.40k
    return 8 + payload_len;
298
4.40k
}
299
300
// Build an NMEA sentence with checksum
301
static size_t build_nmea_packet(const uint8_t *data, size_t len, uint8_t *out)
302
3.06k
{
303
    // List of NMEA sentence types to use
304
3.06k
    static const char *nmea_types[] = {
305
3.06k
        "GPGGA", "GPRMC", "GPGLL", "GPGSA", "GPGSV", "GPVTG", "GPZDA",
306
3.06k
        "GPGBS", "GPGST", "GPGRS", "GNGNS", "GPDTM", "GPTXT",
307
3.06k
        "GLGSA", "GLGSV", "GAGSA", "GAGSV", "GBGSA", "GBGSV",
308
3.06k
        "HCHDG", "SDDBT", "SDDPT", "WIMWV", "GPROT", "GPTHS"
309
3.06k
    };
310
3.06k
    static const size_t num_types = sizeof(nmea_types) / sizeof(nmea_types[0]);
311
312
3.06k
    if (len < 1) return 0;
313
314
    // Use first byte to select sentence type
315
3.06k
    size_t type_idx = data[0] % num_types;
316
3.06k
    const char *sentence_type = nmea_types[type_idx];
317
318
    // Build the sentence body from remaining data
319
3.06k
    out[0] = '$';
320
3.06k
    size_t pos = 1;
321
322
    // Copy sentence type
323
3.06k
    size_t type_len = strlen(sentence_type);
324
3.06k
    memcpy(out + pos, sentence_type, type_len);
325
3.06k
    pos += type_len;
326
327
    // Add comma-separated fields from fuzzer data
328
198k
    for (size_t i = 1; i < len && pos < kMaxInputLength - 10; i++) {
329
195k
        if (data[i] == '\r' || data[i] == '\n' || data[i] == '*') {
330
4.30k
            out[pos++] = ',';  // Replace invalid chars with comma
331
191k
        } else if (data[i] >= 32 && data[i] < 127) {
332
122k
            out[pos++] = data[i];
333
122k
        } else {
334
68.9k
            out[pos++] = ',';
335
68.9k
        }
336
195k
    }
337
338
    // Compute checksum
339
3.06k
    uint8_t cs = nmea_checksum(out + 1, pos - 1);
340
341
    // Add checksum and line ending
342
3.06k
    out[pos++] = '*';
343
3.06k
    static const char hex[] = "0123456789ABCDEF";
344
3.06k
    out[pos++] = hex[(cs >> 4) & 0x0F];
345
3.06k
    out[pos++] = hex[cs & 0x0F];
346
3.06k
    out[pos++] = '\r';
347
3.06k
    out[pos++] = '\n';
348
349
3.06k
    return pos;
350
3.06k
}
351
352
// Build an RTCM3 packet: 0xD3 <len:2> <data> <crc24:3>
353
// CRC-24Q is used for RTCM3
354
static uint32_t crc24q_hash(const uint8_t *data, size_t len)
355
950
{
356
950
    static const uint32_t crc24q_table[256] = {
357
950
        0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC, 0x9F7F17,
358
950
        0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5, 0x3EFE2E,
359
950
        0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65, 0x5A319E,
360
950
        0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C, 0xFBB0A7,
361
950
        0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205, 0x93AEFE,
362
950
        0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, 0x322FC7,
363
950
        0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, 0x56E077,
364
950
        0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5, 0xF7614E,
365
950
        0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E, 0x86DCC5,
366
950
        0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107, 0x275DFC,
367
950
        0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7, 0x43924C,
368
950
        0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, 0xE21375,
369
950
        0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, 0x8A0D2C,
370
950
        0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, 0x2B8C15,
371
950
        0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E, 0x4F43A5,
372
950
        0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67, 0xEEC29C,
373
950
        0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448, 0xAC38B3,
374
950
        0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571, 0x0DB98A,
375
950
        0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, 0x69763A,
376
950
        0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, 0xC8F703,
377
950
        0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, 0xA0E95A,
378
950
        0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498, 0x016863,
379
950
        0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28, 0x65A7D3,
380
950
        0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11, 0xC426EA,
381
950
        0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A, 0xB59B61,
382
950
        0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, 0x141A58,
383
950
        0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, 0x70D5E8,
384
950
        0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, 0xD154D1,
385
950
        0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673, 0xB94A88,
386
950
        0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A, 0x18CBB1,
387
950
        0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA, 0x7C0401,
388
950
        0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3, 0xDD8538
389
950
    };
390
391
950
    uint32_t crc = 0;
392
61.7k
    for (size_t i = 0; i < len; i++) {
393
60.7k
        crc = ((crc << 8) & 0xFFFFFF) ^ crc24q_table[(crc >> 16) ^ data[i]];
394
60.7k
    }
395
950
    return crc;
396
950
}
397
398
static size_t build_rtcm3_packet(const uint8_t *data, size_t len, uint8_t *out)
399
950
{
400
950
    if (len > 1023) len = 1023;  // RTCM3 max payload
401
402
950
    out[0] = 0xD3;
403
950
    out[1] = (uint8_t)((len >> 8) & 0x03);  // High 2 bits of length
404
950
    out[2] = (uint8_t)(len & 0xFF);          // Low 8 bits
405
406
950
    memcpy(out + 3, data, len);
407
408
    // CRC-24Q covers preamble + length + data
409
950
    uint32_t crc = crc24q_hash(out, 3 + len);
410
950
    out[3 + len] = (uint8_t)((crc >> 16) & 0xFF);
411
950
    out[4 + len] = (uint8_t)((crc >> 8) & 0xFF);
412
950
    out[5 + len] = (uint8_t)(crc & 0xFF);
413
414
950
    return 6 + len;
415
950
}
416
417
// Build a TSIP packet: DLE <id> <data with DLE stuffing> DLE ETX
418
static size_t build_tsip_packet(const uint8_t *data, size_t len, uint8_t *out)
419
6.48k
{
420
6.48k
    if (len < 1) return 0;
421
422
6.48k
    uint8_t msg_id = data[0];
423
6.48k
    size_t pos = 0;
424
425
6.48k
    out[pos++] = 0x10;  // DLE
426
6.48k
    out[pos++] = msg_id;
427
428
    // Copy payload with DLE stuffing
429
6.62M
    for (size_t i = 1; i < len && pos < kMaxInputLength - 4; i++) {
430
6.61M
        if (data[i] == 0x10) {
431
65.0k
            out[pos++] = 0x10;  // Stuff DLE
432
65.0k
        }
433
6.61M
        out[pos++] = data[i];
434
6.61M
    }
435
436
6.48k
    out[pos++] = 0x10;  // DLE
437
6.48k
    out[pos++] = 0x03;  // ETX
438
439
6.48k
    return pos;
440
6.48k
}
441
442
// Build a GREIS packet: <id:2> <data> <checksum> CR LF
443
static size_t build_greis_packet(const uint8_t *data, size_t len, uint8_t *out)
444
1.67k
{
445
1.67k
    if (len < 2) return 0;
446
1.67k
    if (len > 200) len = 200;
447
448
    // Standard GREIS message IDs
449
1.67k
    static const char *greis_ids[] = {"RE", "ER", "PM", "RC", "RD", "SI", "EL"};
450
1.67k
    static const size_t num_ids = sizeof(greis_ids) / sizeof(greis_ids[0]);
451
452
1.67k
    size_t id_idx = data[0] % num_ids;
453
1.67k
    out[0] = greis_ids[id_idx][0];
454
1.67k
    out[1] = greis_ids[id_idx][1];
455
456
1.67k
    size_t payload_len = len - 2;
457
1.67k
    if (payload_len > 0) {
458
1.66k
        memcpy(out + 2, data + 2, payload_len);
459
1.66k
    }
460
461
    // Simple XOR checksum for GREIS
462
1.67k
    uint8_t cs = 0;
463
64.3k
    for (size_t i = 0; i < 2 + payload_len; i++) {
464
62.6k
        cs ^= out[i];
465
62.6k
    }
466
1.67k
    out[2 + payload_len] = cs;
467
1.67k
    out[3 + payload_len] = '\r';
468
1.67k
    out[4 + payload_len] = '\n';
469
470
1.67k
    return 5 + payload_len;
471
1.67k
}
472
473
// Build a Skytraq packet: 0xA0 0xA1 <len:2> <payload> <checksum> 0x0D 0x0A
474
static size_t build_skytraq_packet(const uint8_t *data, size_t len, uint8_t *out)
475
463
{
476
463
    if (len > 1000) len = 1000;
477
478
463
    out[0] = 0xA0;
479
463
    out[1] = 0xA1;
480
463
    out[2] = (uint8_t)((len >> 8) & 0xFF);
481
463
    out[3] = (uint8_t)(len & 0xFF);
482
483
463
    memcpy(out + 4, data, len);
484
485
463
    uint8_t cs = skytraq_checksum(data, len);
486
463
    out[4 + len] = cs;
487
463
    out[5 + len] = 0x0D;
488
463
    out[6 + len] = 0x0A;
489
490
463
    return 7 + len;
491
463
}
492
493
int LLVMFuzzerInitialize(int *argc, char ***argv)
494
2
{
495
2
    if (pipe(pipe_fds) < 0) {
496
0
        return -1;
497
0
    }
498
499
2
    int flags = fcntl(pipe_fds[0], F_GETFL, 0);
500
2
    if (flags < 0 || fcntl(pipe_fds[0], F_SETFL, flags | O_NONBLOCK) < 0) {
501
0
        close(pipe_fds[0]);
502
0
        close(pipe_fds[1]);
503
0
        return -1;
504
0
    }
505
506
    // Also make write end non-blocking
507
2
    flags = fcntl(pipe_fds[1], F_GETFL, 0);
508
2
    if (flags >= 0) {
509
2
        fcntl(pipe_fds[1], F_SETFL, flags | O_NONBLOCK);
510
2
    }
511
512
2
    gps_context_init(&context, "fuzz_structured");
513
2
    gpsd_init(&session, &context, "/dev/fuzz_structured");
514
515
2
    context.errout.debug = 0;
516
2
    context.errout.report = null_errout;
517
518
    // Set fd BEFORE gpsd_clear so pps_thread.devicefd gets correct value
519
2
    session.gpsdata.gps_fd = pipe_fds[0];
520
521
    // Set sourcetype to PIPE to prevent NTP/PPS code paths from activating
522
    // (see ntpshm_link_activate which skips pipes)
523
2
    session.sourcetype = SOURCE_PIPE;
524
525
2
    return 0;
526
2
}
527
528
// Drain any leftover data from the pipe
529
static void drain_pipe(int fd)
530
21.3k
{
531
21.3k
    unsigned char buf[4096];
532
21.3k
    while (read(fd, buf, sizeof(buf)) > 0) {
533
        // Keep reading until empty
534
0
    }
535
21.3k
}
536
537
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
538
21.3k
{
539
21.3k
    if (Size < kMinInputLength || Size > kMaxInputLength) {
540
3
        return 0;
541
3
    }
542
543
    // Drain any leftover data from previous iteration
544
21.3k
    drain_pipe(pipe_fds[0]);
545
546
    // Set fd before gpsd_clear so pps_thread gets correct value
547
21.3k
    session.gpsdata.gps_fd = pipe_fds[0];
548
549
21.3k
    gpsd_clear(&session);
550
21.3k
    session.device_type = NULL;
551
21.3k
    session.last_controller = NULL;
552
553
    // Ensure sourcetype stays set to prevent NTP/PPS activation
554
21.3k
    session.sourcetype = SOURCE_PIPE;
555
556
    // Clear satellite data to prevent out-of-bounds access in fill_dop
557
    // when satellites_visible gets corrupted by fuzzer-generated data
558
21.3k
    session.gpsdata.satellites_visible = 0;
559
21.3k
    gpsd_zero_satellites(&session.gpsdata);
560
561
    // First byte selects protocol type
562
21.3k
    uint8_t proto_type = Data[0] % PROTO_COUNT;
563
564
    // Second byte provides additional control
565
21.3k
    context.readonly = (Data[1] & 0x01);
566
21.3k
    context.passive = (Data[1] & 0x02);
567
568
    // Build protocol-specific packet from remaining data
569
21.3k
    size_t packet_len = 0;
570
21.3k
    const uint8_t *payload = Data + 2;
571
21.3k
    size_t payload_len = Size - 2;
572
573
21.3k
    switch (proto_type) {
574
569
    case PROTO_SIRF:
575
569
        packet_len = build_sirf_packet(payload, payload_len, packet_buf);
576
569
        break;
577
3.38k
    case PROTO_UBX:
578
3.38k
        packet_len = build_ubx_packet(payload, payload_len, packet_buf);
579
3.38k
        break;
580
144
    case PROTO_ZODIAC:
581
144
        packet_len = build_zodiac_packet(payload, payload_len, packet_buf);
582
144
        break;
583
213
    case PROTO_GEOSTAR:
584
213
        packet_len = build_geostar_packet(payload, payload_len, packet_buf);
585
213
        break;
586
4.40k
    case PROTO_NAVCOM:
587
4.40k
        packet_len = build_navcom_packet(payload, payload_len, packet_buf);
588
4.40k
        break;
589
3.06k
    case PROTO_NMEA:
590
3.06k
        packet_len = build_nmea_packet(payload, payload_len, packet_buf);
591
3.06k
        break;
592
950
    case PROTO_RTCM3:
593
950
        packet_len = build_rtcm3_packet(payload, payload_len, packet_buf);
594
950
        break;
595
6.48k
    case PROTO_TSIP:
596
6.48k
        packet_len = build_tsip_packet(payload, payload_len, packet_buf);
597
6.48k
        break;
598
1.67k
    case PROTO_GREIS:
599
1.67k
        packet_len = build_greis_packet(payload, payload_len, packet_buf);
600
1.67k
        break;
601
463
    case PROTO_SKYTRAQ:
602
463
        packet_len = build_skytraq_packet(payload, payload_len, packet_buf);
603
463
        break;
604
0
    default:
605
0
        return 0;
606
21.3k
    }
607
608
21.3k
    if (packet_len == 0) {
609
0
        return 0;
610
0
    }
611
612
    // Write constructed packet to pipe
613
21.3k
    ssize_t written = write(pipe_fds[1], packet_buf, packet_len);
614
21.3k
    if (written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
615
0
        return 0;
616
0
    }
617
618
    // Process packets
619
21.3k
    int max_iterations = 100;
620
2.15M
    while (max_iterations-- > 0) {
621
2.13M
        gps_mask_t changed = gpsd_poll(&session);
622
2.13M
        if (changed == 0 || changed == ERROR_SET) {
623
0
            break;
624
0
        }
625
2.13M
    }
626
627
21.3k
    return 0;
628
21.3k
}