Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/tests/fuzz/fuzz_applayerparserparse.c
Line
Count
Source
1
/**
2
 * @file
3
 * @author Philippe Antoine <contact@catenacyber.fr>
4
 * fuzz target for AppLayerParserParse
5
 */
6
7
#include "suricata-common.h"
8
#include "suricata.h"
9
#include "app-layer-detect-proto.h"
10
#include "flow-util.h"
11
#include "app-layer-parser.h"
12
#include "util-unittest-helper.h"
13
#include "util-byte.h"
14
#include "conf-yaml-loader.h"
15
#include "util-conf.h"
16
17
640k
#define HEADER_LEN 6
18
19
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
20
int LLVMFuzzerInitialize(int *argc, char ***argv);
21
22
AppLayerParserThreadCtx *alp_tctx = NULL;
23
24
extern const char *configNoChecksum;
25
26
/* input buffer is structured this way :
27
 * 6 bytes header,
28
 * then sequence of buffers separated by magic bytes 01 D5 CA 7A */
29
30
/* The 6 bytes header is
31
 * alproto
32
 * proto
33
 * source port (uint16_t)
34
 * destination port (uint16_t) */
35
36
const uint8_t separator[] = {0x01, 0xD5, 0xCA, 0x7A};
37
SCInstance surifuzz;
38
AppProto forceLayer = 0;
39
SC_ATOMIC_EXTERN(unsigned int, engine_stage);
40
41
int LLVMFuzzerInitialize(int *argc, char ***argv)
42
60
{
43
60
    char *target_suffix = strrchr((*argv)[0], '_');
44
60
    if (target_suffix != NULL) {
45
60
        AppProto applayer = StringToAppProto(target_suffix + 1);
46
60
        if (applayer != ALPROTO_UNKNOWN) {
47
58
            forceLayer = applayer;
48
58
            printf("Forcing %s=%" PRIu16 "\n", AppProtoToString(forceLayer), forceLayer);
49
58
            return 0;
50
58
        }
51
60
    }
52
    // else
53
2
    const char *forceLayerStr = getenv("FUZZ_APPLAYER");
54
2
    if (forceLayerStr) {
55
0
        if (ByteExtractStringUint16(&forceLayer, 10, 0, forceLayerStr) < 0) {
56
0
            forceLayer = 0;
57
0
            printf("Invalid numeric value for FUZZ_APPLAYER environment variable");
58
0
        } else {
59
0
            printf("Forcing %s\n", AppProtoToString(forceLayer));
60
0
        }
61
0
    }
62
    // http is the output name, but we want to fuzz HTTP1
63
2
    if (forceLayer == ALPROTO_HTTP) {
64
0
        forceLayer = ALPROTO_HTTP1;
65
0
    }
66
2
    return 0;
67
60
}
68
69
// arbitrary value
70
#define ALPROTO_MAXTX 4096
71
72
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
73
213k
{
74
213k
    Flow * f;
75
213k
    TcpSession ssn;
76
213k
    const uint8_t * albuffer;
77
213k
    uint8_t * alnext;
78
213k
    size_t alsize;
79
    // used to find under and overflows
80
    // otherwise overflows do not fail as they read the next packet
81
213k
    uint8_t * isolatedBuffer;
82
83
213k
    if (alp_tctx == NULL) {
84
        //Redirects logs to /dev/null
85
30
        setenv("SC_LOG_OP_IFACE", "file", 0);
86
30
        setenv("SC_LOG_FILE", "/dev/null", 0);
87
88
30
        InitGlobal();
89
30
        run_mode = RUNMODE_PCAP_FILE;
90
30
        GlobalsInitPreConfig();
91
92
        //redirect logs to /tmp
93
30
        ConfigSetLogDirectory("/tmp/");
94
        // disables checksums validation for fuzzing
95
30
        if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
96
0
            abort();
97
0
        }
98
99
30
        PostConfLoadedSetup(&surifuzz);
100
30
        alp_tctx = AppLayerParserThreadCtxAlloc();
101
30
        SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
102
30
    }
103
104
213k
    if (size < HEADER_LEN) {
105
90
        return 0;
106
90
    }
107
108
213k
    if (data[0] >= ALPROTO_MAX) {
109
105
        return 0;
110
105
    }
111
    //no UTHBuildFlow to have storage
112
213k
    f = FlowAlloc();
113
213k
    if (f == NULL) {
114
0
        return 0;
115
0
    }
116
213k
    f->flags |= FLOW_IPV4 | FLOW_SGH_TOCLIENT | FLOW_SGH_TOSERVER;
117
213k
    f->src.addr_data32[0] = 0x01020304;
