Coverage Report

Created: 2026-09-19 06:59

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 alloc_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
44
int LLVMFuzzerInitialize(int *argc, char ***argv) {
51
44
  (void)argc;
52
53
44
  path = dirname(strdup(*argv[0])); /* No errors; no free! */
54
44
  return 0;
55
44
}
56
57
480k
static void node_cleanup_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) {
58
480k
  struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node;
59
60
480k
  (void)depth;
61
480k
  (void)user_data;
62
63
480k
  if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */
64
236k
    if((!flow->detection_completed) && flow->ndpi_flow) {
65
186k
      flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct,
66
186k
                                                      flow->ndpi_flow);
67
186k
    }
68
69
236k
    process_ndpi_collected_info(workflow, flow);
70
236k
  }
71
480k
}
72
73
static void fn_flow_callback(struct ndpi_workflow *w, struct ndpi_flow_info *f, void *d)
74
50.6k
{
75
50.6k
  (void)w;
76
50.6k
  (void)f;
77
50.6k
  (void)d;
78
50.6k
}
79
80
331k
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
81
331k
  pcap_t * pkts;
82
331k
  const u_char *pkt;
83
331k
  struct pcap_pkthdr *header;
84
331k
  int r;
85
331k
  char errbuf[PCAP_ERRBUF_SIZE];
86
331k
  u_int i;
87
331k
  FILE *fd;
88
331k
  char name[256];
89
90
331k
  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
9
                                  NDPI_LICENSE_NOT_FOR_PROFIT_LGPL);
105
106
9
    ndpi_workflow_set_flow_callback(workflow, fn_flow_callback, NULL);
107
108
9
    ndpi_set_config(workflow->ndpi_struct, NULL, "log.level", "3");
109
9
    ndpi_set_config(workflow->ndpi_struct, "all", "log", "1");
110
111
9
    sprintf(name, "%s/public_suffix_list.dat", path);
112
9
    assert(ndpi_load_domain_suffixes(workflow->ndpi_struct, name) >= 0);
113
9
    sprintf(name, "%s/lists/", path);
114
9
    assert(ndpi_load_categories_dir(workflow->ndpi_struct, name) >= 0);
115
9
    sprintf(name, "%s/lists/protocols/", path);
116
9
    assert(ndpi_load_protocols_dir(workflow->ndpi_struct, name) >= 0);
117
9
    sprintf(name, "%s/protos.txt", path);
118
9
    assert(ndpi_load_protocols_file(workflow->ndpi_struct, name) >= 0);
119
9
    sprintf(name, "%s/categories.txt", path);
120
9
    assert(ndpi_load_categories_file(workflow->ndpi_struct, name, NULL) >= 0);
121
9
    sprintf(name, "%s/risky_domains.txt", path);
122
9
    assert(ndpi_load_risk_domain_file(workflow->ndpi_struct, name) >= 0);
123
9
    sprintf(name, "%s/ja4_fingerprints.csv", path);
124
9
    assert(ndpi_load_malicious_ja4_file(workflow->ndpi_struct, name) >= 0);
125
9
    sprintf(name, "%s/tcp_fingerprints.csv", path);
126
9
    assert(ndpi_load_tcp_fingerprint_file(workflow->ndpi_struct, name) >= 0);
127
9
    sprintf(name, "%s/sha1_fingerprints.csv", path);
128
9
    assert(ndpi_load_malicious_sha1_file(workflow->ndpi_struct, name) >= 0);
129
9
    sprintf(name, "%s", path);
130
9
    assert(ndpi_load_protocol_plugins(workflow->ndpi_struct, name) >= 0); /* Plugins are not really used while fuzzing, yet */
131
132
#ifdef ENABLE_ONLY_SUBCLASSIFICATION
133
    sprintf(name, "%s/config_only_classification.txt", path);
