Fossil SCM

Enhance the db_prepare() and db_static_prepare() utility routines so that they throw an error if handed more than one SQL statement. This might help prevent SQL injection attacks.

drh 2020-08-17 18:57 sec2020
Commit be0d95adedbc7ce63ec12a7774903f4124b2c1c70b431af6bbb61ff94964b7be
1 file changed +23 -1
+23 -1
--- src/db.c
+++ src/db.c
@@ -69,10 +69,11 @@
6969
#endif /* INTERFACE */
7070
const struct Stmt empty_Stmt = empty_Stmt_m;
7171
7272
/*
7373
** Call this routine when a database error occurs.
74
+** This routine throws a fatal error. It does not return.
7475
*/
7576
static void db_err(const char *zFormat, ...){
7677
va_list ap;
7778
char *z;
7879
va_start(ap, zFormat);
@@ -363,21 +364,24 @@
363364
*/
364365
int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
365366
int rc;
366367
int prepFlags = 0;
367368
char *zSql;
369
+ const char *zExtra = 0;
368370
blob_zero(&pStmt->sql);
369371
blob_vappendf(&pStmt->sql, zFormat, ap);
370372
va_end(ap);
371373
zSql = blob_str(&pStmt->sql);
372374
db.nPrepare++;
373375
if( flags & DB_PREPARE_PERSISTENT ){
374376
prepFlags = SQLITE_PREPARE_PERSISTENT;
375377
}
376
- rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0);
378
+ rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
377379
if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
378380
db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
381
+ }else if( zExtra && !fossil_all_whitespace(zExtra) ){
382
+ db_err("surplus text follows SQL: \"%s\"", zExtra);
379383
}
380384
pStmt->pNext = db.pAllStmt;
381385
pStmt->pPrev = 0;
382386
if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt;
383387
db.pAllStmt = pStmt;
@@ -640,10 +644,11 @@
640644
return rc;
641645
}
642646
643647
/*
644648
** COMMAND: test-db-exec-error
649
+** Usage: %fossil test-db-exec-error
645650
**
646651
** Invoke the db_exec() interface with an erroneous SQL statement
647652
** in order to verify the error handling logic.
648653
*/
649654
void db_test_db_exec_cmd(void){
@@ -650,10 +655,27 @@
650655
Stmt err;
651656
db_find_and_open_repository(0,0);
652657
db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);");
653658
db_exec(&err);
654659
}
660
+
661
+/*
662
+** COMMAND: test-db-prepare
663
+** Usage: %fossil test-db-prepare ?OPTIONS? SQL
664
+**
665
+** Invoke db_prepare() on the SQL input. Report any errors encountered.
666
+** This command is used to verify error detection logic in the db_prepare()
667
+** utility routine.
668
+*/
669
+void db_test_db_prepare(void){
670
+ Stmt err;
671
+ db_find_and_open_repository(0,0);
672
+ verify_all_options();
673
+ if( g.argc!=3 ) usage("?OPTIONS? SQL");
674
+ db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/);
675
+ db_finalize(&err);
676
+}
655677
656678
/*
657679
** Print the output of one or more SQL queries on standard output.
658680
** This routine is used for debugging purposes only.
659681
*/
660682
--- src/db.c
+++ src/db.c
@@ -69,10 +69,11 @@
69 #endif /* INTERFACE */
70 const struct Stmt empty_Stmt = empty_Stmt_m;
71
72 /*
73 ** Call this routine when a database error occurs.
 
74 */
75 static void db_err(const char *zFormat, ...){
76 va_list ap;
77 char *z;
78 va_start(ap, zFormat);
@@ -363,21 +364,24 @@
363 */
364 int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
365 int rc;
366 int prepFlags = 0;
367 char *zSql;
 
368 blob_zero(&pStmt->sql);
369 blob_vappendf(&pStmt->sql, zFormat, ap);
370 va_end(ap);
371 zSql = blob_str(&pStmt->sql);
372 db.nPrepare++;
373 if( flags & DB_PREPARE_PERSISTENT ){
374 prepFlags = SQLITE_PREPARE_PERSISTENT;
375 }
376 rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0);
377 if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
378 db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
 
 
379 }
380 pStmt->pNext = db.pAllStmt;
381 pStmt->pPrev = 0;
382 if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt;
383 db.pAllStmt = pStmt;
@@ -640,10 +644,11 @@
640 return rc;
641 }
642
643 /*
644 ** COMMAND: test-db-exec-error
 
645 **
646 ** Invoke the db_exec() interface with an erroneous SQL statement
647 ** in order to verify the error handling logic.
648 */
649 void db_test_db_exec_cmd(void){
@@ -650,10 +655,27 @@
650 Stmt err;
651 db_find_and_open_repository(0,0);
652 db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);");
653 db_exec(&err);
654 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
655
656 /*
657 ** Print the output of one or more SQL queries on standard output.
658 ** This routine is used for debugging purposes only.
659 */
660
--- src/db.c
+++ src/db.c
@@ -69,10 +69,11 @@
69 #endif /* INTERFACE */
70 const struct Stmt empty_Stmt = empty_Stmt_m;
71
72 /*
73 ** Call this routine when a database error occurs.
74 ** This routine throws a fatal error. It does not return.
75 */
76 static void db_err(const char *zFormat, ...){
77 va_list ap;
78 char *z;
79 va_start(ap, zFormat);
@@ -363,21 +364,24 @@
364 */
365 int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
366 int rc;
367 int prepFlags = 0;
368 char *zSql;
369 const char *zExtra = 0;
370 blob_zero(&pStmt->sql);
371 blob_vappendf(&pStmt->sql, zFormat, ap);
372 va_end(ap);
373 zSql = blob_str(&pStmt->sql);
374 db.nPrepare++;
375 if( flags & DB_PREPARE_PERSISTENT ){
376 prepFlags = SQLITE_PREPARE_PERSISTENT;
377 }
378 rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
379 if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
380 db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
381 }else if( zExtra && !fossil_all_whitespace(zExtra) ){
382 db_err("surplus text follows SQL: \"%s\"", zExtra);
383 }
384 pStmt->pNext = db.pAllStmt;
385 pStmt->pPrev = 0;
386 if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt;
387 db.pAllStmt = pStmt;
@@ -640,10 +644,11 @@
644 return rc;
645 }
646
647 /*
648 ** COMMAND: test-db-exec-error
649 ** Usage: %fossil test-db-exec-error
650 **
651 ** Invoke the db_exec() interface with an erroneous SQL statement
652 ** in order to verify the error handling logic.
653 */
654 void db_test_db_exec_cmd(void){
@@ -650,10 +655,27 @@
655 Stmt err;
656 db_find_and_open_repository(0,0);
657 db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);");
658 db_exec(&err);
659 }
660
661 /*
662 ** COMMAND: test-db-prepare
663 ** Usage: %fossil test-db-prepare ?OPTIONS? SQL
664 **
665 ** Invoke db_prepare() on the SQL input. Report any errors encountered.
666 ** This command is used to verify error detection logic in the db_prepare()
667 ** utility routine.
668 */
669 void db_test_db_prepare(void){
670 Stmt err;
671 db_find_and_open_repository(0,0);
672 verify_all_options();
673 if( g.argc!=3 ) usage("?OPTIONS? SQL");
674 db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/);
675 db_finalize(&err);
676 }
677
678 /*
679 ** Print the output of one or more SQL queries on standard output.
680 ** This routine is used for debugging purposes only.
681 */
682

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button