Skip to content

fuzz: harden ndpi_hex_decode and add targeted parser harnesses#3163

Merged
IvanNardi merged 3 commits into
ntop:devfrom
parasol-aser:fuzz/fix-3159-hex-decode-and-targets
Apr 20, 2026
Merged

fuzz: harden ndpi_hex_decode and add targeted parser harnesses#3163
IvanNardi merged 3 commits into
ntop:devfrom
parasol-aser:fuzz/fix-3159-hex-decode-and-targets

Conversation

@parasol-aser

Copy link
Copy Markdown
Contributor

Summary

Closes #3159.

This follows the issue thread's requested fix direction: ndpi_hex_decode() now uses a bounded nibble decoder instead of relying on sscanf() over a borrowed buffer, rather than papering over ndpi_decode_tls_blocks() with a scratch NUL-terminated copy. It also upstreams the direct fuzz_ndpi_decode_tls_blocks reproducer that Ivan Nardi asked about in the follow-up comment, plus three additional focused parser fuzz targets from the same fuzzing pass.

Changes

  • replace the per-byte sscanf("%02hhX", ...) loop with a bounded hex decoder that accepts explicit-length, non-NUL-terminated buffers, rejects odd-length or malformed hex, and zeros out_len on failure
  • harden ndpi_decode_tls_blocks() to zero num_tls_blocks on entry and fail closed on malformed decode results instead of partially parsing truncated data
  • document the explicit-length semantics and failure behavior for ndpi_hex_decode() and ndpi_decode_tls_blocks() in ndpi_api.h
  • add unit coverage for non-NUL-terminated, mixed-case, invalid, and odd-length hex input plus TLS-block round-trips and malformed payload handling
  • add fuzz_ndpi_decode_tls_blocks and wire it into fuzz/Makefile.am
  • add focused fuzz_dns_parse, fuzz_http_parse, and fuzz_tls_client_server_hello harnesses and register them in fuzz/Makefile.am

Test Plan

  • make -j4 -C fuzz fuzz_ndpi_decode_tls_blocks fuzz_dns_parse fuzz_http_parse fuzz_tls_client_server_hello
  • ./fuzz/fuzz_ndpi_decode_tls_blocks -fork=2 -ignore_crashes=1 -max_total_time=20 fuzz/corpus/fuzz_ndpi_decode_tls_blocks/
  • smoke-start fuzz_dns_parse, fuzz_http_parse, and fuzz_tls_client_server_hello with -runs=0 after staging the expected sidecar data files next to the binaries
  • attempted make -j4 -C tests/unit, but this environment is missing the json.h / json-c headers needed to build tests/unit/unit

@IvanNardi

Copy link
Copy Markdown
Collaborator

@parasol-aser, could you sign the CLA, please?

@parasol-aser

Copy link
Copy Markdown
Contributor Author

@parasol-aser, could you sign the CLA, please?

is there a link?

@IvanNardi

Copy link
Copy Markdown
Collaborator

@parasol-aser

Copy link
Copy Markdown
Contributor Author

@IvanNardi done

@IvanNardi

Copy link
Copy Markdown
Collaborator

As a first step, I rebased your code to add a fix

@IvanNardi IvanNardi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some doubts about the dns and http fuzzers.
The rest seems right.
@parasol-aser, what do you think?

Comment thread fuzz/fuzz_dns_parse.c Outdated
Comment thread fuzz/fuzz_http_parse.c Outdated
Comment thread tests/unit/unit.c Outdated
}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move these unit tests to example/utests.c with the same logic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved checkHexDecode / checkTlsBlocksDecode / rewriteHexCase helpers to example/utests.c (same logic, static).

Comment thread tests/unit/unit.c Outdated
- fuzz_dns_parse / fuzz_http_parse now call the parsers directly
  (ndpi_search_dns / ndpi_search_http_tcp) with a synthesised
  packet_struct instead of driving ndpi_detection_process_packet,
  matching the fuzz_is_stun / fuzz_quic_get_crypto_data pattern.
- Expose ndpi_search_dns and ndpi_search_http_tcp via ndpi_private.h
  so fuzz/unit targets can reach them; wire -DNDPI_LIB_COMPILATION
  into the two fuzz target CFLAGS.
- Move hexDecodeUnitTest / tlsBlocksUnitTest (and the checkHexDecode,
  checkTlsBlocksDecode, rewriteHexCase helpers) from tests/unit/unit.c
  to example/utests.c; register them in run_unit_tests() so they run
  via ndpiReader --run-tests with the rest of the suite.
@parasol-aser

Copy link
Copy Markdown
Contributor Author

Pushed 0843f93 addressing the review:

  • fuzz_dns_parse / fuzz_http_parse now call ndpi_search_dns / ndpi_search_http_tcp directly with a synthesised packet_struct (mirrors fuzz_is_stun / fuzz_quic_get_crypto_data). Both parsers are exposed via ndpi_private.h; -DNDPI_LIB_COMPILATION wired in fuzz/Makefile.am.
  • Hex-decode and TLS-blocks unit tests (plus the checkHexDecode / checkTlsBlocksDecode / rewriteHexCase helpers) moved from tests/unit/unit.c to example/utests.c and registered in run_unit_tests().

Built + smoke-ran both fuzzers locally (-runs=1 and short -max_total_time) and ran ndpiReader --run-tests; no crashes or assertion failures.

@sonarqubecloud

Copy link
Copy Markdown

@IvanNardi IvanNardi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much, for the fix and for all the follow-ups

@IvanNardi

Copy link
Copy Markdown
Collaborator

@parasol-aser, great work

@IvanNardi IvanNardi merged commit 315a705 into ntop:dev Apr 20, 2026
26 checks passed
@parasol-aser

Copy link
Copy Markdown
Contributor Author

@IvanNardi happy to contribute, and thank you for your work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fuzzing] Heap OOB read: ndpi_hex_decode sscanf on non-NUL-terminated buffer (via ndpi_decode_tls_blocks)

2 participants