Coverage Report

Created: 2026-09-01 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tcpreplay/test/fuzz/fuzz_common.h
Line
Count
Source
1
/*
2
 *   Copyright (c) 2026 Fred Klassen <tcpreplay.dev at gmail dot com> - AppNeta by Broadcom
3
 *
4
 *   The Tcpreplay Suite of tools is free software: you can redistribute it
5
 *   and/or modify it under the terms of the GNU General Public License as
6
 *   published by the Free Software Foundation, either version 3 of the
7
 *   License, or with the authors permission any later version.
8
 *
9
 *   The Tcpreplay Suite is distributed in the hope that it will be useful,
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *   GNU General Public License for more details.
13
 *
14
 *   You should have received a copy of the GNU General Public License
15
 *   along with the Tcpreplay Suite.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
/*
19
 * Shared plumbing for the fuzz targets.
20
 *
21
 * Each target defines LLVMFuzzerTestOneInput() and nothing else. Two ways to
22
 * run them:
23
 *
24
 *   - under libFuzzer (clang -fsanitize=fuzzer,address), for actual fuzzing
25
 *     and for OSS-Fuzz;
26
 *   - as a plain program taking files on the command line, built by any C
27
 *     compiler, which is how the checked-in corpus gets replayed as a
28
 *     regression test with no fuzzing toolchain present.
29
 *
30
 * The second mode is the reason FUZZ_STANDALONE exists. It keeps the targets
31
 * useful on the platforms this project supports where clang and libFuzzer are
32
 * not a given, and it means a crashing input found by OSS-Fuzz can be checked
33
 * in and re-run by anyone.
34
 */
35
36
#pragma once
37
38
#include <stdint.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <string.h>
42
#include <fcntl.h>
43
#include <unistd.h>
44
45
#define FUZZ_PATH_MAX 128
46
47
/*
48
 * Several code paths under test print to stdout - fragroute's "print" module
49
 * dumps every packet it sees. At fuzzing rates that is gigabytes of noise and
50
 * a hard throughput ceiling, so send stdout to /dev/null once, on first use.
51
 * stderr is left alone: that is where the sanitizers report.
52
 */
53
static inline void
54
fuzz_quiet_stdout(void)
55
0
{
56
0
    static int done;
57
0
    int devnull;
58
0
59
0
    if (done)
60
0
        return;
61
0
    done = 1;
62
0
63
0
    if ((devnull = open("/dev/null", O_WRONLY)) >= 0) {
64
0
        dup2(devnull, STDOUT_FILENO);
65
0
        if (devnull != STDOUT_FILENO)
66
0
            close(devnull);
67
0
    }
68
0
}
69
70
/* libFuzzer's entry point; provided by each target */
71
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
72
73
/*
74
 * Several of the interfaces under test take a *filename* rather than a buffer
75
 * - parse_services() and fragroute_init() both read a file off disk. Writing
76
 * the input out is the only honest way to exercise them as the tools do.
77
 *
78
 * Returns 0 on success and fills "path".
79
 */
80
static inline int
81
fuzz_write_tempfile(const uint8_t *data, size_t size, char *path, size_t pathlen)
82
80
{
83
80
    int fd;
84
80
    ssize_t written;
85
86
80
    if (pathlen < sizeof("/tmp/tcpr-fuzz-XXXXXX"))
87
0
        return -1;
88
89
80
    memcpy(path, "/tmp/tcpr-fuzz-XXXXXX", sizeof("/tmp/tcpr-fuzz-XXXXXX"));
90
91
80
    if ((fd = mkstemp(path)) < 0)
92
0
        return -1;
93
94
80
    written = write(fd, data, size);
95
80
    close(fd);
96
97
80
    if (written < 0 || (size_t)written != size) {
98
0
        unlink(path);
99
0
        return -1;
100
0
    }
101
102
80
    return 0;
103
80
}
104
105
#ifdef FUZZ_STANDALONE
106
/*
107
 * Corpus replay driver. Each argument is a file fed to the target once, so
108
 * `fuzz_services corpus/services/*` re-runs the whole corpus. Under a
109
 * sanitizer build this is a regression test; without one it still catches
110
 * anything that crashes outright.
111
 */
112
int
113
main(int argc, char **argv)
114
{
115
    int i;
116
117
    for (i = 1; i < argc; i++) {
118
        FILE *fp = fopen(argv[i], "rb");
119
        long len;
120
        uint8_t *buf;
121
122
        if (fp == NULL) {
123
            fprintf(stderr, "%s: cannot open\n", argv[i]);
124
            return 1;
125
        }
126
127
        if (fseek(fp, 0, SEEK_END) != 0 || (len = ftell(fp)) < 0) {
128
            fclose(fp);
129
            fprintf(stderr, "%s: cannot size\n", argv[i]);
130
            return 1;
131
        }
132
        rewind(fp);
133
134
        if ((buf = malloc((size_t)len ? (size_t)len : 1)) == NULL) {
135
            fclose(fp);
136
            return 1;
137
        }
138
139
        if (len > 0 && fread(buf, 1, (size_t)len, fp) != (size_t)len) {
140
            free(buf);
141
            fclose(fp);
142
            fprintf(stderr, "%s: short read\n", argv[i]);
143
            return 1;
144
        }
145
        fclose(fp);
146
147
        LLVMFuzzerTestOneInput(buf, (size_t)len);
148
        free(buf);
149
150
        printf("ok - %s\n", argv[i]);
151
    }
152
153
    return 0;
154
}
155
#endif /* FUZZ_STANDALONE */