diff --git a/.cmake-format b/.cmake-format index 4d6d048d..347868d1 100644 --- a/.cmake-format +++ b/.cmake-format @@ -23,5 +23,15 @@ additional_commands = { "SOURCE_FILES": '*', "COMPILE_DEFINITIONS": '*', } + }, + "add_fuzzing_test": { + "kwargs": { + "TARGET_NAME": '*', + "SOURCE_FILES": '*', + "COMPILE_DEFINITIONS": '*', + "CORPUS_DIRS": '*', + "MAX_LEN": '*', + "CRASH_FILES_DIR": '*', + } } } diff --git a/core/registration.c b/core/registration.c index 15f18c56..3323551e 100644 --- a/core/registration.c +++ b/core/registration.c @@ -1606,8 +1606,7 @@ static int prv_getId(uint8_t * data, } // Ignore any unrecognized attribute } - if (data[limit] == REG_ATTR_SEPARATOR) - { + if (limit < length && data[limit] == REG_ATTR_SEPARATOR) { limit += 1; } data += limit; diff --git a/core/uri.c b/core/uri.c index 3b5d4cff..421a1a95 100644 --- a/core/uri.c +++ b/core/uri.c @@ -71,6 +71,9 @@ static int prv_parseNumber(uint8_t * uriString, { result *= 10; result += uriString[*headP] - '0'; + if (result > LWM2M_MAX_ID) { + return -1; + } } else { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1a64830e..0055bbf7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -65,3 +65,5 @@ if(NOT WAKAAMA_CLIENT_INITIATED_BOOTSTRAP) LWM2M_SUPPORT_SENML_CBOR ) endif() + +add_subdirectory(fuzzing) diff --git a/tests/fuzzing/CMakeLists.txt b/tests/fuzzing/CMakeLists.txt new file mode 100644 index 00000000..d32390b4 --- /dev/null +++ b/tests/fuzzing/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.13) + + +# TODO sudo apt install libstdc++-12-dev +project(fuzzing_tests C) + +include(Fuzzing.cmake) + +add_subdirectory(coap_parse_message) +add_subdirectory(registration_handleRequest) diff --git a/tests/fuzzing/Fuzzing.cmake b/tests/fuzzing/Fuzzing.cmake new file mode 100644 index 00000000..a02d09aa --- /dev/null +++ b/tests/fuzzing/Fuzzing.cmake @@ -0,0 +1,81 @@ +if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(FUZZING_MAX_TOTAL_TIME + 180 + CACHE STRING "Maximal time (seconds) to run one fuzzing test" + ) + + add_custom_target(run_all_fuzzing_tests COMMENT "Run all fuzzing tests") + add_custom_target(run_all_fuzzing_reg_tests COMMENT "Run all fuzzing regression tests") + + # ! add_fuzzing_test : Add a fuzzing test that uses `libfuzzer` + # ~~~ + # TARGET_NAME: The name of the test + # SOURCE_FILES: A list with all source files needed for the test + # COMPILE_DEFINITIONS (optional): Compile definitions for the target + # CORPUS_DIRS (optional): A list with directories containing corpora for initializing the test runs + # MAX_LEN (optional): Maximum size (bytes) of generated test input + # CRASH_FILES_DIR (optional): Directory containing crash files for regression tests + # ~~~ + function(add_fuzzing_test) + + set(oneValueArgs TARGET_NAME MAX_LEN CRASH_FILES_DIR) + set(multiValueArgs SOURCE_FILES CORPUS_DIRS COMPILE_DEFINITIONS) + cmake_parse_arguments(FUZZING "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + add_executable(${FUZZING_TARGET_NAME}) + target_sources(${FUZZING_TARGET_NAME} PRIVATE ${FUZZING_SOURCE_FILES}) + target_compile_definitions(${FUZZING_TARGET_NAME} PRIVATE ${FUZZING_COMPILE_DEFINITIONS}) + + target_include_directories( + ${FUZZING_TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/coap ${CMAKE_SOURCE_DIR}/core + ${CMAKE_SOURCE_DIR}/include + ) + + target_compile_options( + ${FUZZING_TARGET_NAME} PRIVATE -O1 -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=all + ) + target_link_options( + ${FUZZING_TARGET_NAME} PRIVATE -O1 -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=all + ) + + set(max_len 65535) + if(FUZZING_MAX_LEN) + set(max_len ${FUZZING_MAX_LEN}) + endif() + + set(run_target run_${FUZZING_TARGET_NAME}_fuzzing) + add_custom_target( + ${run_target} + COMMAND ${FUZZING_TARGET_NAME} -max_total_time=${FUZZING_MAX_TOTAL_TIME} -max_len=${max_len} + -artifact_prefix=${CMAKE_CURRENT_LIST_DIR}/ ${FUZZING_CORPUS_DIRS} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMENT "Run a fuzzing test" + ) + add_dependencies(${run_target} ${FUZZING_TARGET_NAME}) + add_dependencies(run_all_fuzzing_tests ${run_target}) + + if(FUZZING_CRASH_FILES_DIR) + set(reg_test_target run_${FUZZING_TARGET_NAME}_fuzzing_reg_test) + file( + GLOB_RECURSE CRASH_FILES + LIST_DIRECTORIES false + CONFIGURE_DEPENDS "${FUZZING_CRASH_FILES_DIR}/*" + ) + add_custom_target( + ${reg_test_target} + COMMAND ${FUZZING_TARGET_NAME} -artifact_prefix=${CMAKE_CURRENT_LIST_DIR}/ ${CRASH_FILES} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMENT "Run fuzzing regression test" + ) + add_dependencies(${reg_test_target} ${FUZZING_TARGET_NAME}) + add_dependencies(run_all_fuzzing_reg_tests ${reg_test_target}) + + endif() + endfunction() +else() + # ! add_fuzzing_test : Dummy function that does nothing. This is needed when not compiling with `clang` and + # therefore `libfuzzer` is not available. + function(add_fuzzing_test) + # do nothing if we are not using clang + endfunction() +endif() diff --git a/tests/fuzzing/coap_parse_message/CMakeLists.txt b/tests/fuzzing/coap_parse_message/CMakeLists.txt new file mode 100644 index 00000000..927085ad --- /dev/null +++ b/tests/fuzzing/coap_parse_message/CMakeLists.txt @@ -0,0 +1,5 @@ +add_fuzzing_test( + TARGET_NAME fuz_coap_parse_message + SOURCE_FILES fuz_coap_parse_message.c ${CMAKE_SOURCE_DIR}/coap/er-coap-13/er-coap-13.c + ${CMAKE_SOURCE_DIR}/examples/shared/platform.c +) diff --git a/tests/fuzzing/coap_parse_message/fuz_coap_parse_message.c b/tests/fuzzing/coap_parse_message/fuz_coap_parse_message.c new file mode 100644 index 00000000..33f098ee --- /dev/null +++ b/tests/fuzzing/coap_parse_message/fuz_coap_parse_message.c @@ -0,0 +1,38 @@ +/******************************************************************************* + * + * Copyright (c) 2023 GARDENA GmbH + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v20.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Lukas Woodtli, GARDENA GmbH - Please refer to git log + * + *******************************************************************************/ + +#include "er-coap-13/er-coap-13.h" + +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (0 >= size || size > UINT16_MAX) { + return 0; + } + + uint8_t non_const_data[size]; + memcpy(non_const_data, data, size); + + coap_packet_t coap_pkt; + memset(&coap_pkt, 0, sizeof(coap_packet_t)); + coap_parse_message(&coap_pkt, non_const_data, (uint16_t)size); + coap_free_header(&coap_pkt); + + return 0; +} diff --git a/tests/fuzzing/registration_handleRequest/CMakeLists.txt b/tests/fuzzing/registration_handleRequest/CMakeLists.txt new file mode 100644 index 00000000..a406f9ce --- /dev/null +++ b/tests/fuzzing/registration_handleRequest/CMakeLists.txt @@ -0,0 +1,19 @@ +add_fuzzing_test( + TARGET_NAME fuz_registration_handleRequest + SOURCE_FILES + fuz_registration_handleRequest.c + ${CMAKE_SOURCE_DIR}/core/registration.c + ${CMAKE_SOURCE_DIR}/core/list.c + ${CMAKE_SOURCE_DIR}/core/utils.c + ${CMAKE_SOURCE_DIR}/core/uri.c + ${CMAKE_SOURCE_DIR}/core/observe.c + ${CMAKE_SOURCE_DIR}/core/liblwm2m.c + ${CMAKE_SOURCE_DIR}/core/packet.c + ${CMAKE_SOURCE_DIR}/core/reporting.c + ${CMAKE_SOURCE_DIR}/coap/er-coap-13/er-coap-13.c + ${CMAKE_SOURCE_DIR}/coap/block.c + ${CMAKE_SOURCE_DIR}/coap/transaction.c + ${CMAKE_SOURCE_DIR}/examples/shared/platform.c + COMPILE_DEFINITIONS LWM2M_SERVER_MODE LWM2M_COAP_DEFAULT_BLOCK_SIZE=64 + CRASH_FILES_DIR crash_files/ +) diff --git a/tests/fuzzing/registration_handleRequest/crash_files/crash-4722541711a0a70b31362a66129f8d3b27449b29 b/tests/fuzzing/registration_handleRequest/crash_files/crash-4722541711a0a70b31362a66129f8d3b27449b29 new file mode 100644 index 00000000..d57e0157 Binary files /dev/null and b/tests/fuzzing/registration_handleRequest/crash_files/crash-4722541711a0a70b31362a66129f8d3b27449b29 differ diff --git a/tests/fuzzing/registration_handleRequest/crash_files/crash-4e842fc818f746482a3bfd0029ed4b5ed5b044d3 b/tests/fuzzing/registration_handleRequest/crash_files/crash-4e842fc818f746482a3bfd0029ed4b5ed5b044d3 new file mode 100644 index 00000000..7dc4b7be --- /dev/null +++ b/tests/fuzzing/registration_handleRequest/crash_files/crash-4e842fc818f746482a3bfd0029ed4b5ed5b044d3 @@ -0,0 +1 @@ +/>/ \ No newline at end of file diff --git a/tests/fuzzing/registration_handleRequest/crash_files/crash-b609a2dd5c6a5ce4fac6b76d1fdfd2841dca4402 b/tests/fuzzing/registration_handleRequest/crash_files/crash-b609a2dd5c6a5ce4fac6b76d1fdfd2841dca4402 new file mode 100644 index 00000000..446862ce Binary files /dev/null and b/tests/fuzzing/registration_handleRequest/crash_files/crash-b609a2dd5c6a5ce4fac6b76d1fdfd2841dca4402 differ diff --git a/tests/fuzzing/registration_handleRequest/fuz_registration_handleRequest.c b/tests/fuzzing/registration_handleRequest/fuz_registration_handleRequest.c new file mode 100644 index 00000000..2ea023ab --- /dev/null +++ b/tests/fuzzing/registration_handleRequest/fuz_registration_handleRequest.c @@ -0,0 +1,93 @@ +/******************************************************************************* + * + * Copyright (c) 2023 GARDENA GmbH + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v20.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Lukas Woodtli, GARDENA GmbH - Please refer to git log + * + *******************************************************************************/ + +#include "er-coap-13/er-coap-13.h" + +#include +#include +#include + +#include "internals.h" +#include "liblwm2m.h" + +bool lwm2m_session_is_equal(void *session1, void *session2, void *userData) { + (void)userData; + + return (session1 == session2); +} + +uint8_t lwm2m_buffer_send(void *sessionH, uint8_t *buffer, size_t length, void *userdata) { + (void)sessionH; + (void)buffer; + (void)length; + (void)userdata; + + return COAP_NO_ERROR; +} + +static void init_test_packet(coap_packet_t *message, const char *registration_message, uint8_t *payload, + const size_t payload_len) { + + memset(message, 0, sizeof(coap_packet_t)); + message->code = COAP_POST; + coap_set_header_uri_query(message, "lwm2m=1.1&ep=abc"); + + memcpy(payload, registration_message, payload_len); + message->payload = payload; + message->payload_len = payload_len; + + message->content_type = (coap_content_type_t)LWM2M_CONTENT_LINK; +} + +void init_uri(lwm2m_uri_t *const uri) { + memset(uri, 0, sizeof(*uri)); + uri->objectId = LWM2M_MAX_ID; +} + +static int call_test_function(const char *reg_msg, const size_t reg_msg_len) { + int ret; + + uint8_t user_data[1] = {0}; + lwm2m_context_t *context = lwm2m_init(user_data); + + lwm2m_uri_t uri; + init_uri(&uri); + coap_packet_t message; + + uint8_t payload[reg_msg_len == 0 ? 1 : reg_msg_len]; + init_test_packet(&message, reg_msg, payload, reg_msg_len); + + coap_packet_t response; + memset(&response, 0, sizeof(coap_packet_t)); + + ret = registration_handleRequest(context, &uri, NULL, &message, &response); + + free_multi_option(message.uri_query); + free_multi_option(response.location_path); + + lwm2m_close(context); + + return ret; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + call_test_function((const char *)data, size); + + return 0; +}