Coverage Report

Created: 2026-08-31 06:17

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
52.4k
#define kMinInputLength 4
36
6.07M
#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
648
#define PROTO_SIRF      0
43
4.10k
#define PROTO_UBX       1
44
196
#define PROTO_ZODIAC    2
45
264
#define PROTO_GEOSTAR   3
46
4.64k
#define PROTO_NAVCOM    4
47
4.06k
#define PROTO_NMEA      5
48
1.15k
#define PROTO_RTCM3     6
49
8.52k
#define PROTO_TSIP      7
50
2.09k
#define PROTO_GREIS     8
51
532
#define PROTO_SKYTRAQ   9
52
26.2k
#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
7.34k
{
64
7.34k
    (void)s;
65
7.34k
}
66
67
// SiRF checksum: 15-bit sum of payload bytes
68
static uint16_t sirf_checksum(const uint8_t *payload, size_t len)
69
648
{
70
648
    uint32_t sum = 0;
71
42.7k
    for (size_t i = 0; i < len; i++) {
72
42.1k
        sum += payload[i];
73
42.1k
    }
74
648
    return (uint16_t)(sum & 0x7fff);
75
648
}
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
4.10k
{
80
4.10k
    *ck_a = 0;
81
4.10k
    *ck_b = 0;
82
803k
    for (size_t i = 0; i < len; i++) {
83
799k
        *ck_a += data[i];
84
799k
        *ck_b += *ck_a;
85
799k
    }
86
4.10k
}
87
88
// Zodiac checksum: negated sum of 16-bit words
89
static uint16_t zodiac_checksum(const uint16_t *words, size_t count)
90
392
{
91
392
    uint16_t sum = 0;
92
6.91k
    for (size_t i = 0; i < count; i++) {
93
6.52k
        sum += words[i];
94
6.52k
    }
95
392
    return (uint16_t)(-sum);
96
392
}
97
98
// GeoStar checksum: XOR of 32-bit words
99
static uint32_t geostar_checksum(const uint8_t *data, size_t len)
100
264
{
101
264
    uint32_t cs = 0;
102
2.15k
    for (size_t i = 0; i + 3 < len; i += 4) {
103
1.89k
        uint32_t word = (uint32_t)data[i] |
104
1.89k
                       ((uint32_t)data[i+1] << 8) |
105
1.89k
                       ((uint32_t)data[i+2] << 16) |
106
1.89k
                       ((uint32_t)data[i+3] << 24);
107
1.89k
        cs ^= word;
108
1.89k
    }
109
264
    return cs;
110
264
}
111
112
// Navcom checksum: XOR of bytes
113
static uint8_t navcom_checksum(const uint8_t *data, size_t len)
114
4.64k
{
115
4.64k
    uint8_t cs = 0;
116
1.14M
    for (size_t i = 0; i < len; i++) {
117
1.14M
        cs ^= data[i];
118
1.14M
    }
119
4.64k
    return cs;
120
4.64k
}
121
122
// NMEA checksum: XOR of bytes between $ and *
123
static uint8_t nmea_checksum(const uint8_t *data, size_t len)
124
4.06k
{
125
4.06k
    uint8_t cs = 0;
126
168k
    for (size_t i = 0; i < len; i++) {
127
163k
        cs ^= data[i];
128
163k
    }
129
4.06k
    return cs;
130
4.06k
}
131
132
// Skytraq checksum: XOR of payload bytes
133
static uint8_t skytraq_checksum(const uint8_t *data, size_t len)
134
532
{
135
532
    uint8_t cs = 0;
136
52.6k
    for (size_t i = 0; i < len; i++) {
137
52.1k
        cs ^= data[i];
138
52.1k
    }
139
532
    return cs;
140
532
}
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
648
{
145
648
    if (len > 1023) len = 1023;  // SiRF max payload
146
147
648
    out[0] = 0xA0;
148
648
    out[1] = 0xA2;
149
648
    out[2] = (uint8_t)((len >> 8) & 0x07);  // Length high (3 bits)
150
648
    out[3] = (uint8_t)(len & 0xFF);          // Length low
151
648
    memcpy(out + 4, data, len);
152
153
648
    uint16_t cs = sirf_checksum(data, len);
154
648
    out[4 + len] = (uint8_t)(cs >> 8);
155
648
    out[5 + len] = (uint8_t)(cs & 0xFF);
156
648
    out[6 + len] = 0xB0;
157
648
    out[7 + len] = 0xB3;
158
159
648
    return 8 + len;
160
648
}
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
4.10k
{
165
4.10k
    if (len < 2) return 0;
166
4.10k
    if (len > 8192) len = 8192;
167
168
4.10k
    uint8_t msg_class = data[0];
169
4.10k
    uint8_t msg_id = data[1];
170
4.10k
    size_t payload_len = len - 2;
171
172
4.10k
    out[0] = 0xB5;
173
4.10k
    out[1] = 0x62;
174
4.10k
    out[2] = msg_class;
175
4.10k
    out[3] = msg_id;
176
4.10k
    out[4] = (uint8_t)(payload_len & 0xFF);
177
4.10k
    out[5] = (uint8_t)((payload_len >> 8) & 0xFF);
178
179
4.10k
    if (payload_len > 0) {
180
4.08k
        memcpy(out + 6, data + 2, payload_len);
181
4.08k
    }
182
183
4.10k
    uint8_t ck_a, ck_b;
184
4.10k
    ubx_checksum(out + 2, 4 + payload_len, &ck_a, &ck_b);
185
4.10k
    out[6 + payload_len] = ck_a;
186
4.10k
    out[7 + payload_len] = ck_b;
187
188
4.10k
    return 8 + payload_len;
189
4.10k
}
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
196
{
194
196
    if (len < 2) return 0;
195
196
196
    uint16_t msg_id = data[0] | ((uint16_t)data[1] << 8);
197
196
    size_t ndata = (len - 2) / 2;  // Number of 16-bit words
198
196
    if (ndata > 100) ndata = 100;
199
200
196
    out[0] = 0xFF;
201
196
    out[1] = 0x81;
202
196
    out[2] = (uint8_t)(msg_id & 0xFF);
203
196
    out[3] = (uint8_t)((msg_id >> 8) & 0xFF);
204
196
    out[4] = (uint8_t)(ndata & 0xFF);
205
196
    out[5] = (uint8_t)((ndata >> 8) & 0xFF);
206
196
    out[6] = 0x00;  // flags
207
196
    out[7] = 0x00;
208
209
    // Header checksum (words 0-3)
210
196
    uint16_t hsum = zodiac_checksum((uint16_t*)out, 4);
211
196
    out[8] = (uint8_t)(hsum & 0xFF);
212
196
    out[9] = (uint8_t)((hsum >> 8) & 0xFF);
213
214
    // Copy data words
215
196
    size_t data_bytes = ndata * 2;
216
196
    if (len >= 2 + data_bytes) {
217
196
        memcpy(out + 10, data + 2, data_bytes);
218
196
    } 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
196
    uint16_t dsum = zodiac_checksum((uint16_t*)(out + 10), ndata);
227
196
    out[10 + data_bytes] = (uint8_t)(dsum & 0xFF);
228
196
    out[11 + data_bytes] = (uint8_t)((dsum >> 8) & 0xFF);
229
230
196
    return 12 + data_bytes;
231
196
}
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
264
{
236
264
    if (len < 2) return 0;
237
238
264
    uint16_t msg_id = data[0] | ((uint16_t)data[1] << 8);
239
264
    size_t nwords = (len - 2) / 4;  // Number of 32-bit words
240
264
    if (nwords > 100) nwords = 100;
241
242
264
    out[0] = 'P';
243
264
    out[1] = 'S';
244
264
    out[2] = 'G';
245
264
    out[3] = 'G';
246
264
    out[4] = (uint8_t)(msg_id & 0xFF);
247
264
    out[5] = (uint8_t)((msg_id >> 8) & 0xFF);
248
264
    out[6] = (uint8_t)(nwords & 0xFF);
249
264
    out[7] = (uint8_t)((nwords >> 8) & 0xFF);
250
251
264
    size_t data_bytes = nwords * 4;
252
264
    if (len >= 2 + data_bytes) {
253
264
        memcpy(out + 8, data + 2, data_bytes);
254
264
    } 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
264
    size_t cs_len = 8 + data_bytes;
263
264
    uint32_t cs = geostar_checksum(out, cs_len);
264
264
    out[cs_len] = (uint8_t)(cs & 0xFF);
265
264
    out[cs_len + 1] = (uint8_t)((cs >> 8) & 0xFF);
266
264
    out[cs_len + 2] = (uint8_t)((cs >> 16) & 0xFF);
267
264
    out[cs_len + 3] = (uint8_t)((cs >> 24) & 0xFF);
268
269
264
    return cs_len + 4;
270
264
}
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.64k
{
275
4.64k
    if (len < 1) return 0;
276
4.64k
    if (len > 1000) len = 1000;
277
278
4.64k
    uint8_t msg_id = data[0];
279
4.64k
    size_t payload_len = len - 1;
280
281
4.64k
    out[0] = 0x02;
282
4.64k
    out[1] = 0x99;
283
4.64k
    out[2] = 0x66;
284
4.64k
    out[3] = msg_id;
285
4.64k
    out[4] = (uint8_t)((payload_len >> 8) & 0xFF);
286
4.64k
    out[5] = (uint8_t)(payload_len & 0xFF);
287
288
4.64k
    if (payload_len > 0) {
289
4.64k
        memcpy(out + 6, data + 1, payload_len);
290
4.64k
    }
291
292
    // Checksum covers ID and payload
293
4.64k
    uint8_t cs = navcom_checksum(out + 3, 3 + payload_len);
294
4.64k
    out[6 + payload_len] = cs;
295
4.64k
    out[7 + payload_len] = 0x03;  // ETX
296
297
4.64k
    return 8 + payload_len;
298
4.64k
}
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
4.06k
{
303
    // List of NMEA sentence types to use
304
4.06k
    static const char *nmea_types[] = {
305
4.06k
        "GPGGA", "GPRMC", "GPGLL", "GPGSA", "GPGSV", "GPVTG", "GPZDA",
306
4.06k
        "GPGBS", "GPGST", "GPGRS", "GNGNS", "GPDTM", "GPTXT",
307
4.06k
        "GLGSA", "GLGSV", "GAGSA", "GAGSV", "GBGSA", "GBGSV",
308
4.06k
        "HCHDG", "SDDBT", "SDDPT", "WIMWV", "GPROT", "GPTHS"
309
4.06k
    };
310
4.06k
    static const size_t num_types = sizeof(nmea_types) / sizeof(nmea_types[0]);
311
312
4.06k
    if (len < 1) return 0;
313
314
    // Use first byte to select sentence type
315
4.06k
    size_t type_idx = data[0] % num_types;
316
4.06k
    const char *sentence_type = nmea_types[type_idx];
317
318
    // Build the sentence body from remaining data
319
4.06k
    out[0] = '$';
320
4.06k
    size_t pos = 1;
321
322
    // Copy sentence type
323
4.06k
    size_t type_len = strlen(sentence_type);
324
4.06k
    memcpy(out + pos, sentence_type, type_len);
325
4.06k
    pos += type_len;
326
327
    // Add comma-separated fields from fuzzer data
328
147k
    for (size_t i = 1; i < len && pos < kMaxInputLength - 10; i++) {
329
143k
        if (data[i] == '\r' || data[i] == '\n' || data[i] == '*') {
330
2.52k
            out[pos++] = ',';  // Replace invalid chars with comma
331
141k
        } else if (data[i] >= 32 && data[i] < 127) {
332
90.1k
            out[pos++] = data[i];
333
90.1k
        } else {
334
50.9k
            out[pos++] = ',';
335
50.9k
        }
336
143k
    }
337
338
    // Compute checksum
339
4.06k
    uint8_t cs = nmea_checksum(out + 1, pos - 1);
340
341
    // Add checksum and line ending
342
4.06k
    out[pos++] = '*';
343
4.06k
    static const char hex[] = "0123456789ABCDEF";
344
4.06k
    out[pos++] = hex[(cs >> 4) & 0x0F];
345
4.06k
    out[pos++] = hex[cs & 0x0F];
346
4.06k
    out[pos++] = '\r';
347
4.06k
    out[pos++] = '\n';
348
349
4.06k
    return pos;
350
4.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
1.15k
{
356
1.15k
    static const uint32_t crc24q_table[256] = {
357
1.15k
        0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC, 0x9F7F17,
358
1.15k
        0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5, 0x3EFE2E,
359
1.15k
        0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65, 0x5A319E,
360
1.15k
        0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C, 0xFBB0A7,
361
1.15k
        0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205, 0x93AEFE,
362
1.15k
        0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, 0x322FC7,
363
1.15k
        0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, 0x56E077,
364
1.15k
        0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5, 0xF7614E,
365
1.15k
        0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E, 0x86DCC5,
366
1.15k
        0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107, 0x275DFC,
367
1.15k
        0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7, 0x43924C,
368
1.15k
        0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, 0xE21375,
369
1.15k
        0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, 0x8A0D2C,
370
1.15k
        0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, 0x2B8C15,
371
1.15k
        0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E, 0x4F43A5,
372
1.15k
        0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67, 0xEEC29C,
373
1.15k
        0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448, 0xAC38B3,
374
1.15k
        0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571, 0x0DB98A,
375
1.15k
        0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, 0x69763A,
376
1.15k
        0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, 0xC8F703,
377
1.15k
        0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, 0xA0E95A,
378
1.15k
        0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498, 0x016863,
379
1.15k
        0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28, 0x65A7D3,
380
1.15k
        0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11, 0xC426EA,
381
1.15k
        0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A, 0xB59B61,
382
1.15k
        0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, 0x141A58,
383
1.15k
        0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, 0x70D5E8,
384
1.15k
        0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, 0xD154D1,
385
1.15k
        0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673, 0xB94A88,
386
1.15k
        0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A, 0x18CBB1,
387
1.15k
        0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA, 0x7C0401,
388
1.15k
        0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3, 0xDD8538
389
1.15k
    };
390
391
1.15k
    uint32_t crc = 0;
392
66.6k
    for (size_t i = 0; i < len; i++) {
393
65.5k
        crc = ((crc << 8) & 0xFFFFFF) ^ crc24q_table[(crc >> 16) ^ data[i]];
394
65.5k
    }
395
1.15k
    return crc;
396
1.15k
}
397
398
static size_t build_rtcm3_packet(const uint8_t *data, size_t len, uint8_t *out)
399
1.15k
{
400
1.15k
    if (len > 1023) len = 1023;  // RTCM3 max payload
401
402
1.15k
    out[0] = 0xD3;
403
1.15k
    out[1] = (uint8_t)((len >> 8) & 0x03);  // High 2 bits of length
404
1.15k
    out[2] = (uint8_t)(len & 0xFF);          // Low 8 bits
405
406
1.15k
    memcpy(out + 3, data, len);
407
408
    // CRC-24Q covers preamble + length + data
409
1.15k
    uint32_t crc = crc24q_hash(out, 3 + len);
410
1.15k
    out[3 + len] = (uint8_t)((crc >> 16) & 0xFF);
411
1.15k
    out[4 + len] = (uint8_t)((crc >> 8) & 0xFF);
412
1.15k
    out[5 + len] = (uint8_t)(crc & 0xFF);
413
414
1.15k
    return 6 + len;
415
1.15k
}
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
8.52k
{
420
8.52k
    if (len < 1) return 0;
421
422
8.52k
    uint8_t msg_id = data[0];
423
8.52k
    size_t pos = 0;
424
425
8.52k
    out[pos++] = 0x10;  // DLE
426
8.52k
    out[pos++] = msg_id;
427
428
    // Copy payload with DLE stuffing
429
5.91M
    for (size_t i = 1; i < len && pos < kMaxInputLength - 4; i++) {
430
5.90M
        if (data[i] == 0x10) {
431
48.4k
            out[pos++] = 0x10;  // Stuff DLE
432
48.4k
        }
433
5.90M
        out[pos++] = data[i];
434
5.90M
    }
435
436
8.52k
    out[pos++] = 0x10;  // DLE
437
8.52k
    out[pos++] = 0x03;  // ETX
438
439
8.52k
    return pos;
440
8.52k
}
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
2.09k
{
445
2.09k
    if (len < 2) return 0;
446
2.09k
    if (len > 200) len = 200;
447
448
    // Standard GREIS message IDs
449
2.09k
    static const char *greis_ids[] = {"RE", "ER", "PM", "RC", "RD", "SI", "EL"};
450
2.09k
    static const size_t num_ids = sizeof(greis_ids) / sizeof(greis_ids[0]);
451
452
2.09k
    size_t id_idx = data[0] % num_ids;
453
2.09k
    out[0] = greis_ids[id_idx][0];
454
2.09k
    out[1] = greis_ids[id_idx][1];
455
456
2.09k
    size_t payload_len = len - 2;
457
2.09k
    if (payload_len > 0) {
458
2.08k
        memcpy(out + 2, data + 2, payload_len);
459
2.08k
    }
460
461
    // Simple XOR checksum for GREIS
462
2.09k
    uint8_t cs = 0;
463
80.9k
    for (size_t i = 0; i < 2 + payload_len; i++) {
464
78.9k
        cs ^= out[i];
465
78.9k
    }
466
2.09k
    out[2 + payload_len] = cs;
467
2.09k
    out[3 + payload_len] = '\r';
468
2.09k
    out[4 + payload_len] = '\n';
469
470
2.09k
    return 5 + payload_len;
471
2.09k
}
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
532
{
476
532
    if (len > 1000) len = 1000;
477
478
532
    out[0] = 0xA0;
479
532
    out[1] = 0xA1;
480
532
    out[2] = (uint8_t)((len >> 8) & 0xFF);
481
532
    out[3] = (uint8_t)(len & 0xFF);
482
483
532
    memcpy(out + 4, data, len);
484
485
532
    uint8_t cs = skytraq_checksum(data, len);
486
532
    out[4 + len] = cs;
487
532
    out[5 + len] = 0x0D;
488
532
    out[6 + len] = 0x0A;
489
490
532
    return 7 + len;
491
532
}
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
26.2k
{
531
26.2k
    unsigned char buf[4096];
532
26.2k
    while (read(fd, buf, sizeof(buf)) > 0) {
533
        // Keep reading until empty
534
0
    }
535
26.2k
}
536
537
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
538
26.2k
{
539
26.2k
    if (Size < kMinInputLength || Size > kMaxInputLength) {
540
12
        return 0;
541
12
    }
542
543
    // Drain any leftover data from previous iteration
544
26.2k
    drain_pipe(pipe_fds[0]);
545
546
    // Set fd before gpsd_clear so pps_thread gets correct value
547
26.2k
    session.gpsdata.gps_fd = pipe_fds[0];
548
549
26.2k
    gpsd_clear(&session);
550
26.2k
    session.device_type = NULL;
551
26.2k
    session.last_controller = NULL;
552
553
    // Ensure sourcetype stays set to prevent NTP/PPS activation
554
26.2k
    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
26.2k
    session.gpsdata.satellites_visible = 0;
559
26.2k
    gpsd_zero_satellites(&session.gpsdata);
560
561
    // First byte selects protocol type
562
26.2k
    uint8_t proto_type = Data[0] % PROTO_COUNT;
563
564
    // Second byte provides additional control
565
26.2k
    context.readonly = (Data[1] & 0x01);
566
26.2k
    context.passive = (Data[1] & 0x02);
567
568
    // Build protocol-specific packet from remaining data
569
26.2k
    size_t packet_len = 0;
570
26.2k
    const uint8_t *payload = Data + 2;
571
26.2k
    size_t payload_len = Size - 2;
572
573
26.2k
    switch (proto_type) {
574
648
    case PROTO_SIRF:
575
648
        packet_len = build_sirf_packet(payload, payload_len, packet_buf);
576
648
        break;
577
4.10k
    case PROTO_UBX:
578
4.10k
        packet_len = build_ubx_packet(payload, payload_len, packet_buf);
579
4.10k
        break;
580
196
    case PROTO_ZODIAC:
581
196
        packet_len = build_zodiac_packet(payload, payload_len, packet_buf);
582
196
        break;
583
264
    case PROTO_GEOSTAR:
584
264
        packet_len = build_geostar_packet(payload, payload_len, packet_buf);
585
264
        break;
586
4.64k
    case PROTO_NAVCOM:
587
4.64k
        packet_len = build_navcom_packet(payload, payload_len, packet_buf);
588
4.64k
        break;
589
4.06k
    case PROTO_NMEA:
590
4.06k
        packet_len = build_nmea_packet(payload, payload_len, packet_buf);
591
4.06k
        break;
592
1.15k
    case PROTO_RTCM3:
593
1.15k
        packet_len = build_rtcm3_packet(payload, payload_len, packet_buf);
594
1.15k
        break;
595
8.52k
    case PROTO_TSIP:
596
8.52k
        packet_len = build_tsip_packet(payload, payload_len, packet_buf);
597
8.52k
        break;
598
2.09k
    case PROTO_GREIS:
599
2.09k
        packet_len = build_greis_packet(payload, payload_len, packet_buf);
600
2.09k
        break;
601
532
    case PROTO_SKYTRAQ:
602
532
        packet_len = build_skytraq_packet(payload, payload_len, packet_buf);
603
532
        break;
604
0
    default:
605
0
        return 0;
606
26.2k
    }
607
608
26.2k
    if (packet_len == 0) {
609
0
        return 0;
610
0
    }
611
612
    // Write constructed packet to pipe
613
26.2k
    ssize_t written = write(pipe_fds[1], packet_buf, packet_len);
614
26.2k
    if (written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
615
0
        return 0;
616
0
    }
617
618
    // Process packets
619
26.2k
    int max_iterations = 100;
620
2.64M
    while (max_iterations-- > 0) {
621
2.62M
        gps_mask_t changed = gpsd_poll(&session);
622
2.62M
        if (changed == 0 || changed == ERROR_SET) {
623
0
            break;
624
0
        }
625
2.62M
    }
626
627
26.2k
    return 0;
628
26.2k
}