Skip to content
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

Fix out-of-bounds read in statement tail parser #996

Merged
merged 3 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
51 changes: 27 additions & 24 deletions src/better_sqlite3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,8 +859,11 @@ void Statement::JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info)
if (handle == NULL) {
return ThrowRangeError("The supplied SQL string contains no statements");
}
for (char c; (c = *tail); ++tail) {
if (IS_SKIPPED(c)) continue;
for (char c; (c = *tail); ) {
if (IS_SKIPPED(c)) {
++tail;
continue;
}
if (c == '/' && tail[1] == '*') {
tail += 2;
for (char c; (c = *tail); ++tail) {
Expand Down Expand Up @@ -891,9 +894,9 @@ void Statement::JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info)

info.GetReturnValue().Set(info.This());
}
#line 149 "./src/objects/statement.lzz"
#line 152 "./src/objects/statement.lzz"
void Statement::JS_run (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 149 "./src/objects/statement.lzz"
#line 152 "./src/objects/statement.lzz"
{
Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; ( ( void ) 0 ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; if ( ! db -> GetState ( ) -> unsafe_mode ) { if ( db -> GetState ( ) -> iterators ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; } ( ( void ) 0 ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ;
sqlite3* db_handle = db->GetHandle();
Expand All @@ -916,9 +919,9 @@ void Statement::JS_run (v8::FunctionCallbackInfo <v8 :: Value> const & info)
}
db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ;
}
#line 172 "./src/objects/statement.lzz"
#line 175 "./src/objects/statement.lzz"
void Statement::JS_get (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 172 "./src/objects/statement.lzz"
#line 175 "./src/objects/statement.lzz"
{
Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ;
int status = sqlite3_step(handle);
Expand All @@ -933,9 +936,9 @@ void Statement::JS_get (v8::FunctionCallbackInfo <v8 :: Value> const & info)
sqlite3_reset(handle);
db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ;
}
#line 187 "./src/objects/statement.lzz"
#line 190 "./src/objects/statement.lzz"
void Statement::JS_all (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 187 "./src/objects/statement.lzz"
#line 190 "./src/objects/statement.lzz"
{
Statement * stmt = node :: ObjectWrap :: Unwrap < Statement > ( info . This ( ) ) ; if ( ! stmt -> returns_data ) return ThrowTypeError ( "This statement does not return data. Use run() instead" ) ; sqlite3_stmt * handle = stmt -> handle ; Database * db = stmt -> db ; if ( ! db -> GetState ( ) -> open ) return ThrowTypeError ( "The database connection is not open" ) ; if ( db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ; if ( stmt -> locked ) return ThrowTypeError ( "This statement is busy executing a query" ) ; const bool bound = stmt -> bound ; if ( ! bound ) { Binder binder ( handle ) ; if ( ! binder . Bind ( info , info . Length ( ) , stmt ) ) { sqlite3_clear_bindings ( handle ) ; return ; } ( ( void ) 0 ) ; } else if ( info . Length ( ) > 0 ) { return ThrowTypeError ( "This statement already has bound parameters" ) ; } ( ( void ) 0 ) ; db -> GetState ( ) -> busy = true ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; if ( db -> Log ( isolate , handle ) ) { db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ; } ( ( void ) 0 ) ;
v8 :: Local < v8 :: Context > ctx = isolate -> GetCurrentContext ( ) ;
Expand All @@ -956,9 +959,9 @@ void Statement::JS_all (v8::FunctionCallbackInfo <v8 :: Value> const & info)
if (js_error) db->GetState()->was_js_error = true;
db -> GetState ( ) -> busy = false ; db -> ThrowDatabaseError ( ) ; if ( ! bound ) { sqlite3_clear_bindings ( handle ) ; } return ;
}
#line 208 "./src/objects/statement.lzz"
#line 211 "./src/objects/statement.lzz"
void Statement::JS_iterate (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 208 "./src/objects/statement.lzz"
#line 211 "./src/objects/statement.lzz"
{
Addon * addon = static_cast < Addon * > ( info . Data ( ) . As < v8 :: External > ( ) -> Value ( ) ) ;
v8 :: Isolate * isolate = info . GetIsolate ( ) ;
Expand All @@ -968,9 +971,9 @@ void Statement::JS_iterate (v8::FunctionCallbackInfo <v8 :: Value> const & info)
addon->privileged_info = NULL;
if (!maybeIterator.IsEmpty()) info.GetReturnValue().Set(maybeIterator.ToLocalChecked());
}
#line 218 "./src/objects/statement.lzz"
#line 221 "./src/objects/statement.lzz"
void Statement::JS_bind (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 218 "./src/objects/statement.lzz"
#line 221 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
if (stmt->bound) return ThrowTypeError("The bind() method can only be invoked once per statement object");
Expand All @@ -981,9 +984,9 @@ void Statement::JS_bind (v8::FunctionCallbackInfo <v8 :: Value> const & info)
stmt->bound = true;
info.GetReturnValue().Set(info.This());
}
#line 229 "./src/objects/statement.lzz"
#line 232 "./src/objects/statement.lzz"
void Statement::JS_pluck (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 229 "./src/objects/statement.lzz"
#line 232 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
if (!stmt->returns_data) return ThrowTypeError("The pluck() method is only for statements that return data");
Expand All @@ -994,9 +997,9 @@ void Statement::JS_pluck (v8::FunctionCallbackInfo <v8 :: Value> const & info)
stmt->mode = use ? Data::PLUCK : stmt->mode == Data::PLUCK ? Data::FLAT : stmt->mode;
info.GetReturnValue().Set(info.This());
}
#line 240 "./src/objects/statement.lzz"
#line 243 "./src/objects/statement.lzz"
void Statement::JS_expand (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 240 "./src/objects/statement.lzz"
#line 243 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
if (!stmt->returns_data) return ThrowTypeError("The expand() method is only for statements that return data");
Expand All @@ -1007,9 +1010,9 @@ void Statement::JS_expand (v8::FunctionCallbackInfo <v8 :: Value> const & info)
stmt->mode = use ? Data::EXPAND : stmt->mode == Data::EXPAND ? Data::FLAT : stmt->mode;
info.GetReturnValue().Set(info.This());
}
#line 251 "./src/objects/statement.lzz"
#line 254 "./src/objects/statement.lzz"
void Statement::JS_raw (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 251 "./src/objects/statement.lzz"
#line 254 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
if (!stmt->returns_data) return ThrowTypeError("The raw() method is only for statements that return data");
Expand All @@ -1020,9 +1023,9 @@ void Statement::JS_raw (v8::FunctionCallbackInfo <v8 :: Value> const & info)
stmt->mode = use ? Data::RAW : stmt->mode == Data::RAW ? Data::FLAT : stmt->mode;
info.GetReturnValue().Set(info.This());
}
#line 262 "./src/objects/statement.lzz"
#line 265 "./src/objects/statement.lzz"
void Statement::JS_safeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 262 "./src/objects/statement.lzz"
#line 265 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
if ( stmt -> db -> GetState ( ) -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ;
Expand All @@ -1031,9 +1034,9 @@ void Statement::JS_safeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const &
else { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a boolean" ) ; stmt -> safe_ints = ( info [ 0 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; }
info.GetReturnValue().Set(info.This());
}
#line 271 "./src/objects/statement.lzz"
#line 274 "./src/objects/statement.lzz"
void Statement::JS_columns (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 271 "./src/objects/statement.lzz"
#line 274 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
if (!stmt->returns_data) return ThrowTypeError("The columns() method is only for statements that return data");
Expand Down Expand Up @@ -1076,9 +1079,9 @@ void Statement::JS_columns (v8::FunctionCallbackInfo <v8 :: Value> const & info)

info.GetReturnValue().Set(columns);
}
#line 314 "./src/objects/statement.lzz"
#line 317 "./src/objects/statement.lzz"
void Statement::JS_busy (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info)
#line 314 "./src/objects/statement.lzz"
#line 317 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
info.GetReturnValue().Set(stmt->alive && stmt->locked);
Expand Down
42 changes: 21 additions & 21 deletions src/better_sqlite3.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,47 +340,47 @@ class Statement : public node::ObjectWrap
explicit Statement (Database * db, sqlite3_stmt * handle, sqlite3_uint64 id, bool returns_data);
#line 85 "./src/objects/statement.lzz"
static void JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 149 "./src/objects/statement.lzz"
#line 152 "./src/objects/statement.lzz"
static void JS_run (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 172 "./src/objects/statement.lzz"
#line 175 "./src/objects/statement.lzz"
static void JS_get (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 187 "./src/objects/statement.lzz"
#line 190 "./src/objects/statement.lzz"
static void JS_all (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 208 "./src/objects/statement.lzz"
#line 211 "./src/objects/statement.lzz"
static void JS_iterate (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 218 "./src/objects/statement.lzz"
#line 221 "./src/objects/statement.lzz"
static void JS_bind (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 229 "./src/objects/statement.lzz"
#line 232 "./src/objects/statement.lzz"
static void JS_pluck (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 240 "./src/objects/statement.lzz"
#line 243 "./src/objects/statement.lzz"
static void JS_expand (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 251 "./src/objects/statement.lzz"
#line 254 "./src/objects/statement.lzz"
static void JS_raw (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 262 "./src/objects/statement.lzz"
#line 265 "./src/objects/statement.lzz"
static void JS_safeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 271 "./src/objects/statement.lzz"
#line 274 "./src/objects/statement.lzz"
static void JS_columns (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 314 "./src/objects/statement.lzz"
#line 317 "./src/objects/statement.lzz"
static void JS_busy (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info);
#line 319 "./src/objects/statement.lzz"
#line 322 "./src/objects/statement.lzz"
Database * const db;
#line 320 "./src/objects/statement.lzz"
#line 323 "./src/objects/statement.lzz"
sqlite3_stmt * const handle;
#line 321 "./src/objects/statement.lzz"
#line 324 "./src/objects/statement.lzz"
Extras * const extras;
#line 322 "./src/objects/statement.lzz"
#line 325 "./src/objects/statement.lzz"
bool alive;
#line 323 "./src/objects/statement.lzz"
#line 326 "./src/objects/statement.lzz"
bool locked;
#line 324 "./src/objects/statement.lzz"
#line 327 "./src/objects/statement.lzz"
bool bound;
#line 325 "./src/objects/statement.lzz"
#line 328 "./src/objects/statement.lzz"
bool has_bind_map;
#line 326 "./src/objects/statement.lzz"
#line 329 "./src/objects/statement.lzz"
bool safe_ints;
#line 327 "./src/objects/statement.lzz"
#line 330 "./src/objects/statement.lzz"
char mode;
#line 328 "./src/objects/statement.lzz"
#line 331 "./src/objects/statement.lzz"
bool const returns_data;
};
#line 1 "./src/objects/statement-iterator.lzz"
Expand Down
7 changes: 5 additions & 2 deletions src/objects/statement.lzz
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ private:
if (handle == NULL) {
return ThrowRangeError("The supplied SQL string contains no statements");
}
for (char c; (c = *tail); ++tail) {
if (IS_SKIPPED(c)) continue;
for (char c; (c = *tail); ) {
if (IS_SKIPPED(c)) {
++tail;
continue;
}
if (c == '/' && tail[1] == '*') {
tail += 2;
for (char c; (c = *tail); ++tail) {
Expand Down