From c61b3af919e855a9791d5a4d4cf39d96c2a369fa Mon Sep 17 00:00:00 2001 From: David Korczynski Date: Wed, 6 May 2026 05:07:05 -0700 Subject: [PATCH] fuzz: add additional harnesses to be consumed by OSS-Fuzz The goal is to increase code coverage relative to the latest code coverage report https://storage.googleapis.com/oss-fuzz-coverage/libhtp/reports/20260504/linux/src/report.html Can confirm these new harnesses increase covered code, based on local runs. Some adjustments are needed in OSS-Fuzz once these are merged. Signed-off-by: David Korczynski --- test/fuzz/fuzz_htp_headers.c | 88 +++++++++++++++++++++++++++++++++ test/fuzz/fuzz_htp_multipart.c | 45 +++++++++++++++++ test/fuzz/fuzz_htp_urlencoded.c | 40 +++++++++++++++ test/fuzz/fuzz_htp_utils.c | 71 ++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 test/fuzz/fuzz_htp_headers.c create mode 100644 test/fuzz/fuzz_htp_multipart.c create mode 100644 test/fuzz/fuzz_htp_urlencoded.c create mode 100644 test/fuzz/fuzz_htp_utils.c diff --git a/test/fuzz/fuzz_htp_headers.c b/test/fuzz/fuzz_htp_headers.c new file mode 100644 index 00000000..315f4546 --- /dev/null +++ b/test/fuzz/fuzz_htp_headers.c @@ -0,0 +1,88 @@ +#include +#include +#include + +#include "htp/htp.h" +#include "htp/htp_private.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 1) return 0; + uint8_t type = data[0]; + data++; size--; + + htp_cfg_t *cfg = htp_config_create(); + if (cfg == NULL) return 0; + + htp_connp_t *connp = htp_connp_create(cfg); + if (connp == NULL) { + htp_config_destroy(cfg); + return 0; + } + + htp_tx_t *tx = htp_connp_tx_create(connp); + if (tx == NULL) { + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + connp->in_tx = tx; + + if (tx->request_headers == NULL) { + tx->request_headers = htp_table_create(4); + } + + if (tx->request_headers == NULL) { + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + + bstr *value = bstr_dup_mem(data, size); + if (value == NULL) { + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + + htp_header_t *h = calloc(1, sizeof(htp_header_t)); + if (h == NULL) { + bstr_free(value); + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + + if (type % 2 == 0) { + h->name = bstr_dup_c("cookie"); + } else { + h->name = bstr_dup_c("authorization"); + } + + if (h->name == NULL) { + free(h); + bstr_free(value); + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + h->value = value; + if (htp_table_add(tx->request_headers, h->name, h) != HTP_OK) { + bstr_free(h->name); + free(h); + bstr_free(value); + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + + if (type % 2 == 0) { + htp_parse_cookies_v0(connp); + } else { + htp_parse_authorization(connp); + } + + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + + return 0; +} diff --git a/test/fuzz/fuzz_htp_multipart.c b/test/fuzz/fuzz_htp_multipart.c new file mode 100644 index 00000000..fa6a2d85 --- /dev/null +++ b/test/fuzz/fuzz_htp_multipart.c @@ -0,0 +1,45 @@ +#include +#include +#include + +#include "htp/htp.h" +#include "htp/htp_multipart.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 1) return 0; + + htp_cfg_t *cfg = htp_config_create(); + if (cfg == NULL) return 0; + + // Use the first byte to determine the boundary length (1-32 bytes) + size_t boundary_len = (data[0] % 32) + 1; + if (size < 1 + boundary_len) { + htp_config_destroy(cfg); + return 0; + } + + bstr *boundary = bstr_dup_mem(data + 1, boundary_len); + if (boundary == NULL) { + htp_config_destroy(cfg); + return 0; + } + + htp_mpartp_t *parser = htp_mpartp_create(cfg, boundary, 0); + if (parser == NULL) { + bstr_free(boundary); + htp_config_destroy(cfg); + return 0; + } + + const uint8_t *mpart_data = data + 1 + boundary_len; + size_t mpart_size = size - 1 - boundary_len; + + htp_mpartp_parse(parser, mpart_data, mpart_size); + htp_mpartp_finalize(parser); + htp_mpartp_destroy(parser); + + bstr_free(boundary); + htp_config_destroy(cfg); + + return 0; +} diff --git a/test/fuzz/fuzz_htp_urlencoded.c b/test/fuzz/fuzz_htp_urlencoded.c new file mode 100644 index 00000000..4d243f18 --- /dev/null +++ b/test/fuzz/fuzz_htp_urlencoded.c @@ -0,0 +1,40 @@ +#include +#include +#include + +#include "htp/htp.h" +#include "htp/htp_urlencoded.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + htp_cfg_t *cfg = htp_config_create(); + if (cfg == NULL) return 0; + + htp_connp_t *connp = htp_connp_create(cfg); + if (connp == NULL) { + htp_config_destroy(cfg); + return 0; + } + + htp_tx_t *tx = htp_tx_create(connp); + if (tx == NULL) { + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + + htp_urlenp_t *urlenp = htp_urlenp_create(tx); + if (urlenp == NULL) { + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + return 0; + } + + htp_urlenp_parse_complete(urlenp, data, size); + htp_urlenp_finalize(urlenp); + htp_urlenp_destroy(urlenp); + + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + + return 0; +} diff --git a/test/fuzz/fuzz_htp_utils.c b/test/fuzz/fuzz_htp_utils.c new file mode 100644 index 00000000..365ba783 --- /dev/null +++ b/test/fuzz/fuzz_htp_utils.c @@ -0,0 +1,71 @@ +#include +#include +#include + +#include "htp/htp.h" +#include "htp/htp_base64.h" +#include "htp/htp_multipart.h" +#include "htp/htp_private.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 1) return 0; + uint8_t type = data[0]; + data++; size--; + + switch (type % 5) { + case 0: { // base64 + bstr *decoded = htp_base64_decode_mem(data, size); + if (decoded != NULL) { + bstr_free(decoded); + } + break; + } + case 1: { // php parameter + if (size == 0) break; + htp_param_t *param = calloc(1, sizeof(htp_param_t)); + if (param == NULL) break; + param->name = bstr_dup_mem(data, size); + if (param->name == NULL) { + free(param); + break; + } + htp_php_parameter_processor(param); + bstr_free(param->name); + bstr_free(param->value); + free(param); + break; + } + case 2: { // mpartp_find_boundary + if (size == 0) break; + bstr *content_type = bstr_dup_mem(data, size); + if (content_type == NULL) break; + bstr *boundary = NULL; + uint64_t flags = 0; + htp_status_t rc = htp_mpartp_find_boundary(content_type, &boundary, &flags); + if (rc == HTP_OK && boundary != NULL) { + bstr_free(boundary); + } + bstr_free(content_type); + break; + } + case 3: { // protocol + if (size == 0) break; + bstr *protocol = bstr_dup_mem(data, size); + if (protocol) { + htp_parse_protocol(protocol); + bstr_free(protocol); + } + break; + } + case 4: { // status + if (size == 0) break; + bstr *status = bstr_dup_mem(data, size); + if (status) { + htp_parse_status(status); + bstr_free(status); + } + break; + } + } + return 0; +}