118
213k
    f->dst.addr_data32[0] = 0x05060708;
119
213k
    f->sp = (uint16_t)((data[2] << 8) | data[3]);
120
213k
    f->dp = (uint16_t)((data[4] << 8) | data[5]);
121
213k
    f->proto = data[1];
122
213k
    memset(&ssn, 0, sizeof(TcpSession));
123
213k
    f->protoctx = &ssn;
124
213k
    f->protomap = FlowGetProtoMapping(f->proto);
125
213k
    if (forceLayer > 0) {
126
165k
        f->alproto = forceLayer;
127
165k
    } else {
128
48.2k
        f->alproto = data[0];
129
48.2k
    }
130
131
213k
    FLOWLOCK_WRLOCK(f);
132
    /*
133
     * We want to fuzz multiple calls to AppLayerParserParse
134
     * because some parts of the code are only reached after
135
     * multiple packets (in SMTP for example).
136
     * So we treat our input as a list of buffers with magic separator.
137
     */
138
213k
    albuffer = data + HEADER_LEN;
139
213k
    alsize = size - HEADER_LEN;
140
213k
    uint8_t flags = STREAM_START;
141
213k
    int flip = 0;
142
213k
    alnext = memmem(albuffer, alsize, separator, 4);
143
16.0M
    while (alnext) {
144
15.8M
        if (flip) {
145
7.89M
            flags |= STREAM_TOCLIENT;
146
7.89M
            flags &= ~(STREAM_TOSERVER);
147
7.89M
            flip = 0;
148
7.96M
        } else {
149
7.96M
            flags |= STREAM_TOSERVER;
150
7.96M
            flags &= ~(STREAM_TOCLIENT);
151
7.96M
            flip = 1;
152
7.96M
        }
153
154
15.8M
        if (alnext != albuffer) {
155
            // only if we have some data
156
14.6M
            isolatedBuffer = malloc(alnext - albuffer);
157
14.6M
            if (isolatedBuffer == NULL) {
158
0
                goto bail;
159
0
            }
160
14.6M
            memcpy(isolatedBuffer, albuffer, alnext - albuffer);
161
14.6M
            (void) AppLayerParserParse(NULL, alp_tctx, f, f->alproto, flags, isolatedBuffer, alnext - albuffer);
162
14.6M
            free(isolatedBuffer);
163
14.6M
            if (FlowChangeProto(f)) {
164
                // exits if a protocol change is requested
165
2
                alsize = 0;
166
2
                break;
167
2
            }
168
14.6M
            flags &= ~(STREAM_START);
169
14.6M
            if (f->alparser &&
170
14.4M
                   (((flags & STREAM_TOSERVER) != 0 &&
171
7.47M
                     AppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_EOF_TS)) ||
172
14.4M
                    ((flags & STREAM_TOCLIENT) != 0 &&
173
6.93M
                     AppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_EOF_TC)))) {
174
                //no final chunk
175
6.16k
                alsize = 0;
176
6.16k
                break;
177
6.16k
            }
178
179
14.5M
            AppLayerParserTransactionsCleanup(f, flags & (STREAM_TOSERVER | STREAM_TOCLIENT));
180
14.5M
        }
181
15.8M
        alsize -= alnext - albuffer + 4;
182
15.8M
        albuffer = alnext + 4;
183
15.8M
        if (alsize == 0) {
184
4.15k
            break;
185
4.15k
        }
186
15.8M
        alnext = memmem(albuffer, alsize, separator, 4);
187
15.8M
    }
188
213k
    if (alsize > 0 ) {
189
202k
        if (flip) {
190
63.1k
            flags |= STREAM_TOCLIENT;
191
63.1k
            flags &= ~(STREAM_TOSERVER);
192
63.1k
            flip = 0;
193
139k
        } else {
194
139k
            flags |= STREAM_TOSERVER;
195
139k
            flags &= ~(STREAM_TOCLIENT);
196
139k
            flip = 1;
197
139k
        }
198
202k
        flags |= STREAM_EOF;
199
202k
        isolatedBuffer = malloc(alsize);
200
202k
        if (isolatedBuffer == NULL) {
201
0
            goto bail;
202
0
        }
203
202k
        memcpy(isolatedBuffer, albuffer, alsize);
204
202k
        (void) AppLayerParserParse(NULL, alp_tctx, f, f->alproto, flags, isolatedBuffer, alsize);
205
202k
        free(isolatedBuffer);
206
202k
    }
207
208
213k
bail:
209
213k
    FLOWLOCK_UNLOCK(f);
210
213k
    FlowFree(f);
211
212
213k
    return 0;
213
213k
}