134
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "filename.config", name) == NDPI_CFG_OK);
135
#else
136
137
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "packets_limit_per_flow", "255") == NDPI_CFG_OK);
138
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "flow.track_payload", "1") == NDPI_CFG_OK);
139
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "tcp_ack_payload_heuristic", "1") == NDPI_CFG_OK);
140
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "fully_encrypted_heuristic", "1") == NDPI_CFG_OK);
141
9
    assert(ndpi_set_config(workflow->ndpi_struct, "dns", "subclassification", "1") == NDPI_CFG_OK);
142
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "application_blocks_tracking", "1") == NDPI_CFG_OK);
143
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "max_num_blocks_to_analyze", "8") == NDPI_CFG_OK);
144
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "tls_blocks_show_timing", "0") == NDPI_CFG_OK);
145
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_ignore_ephemeral_tls_extn", "1") == NDPI_CFG_OK);
146
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ndpifp_ignore_sni_tls_extn", "1") == NDPI_CFG_OK);
147
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_data", "1") == NDPI_CFG_OK);
148
9
    assert(ndpi_set_config(workflow->ndpi_struct, "ssh", "metadata.ssh_data", "1") == NDPI_CFG_OK);
149
9
#ifndef ENABLE_CONFIG2
150
9
    assert(ndpi_set_config(workflow->ndpi_struct, "stun", "max_packets_extra_dissection", "40") == NDPI_CFG_OK);
151
9
    assert(ndpi_set_config(workflow->ndpi_struct, "zoom", "max_packets_extra_dissection", "255") == NDPI_CFG_OK);
152
9
    assert(ndpi_set_config(workflow->ndpi_struct, "rtp", "search_for_stun", "1") == NDPI_CFG_OK);
153
9
#endif
154
9
    assert(ndpi_set_config(workflow->ndpi_struct, "openvpn", "dpi.heuristics", "0x01") == NDPI_CFG_OK);
155
9
    assert(ndpi_set_config(workflow->ndpi_struct, "openvpn", "dpi.heuristics.num_messages", "20") == NDPI_CFG_OK);
156
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja4r_fingerprint", "1") == NDPI_CFG_OK);
157
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "dpi.heuristics", "0x07") == NDPI_CFG_OK);
158
9
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "dpi.heuristics.max_packets_extra_dissection", "40") == NDPI_CFG_OK);
159
9
    assert(ndpi_set_config(workflow->ndpi_struct, "all", "monitoring", "1") == NDPI_CFG_OK);
160
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "dpi.address_cache_size", "8192") == NDPI_CFG_OK);
161
9
    assert(ndpi_set_config(workflow->ndpi_struct, "dns", "custom_port", "0") == NDPI_CFG_OK);
162
163
    /* Roaring code doesn't handle memory allocation failures */
164
#ifdef ENABLE_NALLOC
165
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "hostname_dns_check", "0") == NDPI_CFG_OK);
166
#else
167
9
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "hostname_dns_check", "1") == NDPI_CFG_OK);
168
9
#endif
169
170
#ifdef ENABLE_CONFIG2
171
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "flow_risk.all.info", "0") == NDPI_CFG_OK);
172
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.tcp_fingerprint_format", "1") == NDPI_CFG_OK);
173
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.ndpi_fingerprint_format", "1") == NDPI_CFG_OK);
174
    assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.ndpi_fingerprint_ignore_tcp_fp", "1") == NDPI_CFG_OK);
175
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "max_num_blocks_to_analyze", "8") == NDPI_CFG_OK);
176
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "tls_blocks_show_timing", "0") == NDPI_CFG_OK);
177
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_ignore_ephemeral_tls_extn", "1") == NDPI_CFG_OK);
178
    assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ndpifp_ignore_sni_tls_extn", "1") == NDPI_CFG_OK);
179
180
    addr_dump_path = "/tmp/";
181
#endif
182
183
9
#endif /* ENABLE_ONLY_SUBCLASSIFICATION */
184
185
9
    ndpi_finalize_initialization(workflow->ndpi_struct);
