/src/gpsd/fuzzer/FuzzDrivers.c
Line | Count | Source |
1 | | /* Copyright 2026 Google LLC |
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 | | #include "gpsd_config.h" |
14 | | |
15 | | #include <errno.h> |
16 | | #include <fcntl.h> |
17 | | #include <stdint.h> |
18 | | #include <stdio.h> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | #include <sys/socket.h> |
22 | | #include <sys/types.h> |
23 | | #include <unistd.h> |
24 | | |
25 | | #include "gpsd.h" |
26 | | |
27 | 43.3k | #define kMinInputLength 3 |
28 | 21.6k | #define kMaxInputLength 16384 |
29 | | |
30 | | // Global session for reuse across fuzzer iterations |
31 | | static struct gps_device_t session; |
32 | | static struct gps_context_t context; |
33 | | static int pipe_fds[2] = {-1, -1}; |
34 | | |
35 | | // Minimal error output callback (suppress all output) |
36 | | static void null_errout(const char *s) |
37 | 10.3k | { |
38 | 10.3k | (void)s; |
39 | | // Suppress all debug output during fuzzing |
40 | 10.3k | } |
41 | | |
42 | | // Initialize fuzzer - called once before fuzzing starts |
43 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
44 | 2 | { |
45 | | // Create a pipe for feeding fuzzer data to gpsd_poll() |
46 | 2 | if (pipe(pipe_fds) < 0) { |
47 | 0 | return -1; |
48 | 0 | } |
49 | | |
50 | | // Set read end to non-blocking (packet_get1 expects non-blocking) |
51 | 2 | int flags = fcntl(pipe_fds[0], F_GETFL, 0); |
52 | 2 | if (flags < 0 || fcntl(pipe_fds[0], F_SETFL, flags | O_NONBLOCK) < 0) { |
53 | 0 | close(pipe_fds[0]); |
54 | 0 | close(pipe_fds[1]); |
55 | 0 | return -1; |
56 | 0 | } |
57 | | |
58 | | // Use PRODUCTION initialization functions |
59 | 2 | gps_context_init(&context, "fuzz"); |
60 | 2 | gpsd_init(&session, &context, "/dev/fuzz"); |
61 | | |
62 | | // Override error output to suppress logs |
63 | 2 | context.errout.debug = 0; |
64 | 2 | context.errout.report = null_errout; |
65 | | |
66 | | // Set file descriptor to our pipe |
67 | 2 | session.gpsdata.gps_fd = pipe_fds[0]; |
68 | | |
69 | 2 | return 0; |
70 | 2 | } |
71 | | |
72 | | // Main fuzzing entry point - uses PRODUCTION gpsd_poll() directly |
73 | | int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) |
74 | 21.6k | { |
75 | 21.6k | if (Size < kMinInputLength || Size > kMaxInputLength) { |
76 | 8 | return 0; |
77 | 8 | } |
78 | | |
79 | | // Clear driver state between iterations for stateless fuzzing |
80 | | // Note: gpsd_clear() intentionally preserves device_type and last_controller |
81 | | // because production gpsd_activate() needs them after clearing driver data. |
82 | | // For stateless fuzzing, we must clear these manually to avoid dangling pointers. |
83 | 21.6k | gpsd_clear(&session); |
84 | 21.6k | session.device_type = NULL; |
85 | 21.6k | session.last_controller = NULL; |
86 | | |
87 | | // Restore file descriptor (gpsd_clear resets some fields) |
88 | 21.6k | session.gpsdata.gps_fd = pipe_fds[0]; |
89 | | |
90 | | // Randomize readonly/passive |
91 | 21.6k | context.readonly = (Data[0] & 0x01); |
92 | 21.6k | context.passive = (Data[1] & 0x01); |
93 | | |
94 | | // Write fuzzer data to pipe |
95 | 21.6k | ssize_t written = write(pipe_fds[1], Data + 2, Size - 2); |
96 | 21.6k | if (written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { |
97 | 0 | return 0; |
98 | 0 | } |
99 | | |
100 | | // Call PRODUCTION entry point repeatedly to process all packets |
101 | | // First call: device_type=NULL → packet_get1() → auto-detection |
102 | | // Subsequent calls: device_type=SET → device_type->get_packet() → driver-specific |
103 | | // This tests both code paths within a single stateless iteration |
104 | 21.6k | int max_iterations = 100; // Prevent infinite loops |
105 | 2.19M | while (max_iterations-- > 0) { |
106 | 2.16M | gps_mask_t changed = gpsd_poll(&session); |
107 | | // Stop if no more data available or error |
108 | 2.16M | if (changed == 0 || changed == ERROR_SET) { |
109 | 0 | break; |
110 | 0 | } |
111 | 2.16M | } |
112 | | |
113 | 21.6k | return 0; |
114 | 21.6k | } |