Coverage Report

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