Coverage Report

Created: 2026-09-01 06:30

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