123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- /* Copyright 2019 Google Inc. All Rights Reserved.
- ** Licensed under the Apache License, Version 2.0 (the "License");
- **
- ** Derived from the public domain SQLite (https://sqlite.org) sources.
- */
- #include <fuzzer/FuzzedDataProvider.h>
- #include <stddef.h>
- #include <stdint.h>
- #include <string.h>
- #include <cstdlib>
- #include <iomanip>
- #include <ios>
- #include <iostream>
- #include <sstream>
- #include <string>
- #include "third_party/sqlite/sqlite3.h"
- sqlite3 *g_database = 0; /* The database connection */
- enum class ColumnType {
- kMin = 0,
- kShortNumber = 0,
- kBlob = 1,
- kString = 2,
- kAny = 3,
- kMax = 3,
- };
- enum class TableOperator : unsigned long {
- kMin = 0,
- kShadowTableMin = 0,
- kSegdir = 0,
- kContent = 1,
- kDocsize = 2,
- kSegments = 3,
- kStat = 4,
- kShadowTableMax = 4,
- // 5-7: reserved for further use
- kFuzzTable = 8,
- kMax = 8,
- };
- typedef struct {
- uint8_t op_type : 4;
- uint8_t column_op : 4;
- uint8_t op_sql_operation : 4;
- uint8_t select_operator_1 : 1;
- uint8_t select_operator_2 : 3;
- } opdata_16;
- /*
- ** Callback for sqlite3_exec().
- */
- static int ExecHandler(void *pCnt, int argc, char **argv, char **namev) {
- return ((*static_cast<int *>(pCnt))--) <= 0;
- }
- size_t GetOperator16(FuzzedDataProvider *data_provider, opdata_16 *op16) {
- if (data_provider->remaining_bytes() < sizeof(uint16_t))
- return 0;
- uint16_t operator_data = data_provider->ConsumeIntegral<uint16_t>();
- memcpy(op16, &operator_data, sizeof(uint16_t));
- return sizeof(uint16_t);
- }
- std::string GetValueByType(FuzzedDataProvider *data_provider, ColumnType type) {
- std::string out;
- std::ostringstream ss;
- switch (type) {
- case ColumnType::kShortNumber: {
- uint8_t number = data_provider->ConsumeIntegral<uint8_t>();
- ss << static_cast<uint16_t>(number);
- } break;
- case ColumnType::kBlob: {
- uint16_t length = data_provider->ConsumeIntegral<uint16_t>();
- uint16_t value = 0;
- if (length) {
- ss << "x'" << std::hex;
- for (uint16_t i = 0; i < length; i++) {
- value = data_provider->ConsumeIntegral<uint8_t>();
- if (data_provider->remaining_bytes() == 0)
- break;
- ss << std::setfill('0') << std::setw(2) << value;
- }
- ss << "'";
- } else
- return "x'00'";
- } break;
- default:
- return "'NOT SUPPORTED'";
- }
- out = ss.str();
- return out;
- }
- void RunSqlQuery(std::string &query, int *exec_count) {
- static bool should_print = ::getenv("DUMP_NATIVE_INPUT");
- if (should_print)
- std::cout << query << std::endl;
- char *zErrMsg = 0; /* Error message returned by sqlite_exec() */
- sqlite3_exec(g_database, query.c_str(), ExecHandler,
- static_cast<void *>(exec_count), &zErrMsg);
- sqlite3_free(zErrMsg);
- }
- int InitializeDB(FuzzedDataProvider *data_provider, int *exec_count) {
- int rc; /* Return code from various interfaces */
- const char *icu_list[219] = {
- "af_NA", "af_ZA", "ar_AE", "ar_BH", "ar_DZ",
- "ar_EG", "ar_IQ", "ar_JO", "ar_KW", "ar_LB",
- "ar_LY", "ar_MA", "ar_OM", "ar_QA", "ar_SA",
- "ar_SD", "ar_SY", "ar_TN", "ar_YE", "as_IN",
- "az_Latn", "az_Latn_AZ", "be_BY", "bg_BG", "bn_BD",
- "bn_IN", "bs_BA", "ca_ES", "chr", "chr_US",
- "cs_CZ", "cy_GB", "da_DK", "de_AT", "de_BE",
- "de_CH", "de_DE", "de_LI", "de_LU", "el_CY",
- "el_GR", "en", "en_AS", "en_AU", "en_BE",
- "en_BW", "en_BZ", "en_CA", "en_GB", "en_GU",
- "en_HK", "en_IE", "en_IN", "en_JM", "en_MH",
- "en_MP", "en_MT", "en_MU", "en_NA", "en_NZ",
- "en_PH", "en_PK", "en_SG", "en_TT", "en_UM",
- "en_US", "en_US_POSIX", "en_VI", "en_ZA", "en_ZW",
- "es_419", "es_AR", "es_BO", "es_CL", "es_CO",
- "es_CR", "es_DO", "es_EC", "es_ES", "es_GQ",
- "es_GT", "es_HN", "es_MX", "es_NI", "es_PA",
- "es_PE", "es_PR", "es_PY", "es_SV", "es_US",
- "es_UY", "es_VE", "et_EE", "fa_IR", "fi_FI",
- "fil_PH", "fo_FO", "fr_BE", "fr_BF", "fr_BI",
- "fr_BJ", "fr_BL", "fr_CA", "fr_CD", "fr_CF",
- "fr_CG", "fr_CH", "fr_CI", "fr_CM", "fr_DJ",
- "fr_FR", "fr_GA", "fr_GN", "fr_GP", "fr_GQ",
- "fr_KM", "fr_LU", "fr_MC", "fr_MF", "fr_MG",
- "fr_ML", "fr_MQ", "fr_NE", "fr_RE", "fr_RW",
- "fr_SN", "fr_TD", "fr_TG", "ga", "ga_IE",
- "gu_IN", "ha_Latn", "ha_Latn_GH", "ha_Latn_NE", "ha_Latn_NG",
- "he_IL", "hi_IN", "hr_HR", "hu_HU", "hy_AM",
- "id", "id_ID", "ig_NG", "is_IS", "it",
- "it_CH", "it_IT", "ja_JP", "ka", "ka_GE",
- "kk_KZ", "kl_GL", "kn_IN", "ko_KR", "kok_IN",
- "lt_LT", "lv_LV", "mk_MK", "ml_IN", "mr_IN",
- "ms", "ms_BN", "ms_MY", "mt_MT", "nb_NO",
- "nlnl_BE", "nl_NL", "nn_NO", "om_ET", "om_KE",
- "or_IN", "pa_Arab", "pa_Arab_PK", "pa_Guru", "pa_Guru_IN",
- "pl_PL", "ps_AF", "pt", "pt_BR", "pt_PT",
- "ro_MD", "ro_RO", "ru_MD", "ru_RU", "ru_UA",
- "si_LK", "sk_SK", "sl_SI", "sq_AL", "sr_Cyrl",
- "sr_Cyrl_BA", "sr_Cyrl_ME", "sr_Cyrl_RS", "sr_Latn_BA", "sr_Latn_ME",
- "sr_Latn_RS", "sv_FI", "sv_SE", "sw", "sw_KE",
- "sw_TZ", "ta_IN", "ta_LK", "te_IN", "th_TH",
- "tr_TR", "uk_UA", "ur_IN", "ur_PK", "vi_VN",
- "yo_NG", "zh_Hans", "zh_Hans_CN", "zh_Hans_SG", "zh_Hant_HK",
- "zh_Hant_MO", "zh_Hant_TW", "zu", "zu_ZA"};
- rc = sqlite3_initialize();
- if (rc)
- return rc;
- /* Open the database connection. Only use an in-memory database. */
- rc = sqlite3_open_v2(
- "fuzz.db", &g_database,
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY, 0);
- if (rc)
- return rc;
- /* enables foreign key constraints */
- sqlite3_db_config(g_database, SQLITE_DBCONFIG_ENABLE_FKEY, 1, &rc);
- /* disable defence-in-depth to simplify the fuzzing on shadow tables */
- sqlite3_db_config(g_database, SQLITE_DBCONFIG_DEFENSIVE, 0, &rc);
- if (rc)
- return rc;
- /* determine a limit on the number of output rows */
- *exec_count = 0x3f;
- /* Some initial queries */
- std::string init_drop_db("DROP TABLE IF EXISTS f;");
- std::string init_drop_docsize("DROP TABLE IF EXISTS 'f_docsize';");
- std::string init_drop_stat("DROP TABLE IF EXISTS 'f_stat';");
- std::string init_create_fts3 = "CREATE VIRTUAL TABLE f USING fts3(a,b";
- switch (data_provider->ConsumeIntegralInRange<uint8_t>(0, 3)) {
- case 1:
- init_create_fts3 += ",tokenize=porter";
- break;
- case 2:
- init_create_fts3 += ",tokenize=icu ";
- init_create_fts3 +=
- icu_list[(data_provider->ConsumeIntegralInRange<uint8_t>(
- 0, ((sizeof(icu_list) / sizeof(const char *) % 0x100) - 1)))];
- break;
- case 3:
- init_create_fts3 += ",tokenize=icu";
- break;
- default:
- case 0:
- /*if we don't set tokenizer to anything, it will goes default simple
- * tokenizer*/
- break;
- }
- init_create_fts3 += ");";
- std::string create_fake_docsize(
- "CREATE TABLE 'f_docsize'(docid INTEGER PRIMARY KEY, size BLOB);");
- std::string create_fake_stat(
- "CREATE TABLE 'f_stat'(id INTEGER PRIMARY KEY, value BLOB);");
- std::string init_set_initial_data("INSERT INTO f VALUES (1, '1234');");
- RunSqlQuery(init_drop_db, exec_count);
- RunSqlQuery(init_drop_docsize, exec_count);
- RunSqlQuery(init_drop_stat, exec_count);
- RunSqlQuery(init_create_fts3, exec_count);
- RunSqlQuery(create_fake_docsize, exec_count);
- RunSqlQuery(create_fake_stat, exec_count);
- RunSqlQuery(init_set_initial_data, exec_count);
- return rc;
- }
- /*
- ** Main entry point. The fuzzer invokes this function with each
- ** fuzzed input.
- */
- extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
- if (size < 3 || size > 0x1000000)
- return 0; /* Early out if unsufficient or too much data */
- FuzzedDataProvider data_provider(data, size);
- int exec_count = 0; /* Abort row callback when count reaches zero */
- if (InitializeDB(&data_provider, &exec_count))
- return 0;
- while (1) {
- std::string op, target, target_column;
- ColumnType target_column_type = ColumnType::kMin;
- opdata_16 op_16;
- size_t ret = GetOperator16(&data_provider, &op_16);
- if (ret == 0)
- break;
- unsigned long table_operator_border =
- static_cast<unsigned long>(TableOperator::kMax) + 1;
- TableOperator op_type =
- static_cast<TableOperator>(op_16.op_type % table_operator_border);
- /*
- 1. choose a table and a column to fuzz.
- */
- bool op_on_shadow = (op_type <= TableOperator::kShadowTableMax);
- if (op_on_shadow) {
- switch (op_type) {
- case TableOperator::kSegdir:
- target = "f_segdir";
- switch (op_16.column_op % 5) {
- case 0x00:
- target_column = " level ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x01:
- target_column = " start_block ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x02:
- target_column = " end_block ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x03:
- target_column = " leaves_end_block ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x04:
- target_column = " root ";
- target_column_type = ColumnType::kBlob;
- break;
- }
- break;
- case TableOperator::kContent:
- target = "f_content";
- switch (op_16.column_op % 3) {
- case 0x00:
- target_column = " docid ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x01:
- target_column = " 'c0a' ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x02:
- target_column = " 'c1b' ";
- target_column_type = ColumnType::kBlob;
- break;
- }
- break;
- case TableOperator::kDocsize:
- target = "f_docsize";
- switch (op_16.column_op % 2) {
- case 0x00:
- target_column = " docid ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x01:
- target_column = " size ";
- target_column_type = ColumnType::kBlob;
- break;
- }
- break;
- case TableOperator::kSegments:
- target = "f_segments";
- switch (op_16.column_op % 2) {
- case 0x00:
- target_column = " blockid ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x01:
- target_column = " block ";
- target_column_type = ColumnType::kBlob;
- break;
- }
- break;
- case TableOperator::kStat:
- default:
- target = "f_stat";
- switch (op_16.column_op % 2) {
- case 0x00:
- target_column = " id ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x01:
- target_column = " value ";
- target_column_type = ColumnType::kBlob;
- break;
- }
- break;
- }
- } else {
- target = "f";
- switch (op_16.column_op % 2) {
- case 0x00:
- target_column = " a ";
- target_column_type = ColumnType::kShortNumber;
- break;
- case 0x01:
- target_column = " b ";
- target_column_type = ColumnType::kBlob;
- break;
- }
- op_type = TableOperator::kFuzzTable;
- }
- /*
- 2. choose a verb and generate some data if needed
- */
- switch (op_16.op_sql_operation % 6) {
- case 0x01:
- op = "UPDATE " + target + " SET " + target_column + " = ";
- op += GetValueByType(&data_provider, target_column_type);
- op += " WHERE " + target_column + " IN (SELECT " + target_column;
- op += " FROM " + target + " LIMIT 1 OFFSET ";
- op += GetValueByType(&data_provider, ColumnType::kShortNumber);
- op += ");";
- break;
- case 0x00:
- case 0x02: {
- std::ostringstream ss;
- switch (op_type) {
- case TableOperator::kSegdir:
- ss << GetValueByType(&data_provider, ColumnType::kShortNumber) << ","
- << GetValueByType(&data_provider, ColumnType::kShortNumber) << ","
- << GetValueByType(&data_provider, ColumnType::kShortNumber) << ","
- << GetValueByType(&data_provider, ColumnType::kShortNumber) << ",'"
- << GetValueByType(&data_provider, ColumnType::kShortNumber) << " "
- << GetValueByType(&data_provider, ColumnType::kShortNumber) << "',"
- << GetValueByType(&data_provider, ColumnType::kBlob);
- break;
- case TableOperator::kContent:
- ss << GetValueByType(&data_provider, ColumnType::kShortNumber) << ","
- << GetValueByType(&data_provider, ColumnType::kShortNumber) << ","
- << GetValueByType(&data_provider, ColumnType::kBlob);
- break;
- default:
- /*
- All other tables have the same type, so to simplify, use this
- default instead.
- BLOB is almost the same as STRING and it avoids the annoying
- encoding problem, so choose BLOB instead when some columns
- need STRING
- */
- ss << GetValueByType(&data_provider, ColumnType::kShortNumber) << ","
- << GetValueByType(&data_provider, ColumnType::kBlob);
- break;
- }
- op = "INSERT INTO " + target + " VALUES (" + ss.str() + ");";
- } break;
- case 0x03:
- op = "DELETE FROM f WHERE ";
- op += (op_16.select_operator_1 ? "a" : "b");
- op += "=";
- op += GetValueByType(&data_provider, op_16.select_operator_1
- ? ColumnType::kShortNumber
- : ColumnType::kBlob);
- op += ";";
- break;
- case 0x04: {
- uint8_t selector_indicator =
- data_provider.ConsumeIntegralInRange<uint8_t>(0, 4);
- std::string selector_string;
- switch (selector_indicator) {
- case 0x00: {
- selector_string = " matchinfo( f , '";
- uint8_t matchinfo_argdata = data_provider.ConsumeIntegral<uint8_t>();
- if (matchinfo_argdata == 0) {
- selector_string += "pcx"; // default value
- }
- /* to simplify I removed y, because it is almost the same as 'x'
- * =>https://www.sqlite.org/fts3.html#matchinfo */
- const char matchinfo_args[8] = {'p', 'c', 'n', 'a', 'l', 's', 'x', 'b'};
- size_t matchinfo_counter = 0;
- while (matchinfo_argdata > 0 &&
- matchinfo_counter < sizeof(matchinfo_args)) {
- if (matchinfo_argdata % 2) {
- selector_string += matchinfo_args[matchinfo_counter];
- }
- matchinfo_argdata >>= 1;
- matchinfo_counter++;
- }
- selector_string += "') ";
- break;
- }
- case 0x01:
- selector_string += " snippet(f) ";
- break;
- case 0x02:
- selector_string += " snippet(f, x'";
- selector_string += GetValueByType(&data_provider, ColumnType::kBlob);
- selector_string += "', x'";
- selector_string += GetValueByType(&data_provider, ColumnType::kBlob);
- selector_string += "', x'";
- selector_string += GetValueByType(&data_provider, ColumnType::kBlob);
- selector_string += "') ";
- break;
- case 0x03:
- selector_string += " offsets(f) ";
- break;
- case 0x04:
- selector_string += " * ";
- break;
- }
- op = "SELECT " + selector_string + " FROM f WHERE ";
- op += (op_16.select_operator_1 ? "a" : "b");
- uint16_t processor_byte =
- 0; /* don't read a byte when it compares with = (eq)*/
- uint8_t current_bits = 0; /* 4 bits of data, maximum = 15*/
- switch (op_16.select_operator_2 % 4) {
- case 0: {
- bool need_string = true;
- uint8_t match_length = data_provider.ConsumeIntegralInRange<uint8_t>(
- 1, 10); /* parts involved in match */
- op += " MATCH ";
- while (match_length && data_provider.remaining_bytes()) {
- if (need_string) {
- /* asterisk cases like MATCH 'a*' will be automatically covered in
- * this BLOB */
- op += GetValueByType(&data_provider, ColumnType::kBlob);
- need_string = false;
- } else {
- /*
- ignoring brackets to simplify the logic since they can be
- converted into the equal forms.
- eg: a AND (b OR c) == b OR c AND a
- */
- switch (data_provider.ConsumeIntegralInRange<uint8_t>(0, 3)) {
- case 0:
- op += " AND ";
- break;
- case 1:
- op += " OR ";
- break;
- case 2:
- op += " NEAR ";
- break;
- default:
- case 3:
- op += " NEAR/";
- op += GetValueByType(&data_provider, ColumnType::kShortNumber);
- op += " ";
- break;
- }
- need_string = true;
- }
- match_length--;
- }
- if (need_string) {
- /* asterisk cases like MATCH 'a*' will be automatically covered in
- * this BLOB */
- op += GetValueByType(&data_provider, ColumnType::kBlob);
- need_string = false;
- }
- op += ";";
- } break;
- case 1:
- processor_byte = data_provider.ConsumeIntegral<uint16_t>();
- op += " LIKE ";
- while (processor_byte) {
- current_bits = processor_byte % (1 << 4);
- processor_byte >>= 4;
- switch (current_bits) {
- case 0:
- op += "%";
- break;
- case 1:
- op += "_";
- break;
- case 2:
- op += " ";
- break;
- default:
- op += ('a' + current_bits);
- break;
- }
- }
- op += "';";
- break;
- case 2:
- op += " = 'a b';";
- break;
- case 3:
- processor_byte = data_provider.ConsumeIntegral<uint16_t>();
- op += " GLOB '";
- while (processor_byte) {
- current_bits = processor_byte % (1 << 4);
- processor_byte >>= 4;
- switch (current_bits) {
- case 0:
- op += "*";
- break;
- case 1:
- op += "?";
- break;
- case 2:
- op += "[AB]";
- break;
- case 3:
- op += "[0-9]";
- break;
- case 4:
- op += "[!A]";
- break;
- case 5:
- op += "[!3-5]";
- break;
- case 6:
- op += "\\";
- break;
- case 7:
- op += "/";
- break;
- default: // alphabets
- op += ('a' + current_bits);
- break;
- }
- }
- op += "';";
- break;
- } /* end of switch (op_16.select_operator_2 % 4) */
- break;
- }
- case 0x05: {
- std::string command;
- std::ostringstream ss;
- uint8_t command_operator =
- (op_16.select_operator_2 << 1) + op_16.select_operator_1;
- switch (command_operator % 5) {
- case 0x00:
- command = "optimize";
- break;
- case 0x01:
- command = "rebuild";
- break;
- case 0x02:
- command = "integrity-check";
- break;
- case 0x03:
- ss << GetValueByType(&data_provider, ColumnType::kShortNumber) << ','
- << GetValueByType(&data_provider, ColumnType::kShortNumber);
- command = "merge=";
- command += ss.str();
- break;
- case 0x04:
- ss << GetValueByType(&data_provider, ColumnType::kShortNumber);
- command = "automerge=";
- command += ss.str();
- break;
- }
- op = "INSERT INTO f(f) VALUES ('" + command + "');";
- } break;
- }
- RunSqlQuery(op, &exec_count);
- }
- /* Cleanup and return */
- sqlite3_close(g_database);
- return 0;
- }
|