commit 187a2bf9abeb4a11e1e8a28ca012242e8776eb99 from: Sergey Bronnikov date: Tue Aug 15 14:01:42 2023 UTC test/fuzz: add fuzzing tests for IPROTO decoders Examples of IPROTO decoding issues: #3900, #1928, #6781. Patch adds a number of fuzzing tests that covers IPROTO decoding: - xrow_decode_auth - xrow_decode_begin - xrow_decode_call - xrow_decode_dml - xrow_decode_error - xrow_decode_id - xrow_decode_raft - xrow_decode_sql - xrow_decode_watch - xrow_greeting_decode - xrow_header_decode NO_DOC=testing NO_CHANGELOG=testing commit - 0cb91010c2a52e1ff3d7d6d467a71e7117778413 commit + 187a2bf9abeb4a11e1e8a28ca012242e8776eb99 blob - 140dc7b365b0e06e287773553086811bfb255770 blob + f48caee010865e8a964ca76ea429cad5ceca65b3 --- src/box/xrow.c +++ src/box/xrow.c @@ -1436,7 +1436,8 @@ int xrow_decode_raft(const struct xrow_header *row, struct raft_request *r, struct vclock *vclock) { - assert(row->type == IPROTO_RAFT); + if (row->type != IPROTO_RAFT) + goto bad_msgpack; if (row->bodycnt != 1 || row->group_id != GROUP_LOCAL) { diag_set(ClientError, ER_INVALID_MSGPACK, "malformed raft request"); @@ -1786,7 +1787,8 @@ error: int xrow_decode_begin(const struct xrow_header *row, struct begin_request *request) { - assert(row->type == IPROTO_BEGIN); + if (row->type != IPROTO_BEGIN) + goto bad_msgpack; memset(request, 0, sizeof(*request)); /** Request without extra options. */ blob - e7fc4a50f497b9523361483956a099e883985bac blob + 81500c2d555ea82614997983c066c9c8f74b2d67 --- test/fuzz/CMakeLists.txt +++ test/fuzz/CMakeLists.txt @@ -93,6 +93,54 @@ create_fuzz_test(PREFIX mp_datetime LIBRARIES core fuzzer_config ) +# Building xrow library triggers building LuaJIT that doesn't support UBSan. +# See https://github.com/tarantool/tarantool/issues/8473. +if (NOT ENABLE_UB_SANITIZER) + create_fuzz_test(PREFIX xrow_greeting_decode + SOURCES xrow_greeting_decode_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_id + SOURCES xrow_decode_id_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_header_decode + SOURCES xrow_header_decode_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_auth + SOURCES xrow_decode_auth_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_begin + SOURCES xrow_decode_begin_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_call + SOURCES xrow_decode_call_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_dml + SOURCES xrow_decode_dml_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_error + SOURCES xrow_decode_error_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_raft + SOURCES xrow_decode_raft_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_sql + SOURCES xrow_decode_sql_fuzzer.c + LIBRARIES xrow fuzzer_config) + + create_fuzz_test(PREFIX xrow_decode_watch + SOURCES xrow_decode_watch_fuzzer.c + LIBRARIES xrow fuzzer_config) +endif () + include(ProtobufMutator) # UndefinedBehaviorSanitizer is not supported in LuaJIT. blob - /dev/null blob + 7468b3a635994d2dcf34cc38ee9f2bf21ce0ab53 (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_auth_fuzzer.c @@ -0,0 +1,45 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + row.type = IPROTO_OK; + + struct auth_request request = {0}; + xrow_decode_auth(&row, &request); + + return 0; +} blob - /dev/null blob + cdb597cfb2d24c2392557d24dc862f61467f77fe (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_begin_fuzzer.c @@ -0,0 +1,46 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + row.type = IPROTO_BEGIN; + + struct begin_request request = {0}; + if (xrow_decode_begin(&row, &request) == -1) + return -1; + + return 0; +} blob - /dev/null blob + 63330b4de810e59d1f6cf76b6f1ad6f09348ff4c (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_call_fuzzer.c @@ -0,0 +1,46 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + + struct xrow_header row = {0}; + row.type = IPROTO_CALL; /* TODO: IPROTO_CALL_16 */ + row.body[0] = body; + row.bodycnt = 1; + + struct call_request request = {0}; + xrow_decode_call(&row, &request); + + return 0; +} blob - /dev/null blob + 4486f313702408ea2f3b11979130801d518791c5 (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_dml_fuzzer.c @@ -0,0 +1,46 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + + struct request request = {0}; + if (xrow_decode_dml(&row, &request, 0) == -1) + return -1; + + return 0; +} blob - /dev/null blob + 5d6a6e14d5145a576b0e169edb5d5e2ca08c8e7a (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_error_fuzzer.c @@ -0,0 +1,47 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + + xrow_decode_error(&row); + + diag_destroy(diag_get()); + assert(diag_is_empty(diag_get()) == true); + + return 0; +} blob - /dev/null blob + 087b76d239191b4776c409a472ae88c6fe3cc040 (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_id_fuzzer.c @@ -0,0 +1,46 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + + struct id_request request = {0}; + if (xrow_decode_id(&row, &request) == -1) + return -1; + + return 0; +} blob - /dev/null blob + 554abb8dbbf92ff0fed9b99dee6a95e813587c19 (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_raft_fuzzer.c @@ -0,0 +1,46 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + + struct raft_request request = {0}; + struct vclock vclock = {0}; + xrow_decode_raft(&row, &request, &vclock); + + return 0; +} blob - /dev/null blob + 4cdc1e370433d78e7290191617481edf7cf4b998 (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_sql_fuzzer.c @@ -0,0 +1,46 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + + struct sql_request request = {0}; + if (xrow_decode_sql(&row, &request) == -1) + return -1; + + return 0; +} blob - /dev/null blob + c76c50e8c5f10f572643341f86c853c6663b88da (mode 644) --- /dev/null +++ test/fuzz/xrow_decode_watch_fuzzer.c @@ -0,0 +1,45 @@ +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + struct iovec body = {0}; + body.iov_base = (void *)data; + body.iov_len = size; + + struct xrow_header row = {0}; + row.body[0] = body; + row.bodycnt = 1; + + struct watch_request request = {0}; + xrow_decode_watch(&row, &request); + + return 0; +} blob - /dev/null blob + 88f9b619aa28b07d8896c3070b50436bcd68d721 (mode 644) --- /dev/null +++ test/fuzz/xrow_greeting_decode_fuzzer.c @@ -0,0 +1,33 @@ +#include + +#include "box/xrow.h" +#include "box/iproto_constants.h" +#include "trivia/util.h" + +void +cord_on_yield(void) {} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *d = (const char *)data; + const char *end = (const char *)data + size; + if (mp_check(&d, end) != 0) + return -1; + + if (size < IPROTO_GREETING_SIZE + 1) + return -1; + + char *greetingbuf = xcalloc(size + 1, sizeof(char)); + if (greetingbuf == NULL) + return 0; + memcpy(greetingbuf, data, size); + greetingbuf[size] = '\0'; + + struct greeting greeting = {0}; + greeting_decode(greetingbuf, &greeting); + + free(greetingbuf); + + return 0; +} blob - /dev/null blob + 32dd122c5fc94671f8cb028cd8dbee1a0b1ba2e8 (mode 644) --- /dev/null +++ test/fuzz/xrow_header_decode_fuzzer.c @@ -0,0 +1,51 @@ +#include "box/iproto_constants.h" +#include "box/xrow.h" +#include "trivia/util.h" +#include "fiber.h" +#include "memory.h" + +void +cord_on_yield(void) {} + +__attribute__((constructor)) +static void +setup(void) +{ + memory_init(); + fiber_init(fiber_c_invoke); +} + +__attribute__((destructor)) +static void +teardown(void) +{ + fiber_free(); + memory_free(); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + const char *p = (const char *)data; + const char *pe = (const char *)data + size; + if (mp_check(&p, pe) != 0) + return -1; + + char *buf = xcalloc(size, sizeof(char)); + if (buf == NULL) + return 0; + memcpy(buf, data, size); + + struct xrow_header header; + const char *pos = mp_encode_uint(buf, size); + if (!pos) + return 0; + + const char *end = pos + size; + if (xrow_header_decode(&header, &pos, end, false) == -1) + return -1; + + free(buf); + + return 0; +}