Date Mar 31, 2026

Meta Bug Bounty — Fuzzing "netconsd" for Fun and Profit — Part 1

Writing a fuzz harness for one of Meta's open-source projects — netconsd, a kernel log daemon written in C++. This series walks you through finding the right packet-processing function, writing the harness, and ultimately discovering a heap overflow vulnerability.

netconsd fuzzing

What is netconsd?

netconsd is Meta's open-source daemon for receiving and processing Linux kernel log messages over the network. It's written in C++ and uses a library called ncrx for parsing ext-netconsole packets. When fuzzing network applications with AFL++, the fastest approach is writing a harness that directly calls the packet parsing functions — skipping the network layer entirely.

Step 1: Finding the Packet Processing Code

Starting from main(), the interesting path is: main()create_threads()create_worker_threads()ncrx_worker_thread()consume_msgbuf()ncrx_process().

Tracing the Call Chain

// main() calls create_threads() which spins up workers via pthread_create: r = pthread_create(&cur->id, NULL, ncrx_worker_thread, cur); // ncrx_worker_thread() processes buffered messages: while ((tmp = curbuf)) { consume_msgbuf(cur, curbuf); curbuf = curbuf->next; free(tmp); } // consume_msgbuf() calls the core parser: if (!ncrx_process(buf->buf, now_mono_ms(), buf->rcv_time, ncrx_bucket->ncrx)) { drain_bucket_ncrx(cur, ncrx_bucket); return; }

The ncrx API

The ncrx.h header documents exactly what's needed for the harness — a simple three-call API:

  • ncrx_create() — Initialize the ncrx state machine
  • ncrx_process() — Feed a packet buffer into the parser
  • ncrx_destroy() — Clean up to prevent memory leaks

The First Fuzz Harness

Using AFL++ persistent mode via shared memory (__AFL_FUZZ_TESTCASE_BUF) for maximum throughput:

#include "ncrx.h" #include "string.h" #include <time.h> #define NETCONS_RTO 200 static const struct ncrx_param ncrx_param = { .nr_slots = 512, .retx_intv = NETCONS_RTO, .msg_timeout = NETCONS_RTO, .oos_timeout = NETCONS_RTO, }; __AFL_FUZZ_INIT(); int main(int argc, char **argv) { unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; while (__AFL_LOOP(10000)) { int len = __AFL_FUZZ_TESTCASE_LEN; struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); uint64_t now = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; // Core fuzzing loop struct ncrx *ncrx = ncrx_create(&ncrx_param); ncrx_process(buf, now, 0, ncrx); ncrx_destroy(ncrx); } }

Result: No Crashes Yet

Running this harness produced no crashes. However, while the fuzzer was running, deeper research into the packet format revealed a key insight: netconsole supports fragmented messages — and our harness only tested single-message parsing. The fix, and the resulting heap overflow discovery, are covered in Part 2.

Continue Reading

Part 2 covers the improved fuzz harness that processes multi-message sequences — the critical modification that uncovered the heap overflow vulnerability.

https://ZINAD.net/support-page.html

Written by Fady Othman, Co-founder and Director of R&D at ZINAD.