From 28ac9328ca8cb78f4287ae9a770be602847f0743 Mon Sep 17 00:00:00 2001
From: Sergey Bronnikov <sergeyb@tarantool.org>
Date: Wed, 12 Jan 2022 15:06:23 +0300
Subject: [PATCH] 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_id
- xrow_decode_raft
- xrow_decode_sql
- xrow_decode_watch
- xrow_greeting_decode

NO_DOC=testing
NO_CHANGELOG=testing

(cherry picked from commit 46cacf35bd6454102a6f8e3e47281f66cc3fdd8c)
---
 src/box/xrow.c                          |  6 ++--
 test/fuzz/CMakeLists.txt                | 40 +++++++++++++++++++++
 test/fuzz/xrow_decode_auth_fuzzer.c     | 45 ++++++++++++++++++++++++
 test/fuzz/xrow_decode_begin_fuzzer.c    | 46 +++++++++++++++++++++++++
 test/fuzz/xrow_decode_call_fuzzer.c     | 46 +++++++++++++++++++++++++
 test/fuzz/xrow_decode_dml_fuzzer.c      | 46 +++++++++++++++++++++++++
 test/fuzz/xrow_decode_id_fuzzer.c       | 46 +++++++++++++++++++++++++
 test/fuzz/xrow_decode_raft_fuzzer.c     | 46 +++++++++++++++++++++++++
 test/fuzz/xrow_decode_sql_fuzzer.c      | 46 +++++++++++++++++++++++++
 test/fuzz/xrow_decode_watch_fuzzer.c    | 45 ++++++++++++++++++++++++
 test/fuzz/xrow_greeting_decode_fuzzer.c | 33 ++++++++++++++++++
 11 files changed, 443 insertions(+), 2 deletions(-)
 create mode 100644 test/fuzz/xrow_decode_auth_fuzzer.c
 create mode 100644 test/fuzz/xrow_decode_begin_fuzzer.c
 create mode 100644 test/fuzz/xrow_decode_call_fuzzer.c
 create mode 100644 test/fuzz/xrow_decode_dml_fuzzer.c
 create mode 100644 test/fuzz/xrow_decode_id_fuzzer.c
 create mode 100644 test/fuzz/xrow_decode_raft_fuzzer.c
 create mode 100644 test/fuzz/xrow_decode_sql_fuzzer.c
 create mode 100644 test/fuzz/xrow_decode_watch_fuzzer.c
 create mode 100644 test/fuzz/xrow_greeting_decode_fuzzer.c

diff --git a/src/box/xrow.c b/src/box/xrow.c
index 6e75892314..e8d5dd4462 100644
--- a/src/box/xrow.c
+++ b/src/box/xrow.c
@@ -1403,7 +1403,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");
@@ -1753,7 +1754,8 @@ xrow_decode_error(const struct xrow_header *row)
 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. */
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 183b676e76..6ad7a203be 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -93,6 +93,46 @@ 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_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_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.
diff --git a/test/fuzz/xrow_decode_auth_fuzzer.c b/test/fuzz/xrow_decode_auth_fuzzer.c
new file mode 100644
index 0000000000..7468b3a635
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_decode_begin_fuzzer.c b/test/fuzz/xrow_decode_begin_fuzzer.c
new file mode 100644
index 0000000000..cdb597cfb2
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_decode_call_fuzzer.c b/test/fuzz/xrow_decode_call_fuzzer.c
new file mode 100644
index 0000000000..63330b4de8
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_decode_dml_fuzzer.c b/test/fuzz/xrow_decode_dml_fuzzer.c
new file mode 100644
index 0000000000..4486f31370
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_decode_id_fuzzer.c b/test/fuzz/xrow_decode_id_fuzzer.c
new file mode 100644
index 0000000000..087b76d239
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_decode_raft_fuzzer.c b/test/fuzz/xrow_decode_raft_fuzzer.c
new file mode 100644
index 0000000000..554abb8dbb
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_decode_sql_fuzzer.c b/test/fuzz/xrow_decode_sql_fuzzer.c
new file mode 100644
index 0000000000..4cdc1e3704
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_decode_watch_fuzzer.c b/test/fuzz/xrow_decode_watch_fuzzer.c
new file mode 100644
index 0000000000..c76c50e8c5
--- /dev/null
+++ b/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;
+}
diff --git a/test/fuzz/xrow_greeting_decode_fuzzer.c b/test/fuzz/xrow_greeting_decode_fuzzer.c
new file mode 100644
index 0000000000..88f9b619aa
--- /dev/null
+++ b/test/fuzz/xrow_greeting_decode_fuzzer.c
@@ -0,0 +1,33 @@
+#include <stdint.h>
+
+#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;
+}
-- 
GitLab