Coverage Report

Created: 2026-02-21 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ndpi/fuzz/fuzz_ndpi_reader_payload_analyzer.c
Line
Count
Source
1
#include "reader_util.h"
2
#include "ndpi_api.h"
3
#include "fuzz_common_code.h"
4
5
#include <pcap/pcap.h>
6
7
#include <errno.h>
8
#include <stdint.h>
9
#include <stdio.h>
10
#include <assert.h>
11
#include <libgen.h>
12
13
#ifdef ENABLE_PCAP_L7_MUTATOR
14
#include "pl7m.h"
15
#endif
16
17
#ifdef ENABLE_NALLOC
18
#include "nallocinc.c"
19
#endif
20
21
struct ndpi_workflow_prefs *prefs = NULL;
22
struct ndpi_workflow *workflow = NULL;
23
struct ndpi_global_context *g_ctx;
24
25
u_int8_t enable_payload_analyzer = 0;
26
u_int8_t enable_flow_stats = 1;
27
u_int8_t human_readeable_string_len = 5;
28
u_int8_t max_num_udp_dissected_pkts = 0, max_num_tcp_dissected_pkts = 0; /* Disable limits at application layer */;
29
int malloc_size_stats = 0;
30
FILE *fingerprint_fp = NULL;
31
char *addr_dump_path = NULL;
32
int monitoring_enabled = 1;
33
u_int8_t enable_doh_dot_detection = 0;
34
35
static char *path = NULL;
36
37
extern void ndpi_report_payload_stats(FILE *out);
38
39
#ifdef CRYPT_FORCE_NO_AESNI
40
extern int force_no_aesni;
41
#endif
42
43
#ifdef ENABLE_PCAP_L7_MUTATOR
44
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
45
                               size_t MaxSize, unsigned int Seed) {
46
  return pl7m_mutator(Data, Size, MaxSize, Seed);
47
}
48
#endif
49
50
36
int LLVMFuzzerInitialize(int *argc, char ***argv) {
51
36
  (void)argc;
52
53
36
  path = dirname(strdup(*argv[0])); /* No errors; no free! */
54
36
  return 0;
55
36
}
56
57
467k
static void node_cleanup_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) {
58
467k
  struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node;
59
60
467k
  (void)depth;
61
467k
  (void)user_data;
62
63
467k
  if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */
64
225k
    if((!flow->detection_completed) && flow->ndpi_flow) {
65
172k
      flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct,
66
172k
                                                      flow->ndpi_flow);
67
172k
    }
68
69
225k
    process_ndpi_collected_info(workflow, flow);
70
225k
  }
71
467k
}
72
73
static void fn_flow_callback(struct ndpi_workflow *w, struct ndpi_flow_info *f, void *d)
74
53.2k
{
75
53.2k
  (void)w;
76
53.2k
  (void)f;
77
53.2k
  (void)d;
78
53.2k
}
79
80
256k
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
81
256k
  pcap_t * pkts;
82
256k
  const u_char *pkt;
83
256k
  struct pcap_pkthdr *header;
84
256k
  int r;
85
256k
  char errbuf[PCAP_ERRBUF_SIZE];
86
256k
  u_int i;
87
256k
  FILE *fd;
88
256k
  char name[256];
