diff --git a/projects/esp-idf/Dockerfile b/projects/esp-idf/Dockerfile new file mode 100644 index 000000000000..995649cd45aa --- /dev/null +++ b/projects/esp-idf/Dockerfile @@ -0,0 +1,28 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder:ubuntu-24-04 + +RUN apt-get update && apt-get install -y \ + make cmake && \ + rm -rf /var/lib/apt/lists/* + +RUN git clone --depth 1 https://github.com/espressif/esp-idf $SRC/esp-idf + +COPY build.sh $SRC/ +COPY fuzz_http_parser.c fuzz_http_parser_url.c $SRC/ + +WORKDIR $SRC/esp-idf diff --git a/projects/esp-idf/build.sh b/projects/esp-idf/build.sh new file mode 100755 index 000000000000..b82480a6b9a0 --- /dev/null +++ b/projects/esp-idf/build.sh @@ -0,0 +1,144 @@ +#!/bin/bash -eu +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +# ESP-IDF is primarily an embedded SDK targeting Xtensa/RISC-V SoCs and most +# components require the IDF build system together with the cross-compilation +# toolchain. For the initial OSS-Fuzz integration we fuzz components that are +# self-contained portable C and can be compiled natively with the fuzzer +# instrumentation. + +HTTP_PARSER_DIR="$SRC/esp-idf/components/http_parser" + +# Compile the http_parser source as a standalone object file using the +# OSS-Fuzz sanitizer/coverage flags. +$CC $CFLAGS -I"$HTTP_PARSER_DIR" \ + -c "$HTTP_PARSER_DIR/http_parser.c" \ + -o "$WORK/http_parser.o" + +# fuzz_http_parser: feeds bytes to http_parser_execute() in request/response/both modes. +$CC $CFLAGS -I"$HTTP_PARSER_DIR" \ + -c "$SRC/fuzz_http_parser.c" \ + -o "$WORK/fuzz_http_parser.o" + +$CXX $CXXFLAGS \ + "$WORK/fuzz_http_parser.o" \ + "$WORK/http_parser.o" \ + $LIB_FUZZING_ENGINE \ + -o "$OUT/fuzz_http_parser" + +# fuzz_http_parser_url: exercises http_parser_parse_url() with and without connect=true. +$CC $CFLAGS -I"$HTTP_PARSER_DIR" \ + -c "$SRC/fuzz_http_parser_url.c" \ + -o "$WORK/fuzz_http_parser_url.o" + +$CXX $CXXFLAGS \ + "$WORK/fuzz_http_parser_url.o" \ + "$WORK/http_parser.o" \ + $LIB_FUZZING_ENGINE \ + -o "$OUT/fuzz_http_parser_url" + +# Dictionary of HTTP tokens to help the fuzzer reach the parser's interesting +# states (methods, versions, common headers, transfer-encoding values, etc.). +# NOTE: libFuzzer's dictionary format only supports \\, \" and \xNN escapes, +# so CRLF is encoded as \x0d\x0a. +cat > "$OUT/fuzz_http_parser.dict" <<'DICT' +"GET " +"PUT " +"POST " +"HEAD " +"DELETE " +"OPTIONS " +"TRACE " +"CONNECT " +"PATCH " +"PROPFIND " +"PROPPATCH " +"MKCOL " +"COPY " +"MOVE " +"LOCK " +"UNLOCK " +"REPORT " +"MKACTIVITY " +"CHECKOUT " +"MERGE " +"M-SEARCH " +"NOTIFY " +"SUBSCRIBE " +"UNSUBSCRIBE " +"PURGE " +"MKCALENDAR " +" HTTP/1.0\x0d\x0a" +" HTTP/1.1\x0d\x0a" +" HTTP/2.0\x0d\x0a" +"\x0d\x0a" +"\x0d\x0a\x0d\x0a" +"Host: " +"Content-Length: " +"Content-Type: " +"Transfer-Encoding: chunked\x0d\x0a" +"Connection: close\x0d\x0a" +"Connection: keep-alive\x0d\x0a" +"Connection: upgrade\x0d\x0a" +"Upgrade: websocket\x0d\x0a" +"Trailer: " +"Expect: 100-continue\x0d\x0a" +"chunked" +"identity" +"0\x0d\x0a\x0d\x0a" +"1\x0d\x0aA\x0d\x0a" +"HTTP/1.0 200 OK\x0d\x0a" +"HTTP/1.1 200 OK\x0d\x0a" +"HTTP/1.1 301 Moved Permanently\x0d\x0a" +"HTTP/1.1 304 Not Modified\x0d\x0a" +"HTTP/1.1 404 Not Found\x0d\x0a" +"HTTP/1.1 500 Internal Server Error\x0d\x0a" +DICT + +cp "$OUT/fuzz_http_parser.dict" "$OUT/fuzz_http_parser_url.dict" + +# Seed corpus for the message parser harness. +SEED_PARSER="$WORK/seeds_parser" +mkdir -p "$SEED_PARSER" + +printf 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n' \ + > "$SEED_PARSER/req_get" +printf 'POST /a HTTP/1.1\r\nHost: x\r\nContent-Length: 3\r\n\r\nabc' \ + > "$SEED_PARSER/req_post" +printf 'POST /a HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nabc\r\n0\r\n\r\n' \ + > "$SEED_PARSER/req_chunked" +printf 'HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok' \ + > "$SEED_PARSER/resp_ok" +printf 'HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n2\r\nok\r\n0\r\n\r\n' \ + > "$SEED_PARSER/resp_chunked" +printf 'GET /chat HTTP/1.1\r\nHost: x\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n' \ + > "$SEED_PARSER/req_upgrade" + +(cd "$SEED_PARSER" && zip -q "$OUT/fuzz_http_parser_seed_corpus.zip" ./*) + +# Seed corpus for the URL parser harness. +SEED_URL="$WORK/seeds_url" +mkdir -p "$SEED_URL" + +printf 'http://example.com/' > "$SEED_URL/abs_simple" +printf 'https://user:pass@host:8080/p?q=1#f' > "$SEED_URL/abs_full" +printf '/path/only?x=1' > "$SEED_URL/origin_form" +printf 'host:443' > "$SEED_URL/connect_form" +printf 'coap://[::1]:5683/.well-known/core' > "$SEED_URL/ipv6_coap" +printf '*' > "$SEED_URL/asterisk" + +(cd "$SEED_URL" && zip -q "$OUT/fuzz_http_parser_url_seed_corpus.zip" ./*) diff --git a/projects/esp-idf/fuzz_http_parser.c b/projects/esp-idf/fuzz_http_parser.c new file mode 100644 index 000000000000..648de1171c60 --- /dev/null +++ b/projects/esp-idf/fuzz_http_parser.c @@ -0,0 +1,49 @@ +/* Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "http_parser.h" + +static const http_parser_settings settings_null = { + .on_message_begin = 0, + .on_url = 0, + .on_status = 0, + .on_header_field = 0, + .on_header_value = 0, + .on_headers_complete = 0, + .on_body = 0, + .on_message_complete = 0, + .on_chunk_header = 0, + .on_chunk_complete = 0, +}; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size == 0) { + return 0; + } + + /* Exercise all three parser modes against the same input. */ + enum http_parser_type modes[] = {HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH}; + for (unsigned i = 0; i < sizeof(modes) / sizeof(modes[0]); ++i) { + http_parser parser; + http_parser_init(&parser, modes[i]); + http_parser_execute(&parser, &settings_null, + (const char *)data, size); + } + + return 0; +} diff --git a/projects/esp-idf/fuzz_http_parser_url.c b/projects/esp-idf/fuzz_http_parser_url.c new file mode 100644 index 000000000000..f6e7fb79de6d --- /dev/null +++ b/projects/esp-idf/fuzz_http_parser_url.c @@ -0,0 +1,34 @@ +/* Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "http_parser.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size == 0) { + return 0; + } + + struct http_parser_url u; + http_parser_url_init(&u); + http_parser_parse_url((const char *)data, size, 0, &u); + + http_parser_url_init(&u); + http_parser_parse_url((const char *)data, size, 1, &u); + + return 0; +} diff --git a/projects/esp-idf/project.yaml b/projects/esp-idf/project.yaml new file mode 100644 index 000000000000..a0d038d63e87 --- /dev/null +++ b/projects/esp-idf/project.yaml @@ -0,0 +1,12 @@ +homepage: "https://github.com/espressif/esp-idf" +language: c +main_repo: "https://github.com/espressif/esp-idf" +primary_contact: "bugbounty@espressif.com" +auto_ccs: + - "mahavir@espressif.com" +fuzzing_engines: + - libfuzzer +sanitizers: + - address + - undefined +base_os_version: ubuntu-24-04