Prevent SQL injection C++ - c++

bool run_query(sqlite3* db, const std::string& sql, std::vector< user_record >& records)
{
// clear any prior results
records.clear();
char* error_message;
if(sqlite3_exec(db, sql.c_str(), callback, &records, &error_message) != SQLITE_OK)
{
std::cout << "Data failed to be queried from USERS table. ERROR = " << error_message << std::endl;
sqlite3_free(error_message);
return false;
}
return true;
}
How do I fix this method to fail and display an error if there is a suspected SQL Injection?

Related

SQLite3 - Taking function parameters as values

I'm trying to make a class using sqlite3 database with a method that should insert the data in the table when the user adds the files to the application, but it is not working as intended,
void Database::InsertSample(int Favorite, std::string Filename,
std::string SamplePack, int Channels, int Length,
int SampleRate, int Bitrate, std::string Comment,
std::string Path)
{
try
{
rc = sqlite3_open("Samples.db", &DB);
sql = "INSERT INTO SAMPLES (FAVORITE, FILENAME, SAMPLEPACK, CHANNELS, \
LENGTH, SAMPLERATE, BITRATE, BITSPERSAMPLE, PATH) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);";
rc = sqlite3_prepare_v2(DB, sql.c_str(), 0, &stmt, 0); // create the prepared statement
rc = sqlite3_bind_int(stmt, 1, Favorite);
rc = sqlite3_bind_text(stmt, 2, Filename.c_str(), Filename.size(), SQLITE_STATIC);
rc = sqlite3_bind_text(stmt, 3, SamplePack.c_str(), SamplePack.size(), SQLITE_STATIC);
rc = sqlite3_bind_int(stmt, 4, Channels);
rc = sqlite3_bind_text(stmt, 5, Filename.c_str(), Filename.size(), SQLITE_STATIC);
rc = sqlite3_bind_int(stmt, 6, Length);
rc = sqlite3_bind_int(stmt, 7, SampleRate);
rc = sqlite3_bind_int(stmt, 8, Bitrate);
rc = sqlite3_bind_text(stmt, 9, Comment.c_str(), Comment.size(), SQLITE_STATIC);
rc = sqlite3_bind_text(stmt, 10, Path.c_str(), Path.size(), SQLITE_STATIC);
rc = sqlite3_step(stmt);
rc = sqlite3_exec(DB, Sample.c_str(), NULL, 0, &ErrorMessage);
if (rc != SQLITE_OK)
{
std::cerr << "Error! Cannot insert data into table." << std::endl;
sqlite3_free(ErrorMessage);
}
else
{
std::cout << "Data inserted successfully." << std::endl;
}
sqlite3_close(DB);
}
catch (const std::exception &exception)
{
std::cerr << exception.what();
}
}
But this fails, throwing the error statement "Error! Cannot insert data into table.". Am I doing something wrong here.
I'm using this function in another class as,
void Browser::OnClickDirCtrl(wxCommandEvent& event)
{
TagLib::FileRef File (DirCtrl->GetFilePath());
TagLib::String Artist = File.tag()->artist();
TagLib::String Album = File.tag()->album();
TagLib::String Genre = File.tag()->genre();
TagLib::String Title = File.tag()->title();
TagLib::String Comment = File.tag()->comment();
int Bitrate = File.audioProperties()->bitrate();
int Channels = File.audioProperties()->channels();
int Length = File.audioProperties()->lengthInMilliseconds();
int LengthSec = File.audioProperties()->lengthInSeconds();
int SampleRate = File.audioProperties()->sampleRate();
wxVector<wxVariant> Data;
Data.clear();
Data.push_back(false);
Data.push_back(TagLibTowx(Title));
Data.push_back(TagLibTowx(Artist));
Data.push_back(wxString::Format("%d",Channels));
Data.push_back(wxString::Format("%d",LengthSec));
Data.push_back(wxString::Format("%d",SampleRate));
Data.push_back(wxString::Format("%d",Bitrate));
Data.push_back(TagLibTowx(Comment));
SampleListView->AppendItem(Data);
db.InsertSample(0, Title.to8Bit(), Artist.to8Bit(), Channels, Length, SampleRate, Bitrate, Comment.to8Bit(), DirCtrl->GetFilePath().ToStdString());
}
This just a part of the function that should add the files to the database. As you can see, I am storing the path of the files in the database which is important data that I need for the project.
/---------/
EDIT: Adding a short sample,
main.cpp
#include "testdb.hpp"
int main()
{
Database db;
db.InsertData("Hello, World!");
return 0;
}
testdb.hpp
#include <sqlite3.h>
#include <string>
class Database
{
public:
Database();
~Database();
public:
sqlite3* DB;
int rc;
char* ErrorMessage;
std::string Test;
std::string sql;
sqlite3_stmt* stmt;
public:
void InsertData(std::string Path);
};
testdb.cpp
#include <exception>
#include <iostream>
#include <string>
#include "testdb.hpp"
Database::Database()
{
/* Create SQL statement */
Test = "CREATE TABLE TEST("
"TEST TEXT NOT NULL);";
try
{
rc = sqlite3_open("Test.db", &DB);
rc = sqlite3_exec(DB, Test.c_str(), NULL, 0, &ErrorMessage);
if (rc != SQLITE_OK)
{
std::cerr << "Error! Cannot create table." << std::endl;
sqlite3_free(ErrorMessage);
}
else
{
std::cout << "Table created successfuly." << std::endl;
}
sqlite3_close(DB);
}
catch (const std::exception &exception)
{
std::cerr << exception.what();
}
}
void Database::InsertData(std::string Test)
{
try
{
rc = sqlite3_open("Test.db", &DB);
sql = "INSERT INTO TEST (PATH) VALUES (?);";
rc = sqlite3_prepare_v2(DB, sql.c_str(), 10, &stmt, 0); // create the prepared statement
// error handling goes here
rc = sqlite3_bind_text(stmt, 10, Test.c_str(), Test.size(), SQLITE_STATIC);
// error handling goes here
rc = sqlite3_step(stmt);
// error handling goes here
rc = sqlite3_finalize(stmt);
if (rc != SQLITE_OK)
{
std::cerr << "Error! Cannot insert data into table." << std::endl;
sqlite3_free(ErrorMessage);
}
else if (rc == SQLITE_BUSY)
{
std::cout << "BUSY" << std::endl;
}
else if (rc == SQLITE_DONE)
{
std::cout << "DONE" << std::endl;
}
else if (rc == SQLITE_ERROR)
{
std::cout << "ERROR" << std::endl;
}
else if (rc == SQLITE_MISUSE)
{
std::cout << "MISUSE" << std::endl;
}
else
{
std::cout << "Data inserted successfully." << ErrorMessage << std::endl;
}
sqlite3_close(DB);
}
catch (const std::exception &exception)
{
std::cerr << exception.what();
}
}
Database::~Database(){}
Compile using g++ main.cpp testdb.cpp -l sqlite3 -o db.
Same thing happening here, it says data inserted but database shows empty in sqlitebrowser.
You have a sqlite3_step followed by sqlite3_exec, which is probably not what you intended. It's certainly not good. You must call sqlite3_reset or sqlite3_finalize to complete the prepared statement. It will also provide a specific error code that better describes the error if you get one from sqlite3_step.
See this explanation of `sqlite3_step'

free():invalid size, but no new() or free() was called - SQLite3 using C++

Hi I am playing around with sqlite3 using C++. I am storing an array as BLOB and try to extract it from the database. However, when I select data from the database I got free():invalid size error after the function selectTable() finishes and before main() finishes. So I am a bit confused.
The output on the command line is
selectTable func
Opened database successfully
before finalzie
after close DB
free(): invalid size
Aborted
Below is backtrace from gdb and the code
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007ffff717a801 in __GI_abort () at abort.c:79
#2 0x00007ffff71c3897 in __libc_message (action=action#entry=do_abort,
fmt=fmt#entry=0x7ffff72f0b9a "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3 0x00007ffff71ca90a in malloc_printerr (
str=str#entry=0x7ffff72eeda0 "free(): invalid size") at malloc.c:5350
#4 0x00007ffff71d1e2c in _int_free (have_lock=0,
p=0x7ffff7de5990 <_dl_init+864>, av=0x7ffff7525c40 <main_arena>)
at malloc.c:4161
#5 __GI___libc_free (mem=0x7ffff7de59a0 <_dl_fini>) at malloc.c:3124
#6 0x0000555555555b48 in main ()
int main() {
const char* dir = "testBLOB.db";
sqlite3* DB;
//createDB(dir);
//createTable(dir);
//clearTable(dir);
for(int i = 0; i < 50; i++) {
//insertTable(dir);
}
cout << "selectTable func" << endl;
selectTable(dir);
cout << "Out of selectTable func" << endl;
return 0;
}
static int createDB(const char* s) {
sqlite3* DB;
int exit = 0;
exit = sqlite3_open(s, &DB);
if(!exit) {
//cout << "opened db" << endl;
}
sqlite3_close(DB);
return 0;
}
static int createTable(const char* s) {
sqlite3* DB;
string sql = "CREATE TABLE IF NOT EXISTS test("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT NOT NULL, "
"vector BLOB"
");";
try {
int exit = 0;
exit = sqlite3_open(s, &DB);
char* messageError;
exit = sqlite3_exec(DB, sql.c_str(), NULL, 0, &messageError);
//cout << "Hello, World!" << endl;
if(exit != SQLITE_OK) {
cerr << "Error creating table" << endl;
} else {
sqlite3_close(DB);
}
} catch (const exception & e) {
cerr << e.what();
}
return 0;
};
static int insertTable(const char* s) {
sqlite3* DB;
long double nums [100] = {63.3448897849,56.5751700134,75.6611442415,44.4839808971,12.1012110357,39.8311006389,55.4628689524,44.1856083913,31.8941372232,36.7439371495,64.1919152947,46.6041307466,16.8129922477,26.4797506344,33.0942127905,87.7671523992,76.4743660304,26.895728291,71.6418628151,39.4279836297,33.1511476055,21.9412133656,49.2985876344,54.7288568945,61.7644413811,39.5360580159,48.2049240243,15.0657890486,74.8453755862,3.9697162327,46.3309272981,3.6967671205,29.4109279366,35.2237159178,71.2944696538,83.2668276235,8.6042287278,29.018178646,53.9090162771,74.1875874704,13.362141479,85.534882156,46.1570578637,16.6535624939,60.4663891357,46.4002226626,43.5334234645,5.9192657242,6.4233909782,70.9674985846,16.7364288218,6.140383666,1.6295357671,41.5132888558,7.0105695897,93.7402324433,67.9067125381,79.2202639865,73.9691802822,83.7211616527,95.1748897322,77.6210081594,52.6691899363,45.9668407426,30.0850082804,99.8853106577,44.4116175004,66.7492926269,80.344211132,2.9125625332,13.1282558744,77.1323341686,31.6479987137,12.0439293147,52.34792652,10.0973593886,11.1281788415,42.3558223361,26.0832770714,78.8802218674,65.7943757958,39.4931075012,62.3669620437,40.5895497709,75.5671730948,32.0576337297,97.9215110968,9.9871548843,46.6131220896,11.0937537323,59.9746432863,25.2756546914,39.1604177152,21.5325516688,55.5211448299,29.0247732257,10.4586600007,53.0534949121,77.3997550972,48.2887426956};
int rc = sqlite3_open_v2(s, &DB, SQLITE_OPEN_READWRITE,NULL);
if(rc != SQLITE_OK) {
} else {
string sql = "INSERT INTO test (name, vector) VALUES ('test', ?)";
try {
sqlite3_stmt *stmt = NULL;
rc = sqlite3_prepare_v2(DB, sql.c_str(), -1, &stmt, NULL);
if(rc != SQLITE_OK) {
cerr << "Error preparing stmt" << endl;
} else {
rc = sqlite3_bind_blob(stmt,1, nums, 1600, SQLITE_STATIC);
if(rc != SQLITE_OK) {
cerr << "bind failed" << endl;
} else {
rc = sqlite3_step(stmt);
if(rc != SQLITE_DONE) {
cerr << "no" << endl;
}
}
}
sqlite3_finalize(stmt);
} catch (const exception & e) {
cerr << e.what();
}
}
sqlite3_close(DB);
return 0;
};
static string selectTable(const char* s) {
sqlite3 *db;
int rc;
/* Open database */
rc = sqlite3_open(s, &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
sqlite3_stmt *stmt;
/* Create SQL statement */
const char* sql = "SELECT * FROM test";
if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK)
{
return 0;
}
int result = 0;
while (true)
{
result = sqlite3_step(stmt);
if (result == SQLITE_ROW)
{
// Get the size of the vector
int size = sqlite3_column_bytes(stmt, 2);
// Get the pointer to data
long double * p = (long double *)sqlite3_column_blob(stmt,2);
// Initialize the vector with the data
vector<long double> data(p, p+size);
allVects.push_back(data);
}
else
{
//cout << "not SQLITE_ROW" << endl;
break;
}
}
cout<< "before finalzie" << endl;
sqlite3_finalize(stmt);
while(sqlite3_close(db)!= SQLITE_OK) {
cout << "not ok" << endl;
}
cout<< "after close db" << endl;
}
I figured it out by myself. It's because the return type of my function selectTable() is string, however inside function it does not return a string. It's weird that my compiler did not complaint about it tho...
Similar issue is observed when return type of a function is bool and nothing is returned from that function. Even when return type is bool, call stack shows "invalid free" performed on a string. This was quite misleading.
Behavior is undefined when return statement is missing in a function except main().
In my scenario -
Crash is observed when the code is built with g++ version 8.3.0 on debian10(buster slim).
Same code works fine when it is built with g++ version 6.3.0 on debian9(stretch slim).
Developer will be able to detect this during the compile time based on the compiler options enabled. for eg: -Wall
Please note that this is independent of SQLite usage.

SQLite errorcode: sqlite_step() returns 5 (SQLITE_BUSY)

I am working on a school project which involves SQLite3.
I've created a bunch of functions for my DatabaseManager class which all work quite good.
Now, after adding a function with a DELETE statement, sqlite3_step() returns SQLITE_BUSY and the sqlite3_errmsg() says "Database is locked".
Before opening this function I have already called sqlite3_finalize() and sqlite3_close().
int errorcode;
sqlite3 *db;
//sql statement
std::string statement = "DELETE FROM tbl_follower WHERE flw_ID = " + std::to_string(row);
sqlite3_stmt *binStatement;
errorcode = sqlite3_open(m_filePath.c_str(), &db);
if (errorcode != SQLITE_OK)
std::cerr << sqlite3_errmsg(db) << std::endl;
errorcode = sqlite3_prepare_v2(db, statement.c_str(), statement.size(), &binStatement, NULL);
if(errorcode != SQLITE_OK)
std::cerr << sqlite3_errmsg(db) << std::endl;
errorcode = sqlite3_step(binStatement);
if (errorcode == SQLITE_DONE)
{
sqlite3_finalize(binStatement);
sqlite3_close(db);
}
else
{
//error
std::cerr << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(binStatement);
sqlite3_close(db);
}
I am pretty sure the issue is somewhere in this code because I have already checked all of my other functions for mistakes (which all work without giving an error). My SQL statement is also correct.
"Database is locked" means that there is some other active transaction.
You probably forgot to finalize a statement in some other part of the program.

SQLite queries returning "unknown error"

I am developing a multithreaded cpp app that reads data from an sqlite database. I am running into a problem with multithreaded read attempts causing an "unknown error" to be thrown. I assume this has to be something like database locking.
My question is the following - Why does the database lock on read operations? I would understand if it was reading/writing, but this app only executes select statements.
Potential solutions to my problem: Throw some mutexes at the sections, switch from sqlite to some other database..
I realize sqlite is not an incredibly strong database, so am leaning more toward switching to some other database. Any suggestions?
Thanks!
The code:
//generate a db connection//
bool open(string filename){
if(sqlite3_open(filename.c_str(), &database) == SQLITE_OK)
{
return true;
}
return false;
}
//Query database//
vector<vector<string>> query(string query){
sqlite3_stmt *statement;
vector<vector<string>> results;
if(sqlite3_prepare_v2(database, query.c_str(), -1, &statement, 0) == SQLITE_OK)
{
int cols = sqlite3_column_count(statement);
int result = 0;
while(true)
{
result = sqlite3_step(statement);
if(result == SQLITE_ROW)
{
vector<string> values;
for(int col = 0; col < cols; col++)
{
auto temp_val = (char*)sqlite3_column_text(statement, col);
if(!temp_val){
cout << "FAIL" << endl;
}
else{
values.emplace_back(temp_val);
}
}
results.push_back(values);
}
else
{
break;
}
}
sqlite3_finalize(statement);
}
string error = sqlite3_errmsg(database);
if(error != "not an error") cout << query << " " << error << endl;
return results;
}
void close(){
sqlite3_close(database);
}
You say you are using a single connection from multiple threads. This is probably not the best idea, and depending on the options configured in your particular SQLite installation, it may not work at all. For more on this, see here: https://www.sqlite.org/threadsafe.html
As you can see, a full diagnosis of your problem requires looking at how your SQLite package was built...but you can try setting runtime options to see if that helps, as described on the above page.

SQLite and BOOST_SCOPE_EXIT_ALL

Why does the SQLite C interface requires to pass the pointer by the reference when using BOOST_SCOPE_EXIT_ALL, for example?
Suppose that I have the following code:
#include <boost/scope_exit.hpp>
#include <sqlite3.h>
#include <cstdlib>
#include <iostream>
int main()
{
sqlite3* db;
BOOST_SCOPE_EXIT_ALL(db)
{
if (sqlite3_close(db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << '\n';
}
};
if (sqlite3_open(":memory:", &db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << '\n';
return EXIT_FAILURE;
}
sqlite3_stmt* prepared_stmt = NULL;
BOOST_SCOPE_EXIT_ALL(db, prepared_stmt)
{
if (sqlite3_finalize(prepared_stmt) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << '\n';
}
};
if (sqlite3_prepare_v2(db, "CREATE TABLE IF NOT EXISTS foo(bar TEXT, baz TEXT)", -1, &prepared_stmt, 0) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << '\n';
return EXIT_FAILURE;
}
if (sqlite3_step(prepared_stmt) != SQLITE_DONE)
{
std::cerr << sqlite3_errmsg(db) << '\n';
return EXIT_FAILURE;
}
}
It crashes when trying to call sqlite3_close function and doesn't print anything to the stderr. However, if I change
BOOST_SCOPE_EXIT_ALL(db)
to
BOOST_SCOPE_EXIT_ALL(&db)
it states that
unable to close due to unfinalized statements or unfinished backups
when trying to call sqlite3_close function.
If I pass the prepared_stmt by the reference too, it works as expected:
BOOST_SCOPE_EXIT_ALL(db, &prepared_stmt)
Why? I thought that the SQLite library only complains about the addresses that db and prepared_stmt points to.
Thanks in advance.
The first line in main()
sqlite3* db;
is probably why sqlite3_close() is crashing. Your compiler may be reusing a memory location for that declared pointer and may not necessarily be using a location that is null or making it null. Therefore, the call to sqlite3_close() is receiving a bogus pointer.
The docs at https://www.sqlite.org/c3ref/close.html state:
The C parameter to sqlite3_close(C) and sqlite3_close_v2(C) must be
either a NULL pointer or an sqlite3 object pointer obtained from
sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2(), and not
previously closed. Calling sqlite3_close() or sqlite3_close_v2() with
a NULL pointer argument is a harmless no-op.
So, according to the above statement, the following code should remove the crash:
sqlite3* db = nullptr;
Of course, all of this would be moot if you just removed this block of unecessary code entirely:
BOOST_SCOPE_EXIT_ALL(db)
{
if (sqlite3_close(db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << '\n';
}
};
There is no reason to close a db that has never been opened.
EDIT: Here is a version of your function as I would try it. I'm really out of time right now so I can't test it, but I think it will work. I removed the boost macros since I'm unfamiliar with them, but you could add those back in. Hopefully this helps!
int main()
{
sqlite3* db = nullptr;
if (sqlite3_open(":memory:", &db) != SQLITE_OK)
goto ERROR_OCCURRED;
sqlite3_stmt* prepared_stmt = nullptr;
if (sqlite3_finalize(prepared_stmt) != SQLITE_OK)
std::cerr << sqlite3_errmsg(db) << '\n';
if (sqlite3_prepare_v2(db, "CREATE TABLE IF NOT EXISTS foo(bar TEXT, baz TEXT)", -1, &prepared_stmt, 0) != SQLITE_OK)
goto ERROR_OCCURRED;
if (sqlite3_step(prepared_stmt) != SQLITE_DONE)
goto ERROR_OCCURRED;
if (sqlite3_close(db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << '\n';
sqlite3_close(db);
return EXIT_FAILURE;
}
return 0;
ERROR_OCCURRED:
std::cerr << sqlite3_errmsg(db) << '\n';
sqlite3_close(db);
return EXIT_FAILURE;
}
Unfortunately, I'm out-of-time to check your call to sqlite3_finalize() but I also think that it's probably too early in the function. I don't see a reason to finalize a null pointer.