89
90
256k
  if (prefs == NULL) {
91
9
    prefs = calloc(sizeof(struct ndpi_workflow_prefs), 1); /* No failure here */
92
9
    prefs->decode_tunnels = 1;
93
9
    prefs->num_roots = 16;
94
9
    prefs->max_ndpi_flows = 16 * 1024 * 1024;
95
9
    prefs->quiet_mode = 0;
96
97
9
#ifdef ENABLE_MEM_ALLOC_FAILURES
98
9
    fuzz_set_alloc_callbacks();
99
9
#endif
100
101
9
    g_ctx = ndpi_global_init();
102
103
9
    workflow = ndpi_workflow_init(prefs, NULL /* pcap handler will be set later */, 0, ndpi_serialization_format_json, g_ctx);
104
105
9
    ndpi_workflow_set_flow_callback(workflow, fn_flow_callback, NULL);
106
107
9
    ndpi_set_config(workflow->ndpi_struct, NULL, "log.level", "3");
108
9
    ndpi_set_config(workflow->ndpi_struct, "all", "log", "1");
109
110
9
    sprintf(name, "%s/public_suffix_list.dat", path);
111
9
    assert(ndpi_load_domain_suffixes(workflow->ndpi_struct, name) >= 0);
112
9
    sprintf(name, "%s/lists/", path);
113
9
    assert(ndpi_load_categories_dir(workflow->ndpi_struct, name) >= 0);
114
9
    sprintf(name, "%s/lists/protocols/", path);
115
9
    assert(ndpi_load_protocols_dir(workflow->ndpi_struct, name) >= 0);
116
9
    sprintf(name, "%s/protos.txt", path);
117
9
    assert(ndpi_load_protocols_file(workflow->ndpi_struct, name) >= 0);
118
9
    sprintf(name, "%s/categories.txt", path);
119
9
    assert(ndpi_load_categories_file(workflow->ndpi_struct, name, NULL) >= 0);
120
9
    sprintf(name, "%s/risky_domains.txt", path);
121
9
    assert(ndpi_load_risk_domain_file(workflow->ndpi_struct, name) >= 0);
122
9
    sprintf(name, "%s/ja4_fingerprints.csv", path);
123
9
    assert(ndpi_load_malicious_ja4_file(workflow->ndpi_struct, name) >= 0);
124
9
    sprintf(name, "%s/tcp_fingerprints.csv", path);
125
9
    assert(ndpi_load_tcp_fingerprint_file(workflow->ndpi_struct, name) >= 0);
126
9
    sprintf(name, "%s/sha1_fingerprints.csv", path);
127
9
    assert(ndpi_load_malicious_sha1_file(workflow->ndpi_struct, name) >= 0);
128
9
    sprintf(name, "%s", path);
129
9
    assert(ndpi_load_protocol_plugins(workflow->ndpi_struct, name) >= 0); /* Plugins are not really used while fuzzing, yet */
130
131
#ifdef ENABLE_ONLY_SUBCLASSIFICATION
132
    sprintf(name, "%s/config_only_classification.txt", path);
133
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "filename.config", name) == NDPI_CFG_OK);
134
#else
135
136
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "packets_limit_per_flow", "255") == NDPI_CFG_OK);
137
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "flow.track_payload", "1") == NDPI_CFG_OK);
138
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "tcp_ack_payload_heuristic", "1") == NDPI_CFG_OK);
139
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "fully_encrypted_heuristic", "1") == NDPI_CFG_OK);
140
9
    assert(ndpi_set_config(workflow->ndpi_struct, "dns", "subclassification", "1") == NDPI_CFG_OK);
141
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "application_blocks_tracking", "1") == NDPI_CFG_OK);
142
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "max_num_blocks_to_analyze", "8") == NDPI_CFG_OK);
143
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "tls_blocks_show_timing", "0") == NDPI_CFG_OK);
144
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_ignore_ephemeral_tls_extn", "1") == NDPI_CFG_OK);
145
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ndpifp_ignore_sni_tls_extn", "1") == NDPI_CFG_OK);
146
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_data", "1") == NDPI_CFG_OK);
147
9
    assert(ndpi_set_config(workflow->ndpi_struct, "ssh", "metadata.ssh_data", "1") == NDPI_CFG_OK);
148
9
#ifndef ENABLE_CONFIG2
149
9
    assert(ndpi_set_config(workflow->ndpi_struct, "stun", "max_packets_extra_dissection", "40") == NDPI_CFG_OK);
150
9
    assert(ndpi_set_config(workflow->ndpi_struct, "zoom", "max_packets_extra_dissection", "255") == NDPI_CFG_OK);
151
9
    assert(ndpi_set_config(workflow->ndpi_struct, "rtp", "search_for_stun", "1") == NDPI_CFG_OK);
152
9
#endif
153
9
    assert(ndpi_set_config(workflow->ndpi_struct, "openvpn", "dpi.heuristics", "0x01") == NDPI_CFG_OK);
154
9
    assert(ndpi_set_config(workflow->ndpi_struct, "openvpn", "dpi.heuristics.num_messages", "20") == NDPI_CFG_OK);
155
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja4r_fingerprint", "1") == NDPI_CFG_OK);
156
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "dpi.heuristics", "0x07") == NDPI_CFG_OK);
157
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "dpi.heuristics.max_packets_extra_dissection", "40") == NDPI_CFG_OK);
158
9
    assert(ndpi_set_config(workflow->ndpi_struct, "all", "monitoring", "1") == NDPI_CFG_OK);
159
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "dpi.address_cache_size", "8192") == NDPI_CFG_OK);
160
161
    /* Roaring code doesn't handle memory allocation failures */
