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.
Commit
be0d95adedbc7ce63ec12a7774903f4124b2c1c70b431af6bbb61ff94964b7be
Parent
8c16884aa2d1631…
1 file changed
+23
-1
M
src/db.c
+23
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -69,10 +69,11 @@ | ||
| 69 | 69 | #endif /* INTERFACE */ |
| 70 | 70 | const struct Stmt empty_Stmt = empty_Stmt_m; |
| 71 | 71 | |
| 72 | 72 | /* |
| 73 | 73 | ** Call this routine when a database error occurs. |
| 74 | +** This routine throws a fatal error. It does not return. | |
| 74 | 75 | */ |
| 75 | 76 | static void db_err(const char *zFormat, ...){ |
| 76 | 77 | va_list ap; |
| 77 | 78 | char *z; |
| 78 | 79 | va_start(ap, zFormat); |
| @@ -363,21 +364,24 @@ | ||
| 363 | 364 | */ |
| 364 | 365 | int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){ |
| 365 | 366 | int rc; |
| 366 | 367 | int prepFlags = 0; |
| 367 | 368 | char *zSql; |
| 369 | + const char *zExtra = 0; | |
| 368 | 370 | blob_zero(&pStmt->sql); |
| 369 | 371 | blob_vappendf(&pStmt->sql, zFormat, ap); |
| 370 | 372 | va_end(ap); |
| 371 | 373 | zSql = blob_str(&pStmt->sql); |
| 372 | 374 | db.nPrepare++; |
| 373 | 375 | if( flags & DB_PREPARE_PERSISTENT ){ |
| 374 | 376 | prepFlags = SQLITE_PREPARE_PERSISTENT; |
| 375 | 377 | } |
| 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); | |
| 377 | 379 | if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){ |
| 378 | 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); | |
| 379 | 383 | } |
| 380 | 384 | pStmt->pNext = db.pAllStmt; |
| 381 | 385 | pStmt->pPrev = 0; |
| 382 | 386 | if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt; |
| 383 | 387 | db.pAllStmt = pStmt; |
| @@ -640,10 +644,11 @@ | ||
| 640 | 644 | return rc; |
| 641 | 645 | } |
| 642 | 646 | |
| 643 | 647 | /* |
| 644 | 648 | ** COMMAND: test-db-exec-error |
| 649 | +** Usage: %fossil test-db-exec-error | |
| 645 | 650 | ** |
| 646 | 651 | ** Invoke the db_exec() interface with an erroneous SQL statement |
| 647 | 652 | ** in order to verify the error handling logic. |
| 648 | 653 | */ |
| 649 | 654 | void db_test_db_exec_cmd(void){ |
| @@ -650,10 +655,27 @@ | ||
| 650 | 655 | Stmt err; |
| 651 | 656 | db_find_and_open_repository(0,0); |
| 652 | 657 | db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);"); |
| 653 | 658 | db_exec(&err); |
| 654 | 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 | +} | |
| 655 | 677 | |
| 656 | 678 | /* |
| 657 | 679 | ** Print the output of one or more SQL queries on standard output. |
| 658 | 680 | ** This routine is used for debugging purposes only. |
| 659 | 681 | */ |
| 660 | 682 |
| --- 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 |