Coverage Report

Created: 2026-07-16 06:23

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