Skip to content

Commit 4aa3db3

Browse files
Teddy Reedobelisk
authored andcommitted
[Fix #3859] Lock every access to SQLiteDBInstance::db (#3883)
1 parent f256c3a commit 4aa3db3

8 files changed

Lines changed: 153 additions & 130 deletions

File tree

include/osquery/core.h

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@
1515
#include <string>
1616
#include <vector>
1717

18-
#if defined(__APPLE__) || defined(__FreeBSD__)
18+
#include <boost/thread/recursive_mutex.hpp>
1919
#include <boost/thread/shared_mutex.hpp>
20-
#else
21-
#include <shared_mutex>
22-
#endif
2320

2421
#include <osquery/status.h>
2522

@@ -193,26 +190,20 @@ inline bool isPlatform(PlatformType a, const PlatformType& t = kPlatformType) {
193190
return (static_cast<int>(t) & static_cast<int>(a)) != 0;
194191
}
195192

196-
#if defined(__APPLE__) || defined(__FreeBSD__)
197-
#define MUTEX_IMPL boost
198-
#else
199-
#define MUTEX_IMPL std
200-
#endif
201-
202193
/// Helper alias for defining mutexes.
203-
using Mutex = MUTEX_IMPL::shared_timed_mutex;
194+
using Mutex = boost::shared_timed_mutex;
204195

205196
/// Helper alias for write locking a mutex.
206-
using WriteLock = MUTEX_IMPL::unique_lock<Mutex>;
197+
using WriteLock = boost::unique_lock<Mutex>;
207198

208199
/// Helper alias for read locking a mutex.
209-
using ReadLock = MUTEX_IMPL::shared_lock<Mutex>;
200+
using ReadLock = boost::shared_lock<Mutex>;
210201

211202
/// Helper alias for defining recursive mutexes.
212-
using RecursiveMutex = std::recursive_mutex;
203+
using RecursiveMutex = boost::recursive_mutex;
213204

214205
/// Helper alias for write locking a recursive mutex.
215-
using RecursiveLock = std::lock_guard<std::recursive_mutex>;
206+
using RecursiveLock = boost::unique_lock<boost::recursive_mutex>;
216207

217208
/**
218209
* @brief An abstract similar to boost's noncopyable that defines moves.

osquery/main/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ int profile(int argc, char* argv[]) {
7979
auto dbc = osquery::SQLiteDBManager::get();
8080
for (size_t i = 0; i < static_cast<size_t>(osquery::FLAGS_profile); ++i) {
8181
osquery::QueryData results;
82-
auto status = osquery::queryInternal(query, results, dbc->db());
82+
auto status = osquery::queryInternal(query, results, dbc);
8383
dbc->clearAffectedTables();
8484
if (!status) {
8585
fprintf(stderr,

osquery/sql/benchmarks/sql_benchmarks.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ static void SQL_virtual_table_internal(benchmark::State& state) {
8484

8585
while (state.KeepRunning()) {
8686
QueryData results;
87-
queryInternal("select * from benchmark", results, dbc->db());
87+
queryInternal("select * from benchmark", results, dbc);
8888
dbc->clearAffectedTables();
8989
}
9090
}
@@ -104,7 +104,7 @@ static void SQL_virtual_table_internal_yield(benchmark::State& state) {
104104

105105
while (state.KeepRunning()) {
106106
QueryData results;
107-
queryInternal("select * from benchmark_yield", results, dbc->db());
107+
queryInternal("select * from benchmark_yield", results, dbc);
108108
dbc->clearAffectedTables();
109109
}
110110
}
@@ -124,7 +124,7 @@ static void SQL_virtual_table_internal_global(benchmark::State& state) {
124124
attachTableInternal("benchmark", columnDefinition(res), dbc);
125125

126126
QueryData results;
127-
queryInternal("select * from benchmark", results, dbc->db());
127+
queryInternal("select * from benchmark", results, dbc);
128128
dbc->clearAffectedTables();
129129
}
130130
}
@@ -144,7 +144,7 @@ static void SQL_virtual_table_internal_unique(benchmark::State& state) {
144144
attachTableInternal("benchmark", columnDefinition(res), dbc);
145145

146146
QueryData results;
147-
queryInternal("select * from benchmark", results, dbc->db());
147+
queryInternal("select * from benchmark", results, dbc);
148148
dbc->clearAffectedTables();
149149
}
150150
}
@@ -182,7 +182,7 @@ static void SQL_virtual_table_internal_long(benchmark::State& state) {
182182

183183
while (state.KeepRunning()) {
184184
QueryData results;
185-
queryInternal("select * from long_benchmark", results, dbc->db());
185+
queryInternal("select * from long_benchmark", results, dbc);
186186
dbc->clearAffectedTables();
187187
}
188188
}
@@ -246,7 +246,7 @@ static void SQL_virtual_table_internal_wide(benchmark::State& state) {
246246
kWideCount = state.range_y();
247247
while (state.KeepRunning()) {
248248
QueryData results;
249-
queryInternal("select * from wide_benchmark", results, dbc->db());
249+
queryInternal("select * from wide_benchmark", results, dbc);
250250
dbc->clearAffectedTables();
251251
}
252252
}
@@ -272,7 +272,7 @@ static void SQL_virtual_table_internal_wide_yield(benchmark::State& state) {
272272
kWideCount = state.range_y();
273273
while (state.KeepRunning()) {
274274
QueryData results;
275-
queryInternal("select * from wide_benchmark_yield", results, dbc->db());
275+
queryInternal("select * from wide_benchmark_yield", results, dbc);
276276
dbc->clearAffectedTables();
277277
}
278278
}
@@ -287,8 +287,7 @@ static void SQL_select_metadata(benchmark::State& state) {
287287
auto dbc = SQLiteDBManager::getUnique();
288288
while (state.KeepRunning()) {
289289
QueryData results;
290-
queryInternal(
291-
"select count(*) from sqlite_temp_master;", results, dbc->db());
290+
queryInternal("select count(*) from sqlite_temp_master;", results, dbc);
292291
dbc->clearAffectedTables();
293292
}
294293
}

osquery/sql/sqlite_util.cpp

Lines changed: 66 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ const std::map<std::string, QueryPlanner::Opcode> kSQLOpcodes = {
106106
OpComparator("IfNotZero"),
107107
};
108108

109+
RecursiveMutex SQLiteDBInstance::kPrimaryAttachMutex;
110+
109111
/// The SQLiteSQLPlugin implements the "sql" registry for internal/core.
110112
class SQLiteSQLPlugin : public SQLPlugin {
111113
public:
@@ -147,29 +149,29 @@ Status SQLiteSQLPlugin::query(const std::string& query,
147149
bool use_cache) const {
148150
auto dbc = SQLiteDBManager::get();
149151
dbc->useCache(use_cache);
150-
auto result = queryInternal(query, results, dbc->db());
152+
auto result = queryInternal(query, results, dbc);
151153
dbc->clearAffectedTables();
152154
return result;
153155
}
154156

155157
Status SQLiteSQLPlugin::getQueryColumns(const std::string& query,
156158
TableColumns& columns) const {
157159
auto dbc = SQLiteDBManager::get();
158-
return getQueryColumnsInternal(query, columns, dbc->db());
160+
return getQueryColumnsInternal(query, columns, dbc);
159161
}
160162

161163
Status SQLiteSQLPlugin::getQueryTables(const std::string& query,
162164
std::vector<std::string>& tables) const {
163165
auto dbc = SQLiteDBManager::get();
164-
QueryPlanner planner(query, dbc->db());
166+
QueryPlanner planner(query, dbc);
165167
tables = planner.tables();
166168
return Status(0);
167169
}
168170

169171
SQLInternal::SQLInternal(const std::string& query, bool use_cache) {
170172
auto dbc = SQLiteDBManager::get();
171173
dbc->useCache(use_cache);
172-
status_ = queryInternal(query, results_, dbc->db());
174+
status_ = queryInternal(query, results_, dbc);
173175

174176
// One of the advantages of using SQLInternal (aside from the Registry-bypass)
175177
// is the ability to "deep-inspect" the table attributes and actions.
@@ -207,7 +209,7 @@ void SQLiteSQLPlugin::detach(const std::string& name) {
207209
}
208210

209211
SQLiteDBInstance::SQLiteDBInstance(sqlite3*& db, Mutex& mtx)
210-
: db_(db), lock_(mtx, MUTEX_IMPL::try_to_lock) {
212+
: db_(db), lock_(mtx, boost::try_to_lock) {
211213
if (lock_.owns_lock()) {
212214
primary_ = true;
213215
} else {
@@ -251,8 +253,11 @@ bool SQLiteDBInstance::useCache() const {
251253
return use_cache_;
252254
}
253255

254-
WriteLock SQLiteDBInstance::attachLock() const {
255-
return WriteLock(attach_mutex_);
256+
RecursiveLock SQLiteDBInstance::attachLock() const {
257+
if (isPrimary()) {
258+
return RecursiveLock(kPrimaryAttachMutex);
259+
}
260+
return RecursiveLock(attach_mutex_);
256261
}
257262

258263
void SQLiteDBInstance::addAffectedTable(VirtualTableContent* table) {
@@ -371,10 +376,11 @@ SQLiteDBManager::~SQLiteDBManager() {
371376
}
372377
}
373378

374-
QueryPlanner::QueryPlanner(const std::string& query, sqlite3* db) {
379+
QueryPlanner::QueryPlanner(const std::string& query,
380+
const SQLiteDBInstanceRef& instance) {
375381
QueryData plan;
376-
queryInternal("EXPLAIN QUERY PLAN " + query, plan, db);
377-
queryInternal("EXPLAIN " + query, program_, db);
382+
queryInternal("EXPLAIN QUERY PLAN " + query, plan, instance);
383+
queryInternal("EXPLAIN " + query, program_, instance);
378384

379385
for (const auto& row : plan) {
380386
auto details = osquery::split(row.at("detail"));
@@ -442,10 +448,13 @@ int queryDataCallback(void* argument, int argc, char* argv[], char* column[]) {
442448
return 0;
443449
}
444450

445-
Status queryInternal(const std::string& q, QueryData& results, sqlite3* db) {
451+
Status queryInternal(const std::string& q,
452+
QueryData& results,
453+
const SQLiteDBInstanceRef& instance) {
446454
char* err = nullptr;
447-
sqlite3_exec(db, q.c_str(), queryDataCallback, &results, &err);
448-
sqlite3_db_release_memory(db);
455+
auto lock = instance->attachLock();
456+
sqlite3_exec(instance->db(), q.c_str(), queryDataCallback, &results, &err);
457+
sqlite3_db_release_memory(instance->db());
449458
if (err != nullptr) {
450459
auto error_string = std::string(err);
451460
sqlite3_free(err);
@@ -456,55 +465,62 @@ Status queryInternal(const std::string& q, QueryData& results, sqlite3* db) {
456465

457466
Status getQueryColumnsInternal(const std::string& q,
458467
TableColumns& columns,
459-
sqlite3* db) {
460-
// Turn the query into a prepared statement
461-
sqlite3_stmt* stmt{nullptr};
462-
auto rc = sqlite3_prepare_v2(
463-
db, q.c_str(), static_cast<int>(q.length() + 1), &stmt, nullptr);
464-
if (rc != SQLITE_OK || stmt == nullptr) {
465-
if (stmt != nullptr) {
466-
sqlite3_finalize(stmt);
468+
const SQLiteDBInstanceRef& instance) {
469+
Status status = Status();
470+
TableColumns results;
471+
{
472+
auto lock = instance->attachLock();
473+
474+
// Turn the query into a prepared statement
475+
sqlite3_stmt* stmt{nullptr};
476+
auto rc = sqlite3_prepare_v2(instance->db(),
477+
q.c_str(),
478+
static_cast<int>(q.length() + 1),
479+
&stmt,
480+
nullptr);
481+
if (rc != SQLITE_OK || stmt == nullptr) {
482+
if (stmt != nullptr) {
483+
sqlite3_finalize(stmt);
484+
}
485+
return Status(1, sqlite3_errmsg(instance->db()));
467486
}
468-
return Status(1, sqlite3_errmsg(db));
469-
}
470487

471-
// Get column count
472-
auto num_columns = sqlite3_column_count(stmt);
473-
TableColumns results;
474-
results.reserve(num_columns);
488+
// Get column count
489+
auto num_columns = sqlite3_column_count(stmt);
490+
results.reserve(num_columns);
475491

476-
// Get column names and types
477-
Status status = Status();
478-
bool unknown_type = false;
479-
for (int i = 0; i < num_columns; ++i) {
480-
auto col_name = sqlite3_column_name(stmt, i);
481-
auto col_type = sqlite3_column_decltype(stmt, i);
482-
483-
if (col_name == nullptr) {
484-
status = Status(1, "Could not get column type");
485-
break;
486-
}
492+
// Get column names and types
493+
bool unknown_type = false;
494+
for (int i = 0; i < num_columns; ++i) {
495+
auto col_name = sqlite3_column_name(stmt, i);
496+
auto col_type = sqlite3_column_decltype(stmt, i);
497+
498+
if (col_name == nullptr) {
499+
status = Status(1, "Could not get column type");
500+
break;
501+
}
487502

488-
if (col_type == nullptr) {
489-
// Types are only returned for table columns (not expressions).
490-
col_type = "UNKNOWN";
491-
unknown_type = true;
503+
if (col_type == nullptr) {
504+
// Types are only returned for table columns (not expressions).
505+
col_type = "UNKNOWN";
506+
unknown_type = true;
507+
}
508+
results.push_back(std::make_tuple(
509+
col_name, columnTypeName(col_type), ColumnOptions::DEFAULT));
492510
}
493-
results.push_back(std::make_tuple(
494-
col_name, columnTypeName(col_type), ColumnOptions::DEFAULT));
495-
}
496511

497-
// An unknown type means we have to parse the plan and SQLite opcodes.
498-
if (unknown_type) {
499-
QueryPlanner planner(q, db);
500-
planner.applyTypes(results);
512+
// An unknown type means we have to parse the plan and SQLite opcodes.
513+
if (unknown_type) {
514+
QueryPlanner planner(q, instance);
515+
planner.applyTypes(results);
516+
}
517+
sqlite3_finalize(stmt);
501518
}
502519

503520
if (status.ok()) {
504521
columns = std::move(results);
505522
}
506523

507-
sqlite3_finalize(stmt);
508524
return status;
509525
}
510526
}

osquery/sql/sqlite_util.h

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class SQLiteDBInstance : private boost::noncopyable {
7878
bool useCache() const;
7979

8080
/// Lock the database for attaching virtual tables.
81-
WriteLock attachLock() const;
81+
RecursiveLock attachLock() const;
8282

8383
private:
8484
/// Handle the primary/forwarding requests for table attribute accesses.
@@ -102,11 +102,25 @@ class SQLiteDBInstance : private boost::noncopyable {
102102
/// Either the managed primary database or an ephemeral instance.
103103
sqlite3* db_{nullptr};
104104

105-
/// An attempted unique lock on the manager's primary database access mutex.
105+
/**
106+
* @brief An attempted unique lock on the manager's primary database mutex.
107+
*
108+
* This lock is not always acquired. If it is then this instance has locked
109+
* access to the 'primary' SQLite database.
110+
*/
106111
WriteLock lock_;
107112

108-
/// Attaching can occur async from the registry APIs.
109-
mutable Mutex attach_mutex_;
113+
/**
114+
* @brief A mutex protecting access to this instance's SQLite database.
115+
*
116+
* Attaching, and other access, can occur async from the registry APIs.
117+
*
118+
* If a database is primary then the static attach mutex is used.
119+
*/
120+
mutable RecursiveMutex attach_mutex_;
121+
122+
/// See attach_mutex_ but used for the primary database.
123+
static RecursiveMutex kPrimaryAttachMutex;
110124

111125
/// Vector of tables that need their constraints cleared after execution.
112126
std::map<std::string, VirtualTableContent*> affected_tables_;
@@ -227,8 +241,8 @@ class SQLiteDBManager : private boost::noncopyable {
227241
class QueryPlanner : private boost::noncopyable {
228242
public:
229243
explicit QueryPlanner(const std::string& query)
230-
: QueryPlanner(query, SQLiteDBManager::get()->db()) {}
231-
QueryPlanner(const std::string& query, sqlite3* db);
244+
: QueryPlanner(query, SQLiteDBManager::get()) {}
245+
QueryPlanner(const std::string& query, const SQLiteDBInstanceRef& instance);
232246
~QueryPlanner() {}
233247

234248
public:
@@ -298,7 +312,9 @@ extern const std::map<std::string, QueryPlanner::Opcode> kSQLOpcodes;
298312
*
299313
* @return A status indicating SQL query results.
300314
*/
301-
Status queryInternal(const std::string& q, QueryData& results, sqlite3* db);
315+
Status queryInternal(const std::string& q,
316+
QueryData& results,
317+
const SQLiteDBInstanceRef& instance);
302318

303319
/**
304320
* @brief SQLite Intern: Analyze a query, providing information about the
@@ -317,7 +333,7 @@ Status queryInternal(const std::string& q, QueryData& results, sqlite3* db);
317333
*/
318334
Status getQueryColumnsInternal(const std::string& q,
319335
TableColumns& columns,
320-
sqlite3* db);
336+
const SQLiteDBInstanceRef& instance);
321337

322338
/**
323339
* @brief SQLInternal: SQL, but backed by internal calls.

0 commit comments

Comments
 (0)