162
#ifdef ENABLE_NALLOC
163
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "hostname_dns_check", "0") == NDPI_CFG_OK);
164
#else
165
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "hostname_dns_check", "1") == NDPI_CFG_OK);
166
9
#endif
167
168
#ifdef ENABLE_CONFIG2
169
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "flow_risk.all.info", "0") == NDPI_CFG_OK);
170
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.tcp_fingerprint_format", "1") == NDPI_CFG_OK);
171
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.ndpi_fingerprint_format", "1") == NDPI_CFG_OK);
172
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.ndpi_fingerprint_ignore_tcp_fp", "1") == NDPI_CFG_OK);
173
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "max_num_blocks_to_analyze", "8") == NDPI_CFG_OK);
174
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "tls_blocks_show_timing", "0") == NDPI_CFG_OK);
175
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_ignore_ephemeral_tls_extn", "1") == NDPI_CFG_OK);
176
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ndpifp_ignore_sni_tls_extn", "1") == NDPI_CFG_OK);
177
178
    addr_dump_path = "/tmp/";
179
#endif
180
181
9
#endif /* ENABLE_ONLY_SUBCLASSIFICATION */
182
183
9
    ndpi_finalize_initialization(workflow->ndpi_struct);
184
185
#ifdef ENABLE_FINGERPRINT_FP
186
    fingerprint_fp = stdout;
187
#endif
188
189
#ifdef CRYPT_FORCE_NO_AESNI
190
    force_no_aesni = 1;
191
#endif
192
193
9
#ifdef ENABLE_PAYLOAD_ANALYZER
194
9
   enable_payload_analyzer = 1;
195
9
#endif
196
9
  }
197
198
256k
#ifdef ENABLE_MEM_ALLOC_FAILURES
199
  /* Don't fail memory allocations until init phase is done */
200
256k
  fuzz_set_alloc_callbacks_and_seed(Size);
201
256k
#endif
202
203
256k
  fd = buffer_to_file(Data, Size);
204
256k
  if (fd == NULL)
205
0
    return 0;
206
207
256k
  pkts = pcap_fopen_offline(fd, errbuf);
208
256k
  if (pkts == NULL) {
209
25
    fclose(fd);
210
25
    return 0;
211
25
  }
212
256k
  if (ndpi_is_datalink_supported(pcap_datalink(pkts)) == 0)
213
332
  {
214
    /* Do not fail if the datalink type is not supported (may happen often during fuzzing). */
215
332
    pcap_close(pkts);
216
332
    return 0;
217
332
  }
218
219
256k
  workflow->pcap_handle = pkts;
220
  /* Init flow tree */
221
256k
  workflow->ndpi_flows_root = ndpi_calloc(workflow->prefs.num_roots, sizeof(void *));
222
256k
  if(!workflow->ndpi_flows_root) {
223
2
    pcap_close(pkts);
224
2
    return 0;
225
2
  }
226
227
#ifdef ENABLE_NALLOC
228
  nalloc_init("nalloc");
229
  nalloc_start(Data, Size);
230
#endif
231
232
256k
  header = NULL;
233
256k
  r = pcap_next_ex(pkts, &header, &pkt);
234
14.0M
  while (r > 0) {
235
    /* allocate an exact size buffer to check overflows */
236
13.7M
    uint8_t *packet_checked = malloc(header->caplen);
237
238
13.7M
    if(packet_checked) {
239
13.5M
      ndpi_risk flow_risk;
240
13.5M
      struct ndpi_flow_info *flow = NULL; /* unused */
241
242
13.5M
      memcpy(packet_checked, pkt, header->caplen);
243
13.5M
      ndpi_workflow_process_packet(workflow, header, packet_checked, &flow_risk, &flow);
244
13.5M
      free(packet_checked);
245
13.5M
    }
246
247
13.7M
    r = pcap_next_ex(pkts, &header, &pkt);
248
13.7M
  }
249
256k
  pcap_close(pkts);
250
251
  /* Free flow trees */
252
4.36M
  for(i = 0; i < workflow->prefs.num_roots; i++) {
253
4.10M
    ndpi_twalk(workflow->ndpi_flows_root[i], node_cleanup_walker, NULL);
254
4.10M
    ndpi_tdestroy(workflow->ndpi_flows_root[i], ndpi_flow_info_freer);
255
4.10M
  }
256
256k
  ndpi_free(workflow->ndpi_flows_root);
257
  /* Free payload analyzer data */
258
256k
  if(enable_payload_analyzer)
259
26.7k
    ndpi_report_payload_stats(stdout);
260
261
256k
#ifdef ENABLE_PAYLOAD_ANALYZER
262
256k
  ndpi_update_params(SPLT_PARAM_TYPE, "splt_param.txt");
263
256k
  ndpi_update_params(BD_PARAM_TYPE, "bd_param.txt");
264
256k
  ndpi_update_params(2, ""); /* invalid */
265
256k
#endif
266
267
#ifdef ENABLE_NALLOC
268
  nalloc_end();
269
#endif
270
271
256k
  return 0;
272
256k
}