Finding a Bug in SQLite by Fuzzing osquery


Background

While fuzzing osquery using afl, I encountered a bug in SQLite.

Investigation of the Bug

The bug was reproducible using printf "select * from routes where destination ;VACUUM '::1';\n" | osqueryi. Using osquery binaries compiled with ASan, I got a heap-use-after-free report when running the osqueryi program as demonstrated in the previous sentence.

The following is the ASan output:

=================================================================
==1605==ERROR: AddressSanitizer: heap-use-after-free on address 0x61100000c780 at pc 0x7f9715797141 bp 0x7ffd45cd3050 sp 0x7ffd45cd3048
READ of size 8 at 0x61100000c780 thread T0
    #0 0x7f9715797140 in std::_Hashtable<unsigned long, std::pair<unsigned long const, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > > >, std::allocator<std::pair<unsigned long const, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > > > >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_begin() const /usr/local/osquery/Cellar/gcc/5.3.0/lib64/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/hashtable.h:370:58
    #1 0x7f9715797140 in std::_Hashtable<unsigned long, std::pair<unsigned long const, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > > >, std::allocator<std::pair<unsigned long const, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > > > >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::clear() /usr/local/osquery/Cellar/gcc/5.3.0/lib64/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/hashtable.h:1914
    #2 0x7f9715797140 in std::unordered_map<unsigned long, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > >, std::hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > > > > >::clear() /usr/local/osquery/Cellar/gcc/5.3.0/lib64/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/unordered_map.h:568
    #3 0x7f9715797140 in osquery::SQLiteDBInstance::clearAffectedTables() /vagrant/osquery/sql/sqlite_util.cpp:226
    #4 0x7f9715796e2b in osquery::SQLiteDBInstance::clearAffectedTables() /vagrant/osquery/sql/sqlite_util.cpp:221:5
    #5 0x7f9714a1dcb6 in shell_exec(char const*, int (*)(void*, int, char**, char**, int*), callback_data*, char**) /vagrant/osquery/devtools/shell.cpp:900:3
    #6 0x7f9714a218d0 in osquery::runQuery(callback_data*, char const*) /vagrant/osquery/devtools/shell.cpp:1603:12
    #7 0x7f9714a218d0 in osquery::launchIntoShell(int, char**) /vagrant/osquery/devtools/shell.cpp:1690
    #8 0x7f9714a4ca7e in main /vagrant/osquery/main/shell.cpp:117:15
    #9 0x7f97134a1f44 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21f44)
    #10 0x7f9714953d27 in _start (/vagrant/build/trusty/osquery/osqueryi+0x6a6d27)

0x61100000c780 is located 128 bytes inside of 216-byte region [0x61100000c700,0x61100000c7d8)
freed by thread T0 here:
    #0 0x7f9714a160bb in operator delete(void*) /tmp/llvm-20160901-473558-1grod1w/llvm-3.8.1.src/projects/compiler-rt/lib/asan/asan_new_delete.cc:94:3
    #1 0x7f97157fb341 in osquery::tables::sqlite::xDestroy(sqlite3_vtab*) /vagrant/osquery/sql/virtual_table.cpp:115:3

previously allocated by thread T0 here:
    #0 0x7f9714a15afb in operator new(unsigned long) /tmp/llvm-20160901-473558-1grod1w/llvm-3.8.1.src/projects/compiler-rt/lib/asan/asan_new_delete.cc:62:35
    #1 0x7f97157fb863 in osquery::tables::sqlite::xCreate(sqlite3*, void*, int, char const* const*, sqlite3_vtab**, char**) /vagrant/osquery/sql/virtual_table.cpp:145:20
    #2 0x7f9714c649ba in vtabCallConstructor /vagrant/third-party/sqlite3/sqlite3.c:121711:8
    #3 0x7f9714bba8eb in sqlite3VtabCallCreate /vagrant/third-party/sqlite3/sqlite3.c:121880:10
    #4 0x7f9714bba8eb in sqlite3VdbeExec /vagrant/third-party/sqlite3/sqlite3.c:83144
    #5 0x7f9714a9bdea in sqlite3Step /vagrant/third-party/sqlite3/sqlite3.c:75177:10
    #6 0x7f9714a9bdea in sqlite3_step /vagrant/third-party/sqlite3/sqlite3.c:75238
    #7 0x7f9714ac51c4 in sqlite3_exec /vagrant/third-party/sqlite3/sqlite3.c:108218:12
    #8 0x7f9715807cc4 in osquery::attachTableInternal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::shared_ptr<osquery::SQLiteDBInstance> const&) /vagrant/osquery/sql/virtual_table.cpp:563:10
    #9 0x7f97158150ad in osquery::attachVirtualTables(std::shared_ptr<osquery::SQLiteDBInstance> const&) /vagrant/osquery/sql/virtual_table.cpp:613:7
    #10 0x7f971579fd2e in osquery::SQLiteDBManager::getConnection(bool) /vagrant/osquery/sql/sqlite_util.cpp:272:5
    #11 0x7f9715813a96 in osquery::SQLiteDBManager::get() /vagrant/osquery/sql/sqlite_util.h:139:12
    #12 0x7f9715813a96 in osquery::attachFunctionInternal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::function<void (sqlite3_context*, int, Mem**)>) /vagrant/osquery/sql/virtual_table.cpp:586
    #13 0x7f9714a20441 in osquery::launchIntoShell(int, char**) /vagrant/osquery/devtools/shell.cpp:1646:3
    #14 0x7f9714a4ca7e in main /vagrant/osquery/main/shell.cpp:117:15
    #15 0x7f97134a1f44 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21f44)

SUMMARY: AddressSanitizer: heap-use-after-free /usr/local/osquery/Cellar/gcc/5.3.0/lib64/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/hashtable.h:370:58 in std::_Hashtable<unsigned long, std::pair<unsigned long const, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > > >, std::allocator<std::pair<unsigned long const, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, osquery::Constraint> > > > >, std::__detail::_Select1st, std::equal_to<unsigned long>, std::hash<unsigned long>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashta
Shadow bytes around the buggy address:
  0x0c227fff98a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fff98b0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c227fff98c0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fff98d0: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
  0x0c227fff98e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c227fff98f0:[fd]fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa
  0x0c227fff9900: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c227fff9910: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fff9920: fd fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff9930: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fff9940: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1605==ABORTING

Determining That it Was Actually a SQLite Bug

I reported the bug on 2016-11-13, at the time thinking it was a bug with osquery. I looked into the bug, but couldn’t determine what exactly osquery was doing wrong that led to the bug. Then on 2016-11-15, I saw a version control check-in for SQLite with hash c5dbc599b910c02a961675b12b273b8df6d29450 that fixes a use-after-free according to the comment. It occured to me that I may be chasing after a bug in osquery, when the bug may actually be in SQLite. So I tried building osquery with the latest check-in of SQLite (that includes that use-after-free fix). I was no longer able to reproduce the bug.

It turns out that the bug was found by Google’s OSS-Fuzz the previous day (based on their bug report date). So it looks like I found the bug first, but just didn’t recognize it as a SQLite bug.

December 2, 2016
900 words


Categories

Tags
afl osquery sqlite linux

Connect. Socialize.