Coverage Report

Created: 2026-07-16 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}