While fuzzing osquery using afl, I encountered a bug in SQLite.
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
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.