From patchwork Mon Oct 12 12:37:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Petr_=C5=A0tetiar?= X-Patchwork-Id: 1380885 X-Patchwork-Delegate: ynezz@true.cz Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.openwrt.org (client-ip=2001:8b0:10b:1231::1; helo=merlin.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=true.cz Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=merlin.20170209 header.b=i0sh/ynB; dkim-atps=neutral Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:8b0:10b:1231::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4C8ytF41g3z9sTr for ; Mon, 12 Oct 2020 23:39:32 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=U8DENWs2nGKd61U8wynNjLS7/P8utwfyj6iWUc4Kg2I=; b=i0sh/ynBbVsFB4cGmaf3z+AQU PW+QCDwKb3fS6Mzl6bmCVw3AqaOSf+04rxkD01SE7halrRLia4pKtizMJRB0JAeQPmBszkzxTIZf2 BgerWyOIcouChyvt+v4D6i1GaCiSDIgW3WtOISIYjptmKsam7PNnEfrzPli71kONy+rbV6SGR1RJV tbjVN4QLpui3Lv8CIUwQ4VHqyM9uT+Me6JgJdcYkGgU5kXpYH+R8SYQyDhodlO2RdEn4l8gMM+6YC ThdKsZlbKJZm7q6cWgdwLWj4yLTutdUG4J+ZCfCjMg+n0XZRy/c2K3hoohTzDnfsgxUf3NBaDdZ/U tb1YjuNng==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kRx5M-0002tM-L7; Mon, 12 Oct 2020 12:37:52 +0000 Received: from smtp-out.xnet.cz ([178.217.244.18]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1kRx51-0002nN-AW for openwrt-devel@lists.openwrt.org; Mon, 12 Oct 2020 12:37:35 +0000 Received: from meh.true.cz (meh.true.cz [108.61.167.218]) (Authenticated sender: petr@true.cz) by smtp-out.xnet.cz (Postfix) with ESMTPSA id 018FA35DF; Mon, 12 Oct 2020 14:37:26 +0200 (CEST) Received: by meh.true.cz (OpenSMTPD) with ESMTP id 1cf93ac9; Mon, 12 Oct 2020 14:37:10 +0200 (CEST) From: =?utf-8?q?Petr_=C5=A0tetiar?= To: openwrt-devel@lists.openwrt.org Subject: [PATCH cgi-io 05/12] Add fuzzing of multipart_parser Date: Mon, 12 Oct 2020 14:37:11 +0200 Message-Id: <20201012123718.25623-6-ynezz@true.cz> In-Reply-To: <20201012123718.25623-1-ynezz@true.cz> References: <20201012123718.25623-1-ynezz@true.cz> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20201012_083731_554975_4843016C X-CRM114-Status: GOOD ( 25.62 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 SPF_NONE SPF: sender does not publish an SPF Record X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?utf-8?q?Petr_=C5=A0tetiar?= Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org LibFuzzer is in-process, coverage-guided, evolutionary fuzzing engine. LibFuzzer is linked with the library under test, and feeds fuzzed inputs to the library via a specific fuzzing entrypoint (aka "target function"); the fuzzer then tracks which areas of the code are reached, and generates mutations on the corpus of input data in order to maximize the code coverage. Lets use libFuzzer to fuzz multipart_parser for the start. Ref: https://llvm.org/docs/LibFuzzer.html Signed-off-by: Petr Štetiar --- CMakeLists.txt | 4 ++ tests/CMakeLists.txt | 3 ++ tests/fuzz-multipart-parser/CMakeLists.txt | 18 ++++++++ tests/fuzz-multipart-parser/corpus/.keep | 0 tests/fuzz-multipart-parser/dict/parser.dict | 10 +++++ tests/fuzz-multipart-parser/inputs/input1.txt | 6 +++ tests/fuzz-multipart-parser/inputs/input2.txt | 10 +++++ .../test-fuzz-multipart-parser.c | 43 +++++++++++++++++++ 8 files changed, 94 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/fuzz-multipart-parser/CMakeLists.txt create mode 100644 tests/fuzz-multipart-parser/corpus/.keep create mode 100644 tests/fuzz-multipart-parser/dict/parser.dict create mode 100644 tests/fuzz-multipart-parser/inputs/input1.txt create mode 100644 tests/fuzz-multipart-parser/inputs/input2.txt create mode 100644 tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 693830a85274..b60d08e96e3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,5 +24,9 @@ ADD_LIBRARY(cgi-lib STATIC multipart_parser.c util.c) ADD_EXECUTABLE(cgi-io main.c) TARGET_LINK_LIBRARIES(cgi-io cgi-lib ${ubox} ${ubus}) +IF(UNIT_TESTING) + ENABLE_TESTING() + ADD_SUBDIRECTORY(tests) +ENDIF() INSTALL(TARGETS cgi-io RUNTIME DESTINATION sbin) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000000..8bb2a59412da --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") + ADD_SUBDIRECTORY(fuzz-multipart-parser) +ENDIF() diff --git a/tests/fuzz-multipart-parser/CMakeLists.txt b/tests/fuzz-multipart-parser/CMakeLists.txt new file mode 100644 index 000000000000..0a3a9893fc70 --- /dev/null +++ b/tests/fuzz-multipart-parser/CMakeLists.txt @@ -0,0 +1,18 @@ +FILE(GLOB test_cases "test-*.c") + +MACRO(ADD_FUZZER_TEST name) + ADD_EXECUTABLE(${name} ${name}.c) + TARGET_COMPILE_OPTIONS(${name} PRIVATE -g -O1 -fno-omit-frame-pointer -fsanitize=fuzzer,address,leak,undefined) + TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR}) + TARGET_LINK_OPTIONS(${name} PRIVATE -stdlib=libc++ -fsanitize=fuzzer,address,leak,undefined) + TARGET_LINK_LIBRARIES(${name} cgi-lib) + ADD_TEST( + NAME ${name} + COMMAND ${name} -max_len=256 -timeout=10 -max_total_time=300 -dict=${CMAKE_CURRENT_SOURCE_DIR}/dict/parser.dict ${CMAKE_CURRENT_SOURCE_DIR}/corpus + ) +ENDMACRO(ADD_FUZZER_TEST) + +FOREACH(test_case ${test_cases}) + GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE) + ADD_FUZZER_TEST(${test_case}) +ENDFOREACH(test_case) diff --git a/tests/fuzz-multipart-parser/corpus/.keep b/tests/fuzz-multipart-parser/corpus/.keep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/fuzz-multipart-parser/dict/parser.dict b/tests/fuzz-multipart-parser/dict/parser.dict new file mode 100644 index 000000000000..72a5bdd3d8e6 --- /dev/null +++ b/tests/fuzz-multipart-parser/dict/parser.dict @@ -0,0 +1,10 @@ +"Content-Disposition: form-data; name=" +"\x0D\x0A" +"\x0D" +"x0A" +"=" +";" +"--" +"Content-Type:" +"filename=" +"--12--34--56" diff --git a/tests/fuzz-multipart-parser/inputs/input1.txt b/tests/fuzz-multipart-parser/inputs/input1.txt new file mode 100644 index 000000000000..849873e3905e --- /dev/null +++ b/tests/fuzz-multipart-parser/inputs/input1.txt @@ -0,0 +1,6 @@ +--AaB03x +Content-Disposition: form-data; name="files"; filename="fi;le1.txt" +Content-Type: text/plain + +contents +--AaB03x-- \ No newline at end of file diff --git a/tests/fuzz-multipart-parser/inputs/input2.txt b/tests/fuzz-multipart-parser/inputs/input2.txt new file mode 100644 index 000000000000..7ba009902eee --- /dev/null +++ b/tests/fuzz-multipart-parser/inputs/input2.txt @@ -0,0 +1,10 @@ +--AaB03x +Content-Disposition: form-data; name="submit-name" + +Larry +--AaB03x +Content-Disposition: form-data; name="files"; filename="file1.txt" +Content-Type: text/plain + + +--AaB03x-- diff --git a/tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c b/tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c new file mode 100644 index 000000000000..ca952d9af9db --- /dev/null +++ b/tests/fuzz-multipart-parser/test-fuzz-multipart-parser.c @@ -0,0 +1,43 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "multipart_parser.h" + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t size) +{ + char *buf = NULL; + multipart_parser *p; + static multipart_parser_settings s = { + .on_part_data = NULL, + .on_headers_complete = NULL, + .on_part_data_end = NULL, + .on_header_field = NULL, + .on_header_value = NULL, + }; + buf = calloc(1, size + 1); + if (!buf) + return 0; + + memcpy(buf, input, size); + p = multipart_parser_init(buf, &s); + if (!p) { + free(buf); + return 0; + } + + multipart_parser_execute(p, buf, size + 1); + multipart_parser_free(p); + free(buf); + + return 0; +}