Skip to content

Adding fuzz directory and modifications to support it. #614

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions client/fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
130 changes: 130 additions & 0 deletions client/fuzz/fuzz_real_query.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <mysqld_error.h>
#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;
}
3 changes: 2 additions & 1 deletion include/mysql.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion include/mysql.h.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 7 additions & 1 deletion include/violite.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/**
Expand Down Expand Up @@ -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_ */
5 changes: 5 additions & 0 deletions sql-common/client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<char *>(ER_CLIENT(CR_LOCALHOST_CONNECTION));
}
#if defined(HAVE_SYS_UN_H)
if (!net->vio &&
(!mysql->options.protocol ||
Expand Down
3 changes: 2 additions & 1 deletion vio/vio.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
36 changes: 36 additions & 0 deletions vio/viosocket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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; i<size; i++)
printf("%02x ", bufp[i]);
printf("\n");
#endif //FUZZ_DEBUG
return size;
}
#endif
/* Ensure nobody uses vio_read_buff and vio_read simultaneously. */
assert(vio->read_end == vio->read_pos);

Expand Down Expand Up @@ -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;

Expand Down