186
187
#ifdef ENABLE_FINGERPRINT_FP
188
    fingerprint_fp = stdout;
189
#endif
190
191
#ifdef CRYPT_FORCE_NO_AESNI
192
    force_no_aesni = 1;
193
#endif
194
195
9
#ifdef ENABLE_PAYLOAD_ANALYZER
196
9
   enable_payload_analyzer = 1;
197
9
#endif
198
9
  }
199
200
331k
#ifdef ENABLE_MEM_ALLOC_FAILURES
201
  /* Don't fail memory allocations until init phase is done */
202
331k
  fuzz_set_alloc_callbacks_and_seed(Size);
203
331k
#endif
204
205
331k
  fd = buffer_to_file(Data, Size);
206
331k
  if (fd == NULL)
207
0
    return 0;
208
209
331k
  pkts = pcap_fopen_offline(fd, errbuf);
210
331k
  if (pkts == NULL) {
211
27
    fclose(fd);
212
27
    return 0;
213
27
  }
214
331k
  if (ndpi_is_datalink_supported(pcap_datalink(pkts)) == 0)
215
361
  {
216
    /* Do not fail if the datalink type is not supported (may happen often during fuzzing). */
217
361
    pcap_close(pkts);
218
361
    return 0;
219
361
  }
220
221
331k
  workflow->pcap_handle = pkts;
222
  /* Init flow tree */
223
331k
  workflow->ndpi_flows_root = ndpi_calloc(workflow->prefs.num_roots, sizeof(void *));
224
331k
  if(!workflow->ndpi_flows_root) {
225
2
    pcap_close(pkts);
226
2
    return 0;
227
2
  }
228
229
#ifdef ENABLE_NALLOC
230
  nalloc_init("nalloc");
231
  nalloc_start(Data, Size);
232
#endif
233
234
331k
  header = NULL;
235
331k
  r = pcap_next_ex(pkts, &header, &pkt);
236
12.2M
  while (r > 0) {
237
    /* allocate an exact size buffer to check overflows */
238
11.8M
    uint8_t *packet_checked = malloc(header->caplen);
239
240
11.8M
    if(packet_checked) {
241
11.7M
      ndpi_risk flow_risk;
242
11.7M
      struct ndpi_flow_info *flow = NULL; /* unused */
243
244
11.7M
      memcpy(packet_checked, pkt, header->caplen);
245
11.7M
      ndpi_workflow_process_packet(workflow, header, packet_checked, &flow_risk, &flow);
246
11.7M
      free(packet_checked);
247
11.7M
    }
248
249
11.8M
    r = pcap_next_ex(pkts, &header, &pkt);
250
11.8M
  }
251
331k
  pcap_close(pkts);
252
253
  /* Free flow trees */
254
5.63M
  for(i = 0; i < workflow->prefs.num_roots; i++) {
255
5.29M
    ndpi_twalk(workflow->ndpi_flows_root[i], node_cleanup_walker, NULL);
256
5.29M
    ndpi_tdestroy(workflow->ndpi_flows_root[i], ndpi_flow_info_freer);
257
5.29M
  }
258
331k
  ndpi_free(workflow->ndpi_flows_root);
259
  /* Free payload analyzer data */
260
331k
  if(enable_payload_analyzer)
261
33.9k
    ndpi_report_payload_stats(stdout);
262
263
331k
#ifdef ENABLE_PAYLOAD_ANALYZER
264
331k
  ndpi_update_params(SPLT_PARAM_TYPE, "splt_param.txt");
265
331k
  ndpi_update_params(BD_PARAM_TYPE, "bd_param.txt");
266
331k
  ndpi_update_params(2, ""); /* invalid */
267
331k
#endif
268
269
#ifdef ENABLE_NALLOC
270
  nalloc_end();
271
#endif
272
273
331k
  return 0;
274
331k
}