diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4933a7131029..5b99dae5ffe5 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -29,6 +29,11 @@ DISABLE_MISSING_PROFILE_WARNING() ## Subdirectory for mysql_migrate_keyring code. ADD_SUBDIRECTORY(migrate_keyring) +INCLUDE(../router/cmake/fuzzer.cmake) +IF(LIBFUZZER_COMPILE_FLAGS) + ADD_SUBDIRECTORY(fuzz) +ENDIF() + MYSQL_ADD_EXECUTABLE(mysql ${CMAKE_SOURCE_DIR}/sql-common/net_ns.cc completion_hash.cc diff --git a/client/fuzz/CMakeLists.txt b/client/fuzz/CMakeLists.txt new file mode 100644 index 000000000000..aa58e8f9d2f0 --- /dev/null +++ b/client/fuzz/CMakeLists.txt @@ -0,0 +1,8 @@ +IF(LIBFUZZER_COMPILE_FLAGS) + MYSQL_ADD_EXECUTABLE(fuzz_real_query + fuzz_real_query.cc + LINK_LIBRARIES mysqlclient + SKIP_INSTALL + ) + LIBFUZZER_ADD_TEST(fuzz_real_query) +ENDIF() diff --git a/client/fuzz/fuzz_real_query.cc b/client/fuzz/fuzz_real_query.cc new file mode 100644 index 000000000000..af6ba562fc1f --- /dev/null +++ b/client/fuzz/fuzz_real_query.cc @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "violite.h" + +using namespace std; +FILE *logfile = NULL; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + MYSQL mysql; + bool opt_cleartext = true; + unsigned int opt_ssl = SSL_MODE_DISABLED; + MYSQL_RES *result; + uint8_t op; + + if (Size < 1) { + return 0; + } + op = Data[0]; + Data++; + Size--; + if (logfile == NULL) { + logfile = fopen("/dev/null", "w"); + } + mysql_init(&mysql); + if (mysql_options(&mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, &opt_cleartext) != 0) { + abort(); + } + if (mysql_options(&mysql, MYSQL_OPT_SSL_MODE, &opt_ssl) != 0) { + abort(); + } + unsigned int my_protocol = MYSQL_PROTOCOL_FUZZ; + if (mysql_options(&mysql, MYSQL_OPT_PROTOCOL, &my_protocol) != 0) { + abort(); + } + // The fuzzing takes place on network data received from server + sock_initfuzz(Data,Size); + if (!mysql_real_connect(&mysql, "localhost", "user", "pass", "db", 0, NULL, 0)) { + goto out; + } + + mysql_info(&mysql); + mysql_ping(&mysql); + + switch (op) { + case 0: + mysql_query(&mysql, "CREATE DATABASE fuzz"); + if (mysql_query(&mysql, "SELECT * FROM crashes")) { + goto out; + } + result = mysql_store_result(&mysql); + if (result != NULL) { + mysql_result_metadata(result); + mysql_num_rows(result); + int num_fields = mysql_num_fields(result); + MYSQL_FIELD *field; + while((field = mysql_fetch_field(result))) { + fprintf(logfile, "%s\n", field->name); + } + MYSQL_ROW row = mysql_fetch_row(result); + unsigned long * lengths = mysql_fetch_lengths(result); + while (row ) { + for(int i = 0; i < num_fields; i++) { + fprintf(logfile, "length %lu, %s\n", lengths[i], row[i] ? row[i] : "NULL"); + } + row = mysql_fetch_row(result); + } + mysql_free_result(result); + } + break; + case 1: + result = mysql_list_dbs(&mysql, NULL); + if (result) { + mysql_free_result(result); + } + break; + case 2: + result = mysql_list_tables(&mysql, NULL); + if (result) { + mysql_free_result(result); + } + break; + case 3: + result = mysql_list_fields(&mysql, "table", NULL); + if (result) { + mysql_free_result(result); + } + break; + case 4: + result = mysql_list_processes(&mysql); + if (result) { + mysql_free_result(result); + } + break; + case 5: + if (mysql_query(&mysql, "INSERT INTO Fuzzers(Name) VALUES('target')") == 0) { + fprintf(logfile, "The last inserted row id is: %llu\n", mysql_insert_id(&mysql)); + fprintf(logfile, "%llu affected rows\n", mysql_affected_rows(&mysql)); + } + break; + case 6: + if (mysql_change_user(&mysql, "user", "password", "new_database")) { + goto out; + } + break; + case 7: + if (mysql_select_db(&mysql, "new_database")) { + goto out; + } + break; + } + + mysql_get_host_info(&mysql); + mysql_get_proto_info(&mysql); + mysql_get_server_info(&mysql); + mysql_get_server_version(&mysql); + mysql_dump_debug_info(&mysql); + mysql_sqlstate(&mysql); + mysql_stat(&mysql); + mysql_info(&mysql); + +out: + mysql_close(&mysql); + return 0; +} diff --git a/include/mysql.h b/include/mysql.h index ef1681da644a..384179c2096d 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -266,7 +266,8 @@ enum mysql_protocol_type { MYSQL_PROTOCOL_TCP, MYSQL_PROTOCOL_SOCKET, MYSQL_PROTOCOL_PIPE, - MYSQL_PROTOCOL_MEMORY + MYSQL_PROTOCOL_MEMORY, + MYSQL_PROTOCOL_FUZZ }; enum mysql_ssl_mode { diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 00cb5969fbc0..cd11bb1322e9 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -504,7 +504,8 @@ MYSQL_PROTOCOL_TCP, MYSQL_PROTOCOL_SOCKET, MYSQL_PROTOCOL_PIPE, - MYSQL_PROTOCOL_MEMORY + MYSQL_PROTOCOL_MEMORY, + MYSQL_PROTOCOL_FUZZ }; enum mysql_ssl_mode { SSL_MODE_DISABLED = 1, diff --git a/include/violite.h b/include/violite.h index 3a6e6bb6fab5..04eebccd9953 100644 --- a/include/violite.h +++ b/include/violite.h @@ -110,12 +110,14 @@ enum enum_vio_type : int { */ VIO_TYPE_PLUGIN = 7, + VIO_TYPE_FUZZ = 8, + FIRST_VIO_TYPE = VIO_TYPE_TCPIP, /* If a new type is added, please update LAST_VIO_TYPE. In addition, please change get_vio_type_name() in vio/vio.c to return correct name for it. */ - LAST_VIO_TYPE = VIO_TYPE_PLUGIN + LAST_VIO_TYPE = VIO_TYPE_FUZZ }; /** @@ -453,4 +455,8 @@ struct Vio { #define SSL_handle SSL * +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +void sock_initfuzz(const uint8_t *Data, size_t Size); +#endif + #endif /* vio_violite_h_ */ diff --git a/sql-common/client.cc b/sql-common/client.cc index 40a3516e2aa5..f75061421e11 100644 --- a/sql-common/client.cc +++ b/sql-common/client.cc @@ -6479,6 +6479,11 @@ static mysql_state_machine_status csm_begin_connect(mysql_async_connect *ctx) { } } #endif /* _WIN32 */ + if (!net->vio && + (mysql->options.protocol == MYSQL_PROTOCOL_FUZZ)) { + net->vio = vio_new(0, VIO_TYPE_FUZZ, 0); + ctx->host_info = const_cast(ER_CLIENT(CR_LOCALHOST_CONNECTION)); + } #if defined(HAVE_SYS_UN_H) if (!net->vio && (!mysql->options.protocol || diff --git a/vio/vio.cc b/vio/vio.cc index 0869b6ac524d..8f586bf1a35a 100644 --- a/vio/vio.cc +++ b/vio/vio.cc @@ -577,7 +577,8 @@ static const vio_string vio_type_names[] = {{"", 0}, {STRING_WITH_LEN("SSL/TLS")}, {STRING_WITH_LEN("Shared Memory")}, {STRING_WITH_LEN("Internal")}, - {STRING_WITH_LEN("Plugin")}}; + {STRING_WITH_LEN("Plugin")}, + {STRING_WITH_LEN("Fuzz")}}; void get_vio_type_name(enum enum_vio_type vio_type, const char **str, int *len) { diff --git a/vio/viosocket.cc b/vio/viosocket.cc index a499720cb39a..459d61479c47 100644 --- a/vio/viosocket.cc +++ b/vio/viosocket.cc @@ -134,11 +134,41 @@ int vio_socket_io_wait(Vio *vio, enum enum_vio_io_event event) { #define VIO_DONTWAIT 0 #endif +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +static const uint8_t *fuzzBuffer; +static size_t fuzzSize; +static size_t fuzzPos; + +void sock_initfuzz(const uint8_t *Data, size_t Size) { + fuzzPos = 0; + fuzzSize = Size; + fuzzBuffer = Data; +} +#endif + size_t vio_read(Vio *vio, uchar *buf, size_t size) { ssize_t ret; int flags = 0; DBUG_TRACE; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (vio->type == VIO_TYPE_FUZZ) { + if (size > fuzzSize - fuzzPos) { + size = fuzzSize - fuzzPos; + } + if (fuzzPos < fuzzSize) { + memcpy(buf, fuzzBuffer + fuzzPos, size); + } + fuzzPos += size; +#ifdef FUZZ_DEBUG + printf("net cli %zu ", size); + for (size_t i=0; iread_end == vio->read_pos); @@ -216,6 +246,12 @@ size_t vio_write(Vio *vio, const uchar *buf, size_t size) { int flags = 0; DBUG_TRACE; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (vio->type == VIO_TYPE_FUZZ) { + return size; + } +#endif + /* If timeout is enabled, do not block. */ if (vio->write_timeout >= 0) flags = VIO_DONTWAIT;