1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Fix segfaults that can occur if a malloc failure happens just before

a built-in function calls sqlite3_value_text(). (CVS 3874)

FossilOrigin-Name: 9cb0ed6ee9827bc6884a0195044d5b6ad0de698e
This commit is contained in:
drh
2007-04-25 18:23:52 +00:00
parent 0b3d55d4f9
commit 7a521cfb79
6 changed files with 214 additions and 42 deletions

View File

@ -1,5 +1,5 @@
C Disable\stests\sin\smisc7\sthat\sdo\snot\swork\son\swindows\sdue\sto\slimitations\sof\nthe\swindows\sfile\ssystem.\s(CVS\s3873)
D 2007-04-25T15:42:26
C Fix\ssegfaults\sthat\scan\soccur\sif\sa\smalloc\sfailure\shappens\sjust\sbefore\na\sbuilt-in\sfunction\scalls\ssqlite3_value_text().\s(CVS\s3874)
D 2007-04-25T18:23:53
F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@ -64,11 +64,11 @@ F src/btree.h 9b2cc0d113c0bc2d37d244b9a394d56948c9acbf
F src/build.c 1880da163d9aa404016242b8b76d69907f682cd8
F src/callback.c 6414ed32d55859d0f65067aa5b88d2da27b3af9e
F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675
F src/date.c 74b76691bddf58b634f6bf4a77c8c58234268c6e
F src/date.c 94a6777df13d2aaacd19de080d9e8d3444364133
F src/delete.c 5c0d89b3ef7d48fe1f5124bfe8341f982747fe29
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
F src/expr.c 2f0f9f89efe9170e5e6ca5d5e93a9d5896fff5ac
F src/func.c 007d957c057bb42b0d37aa6ad4be0e1c67a8871b
F src/func.c acb2c5055629ae3eebd71868af10fe425ef05f06
F src/hash.c 67b23e14f0257b69a3e8aa663e4eeadc1a2b6fd5
F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564
F src/insert.c 413cc06990cb3c401e64e596776c1e43934f8841
@ -128,7 +128,7 @@ F src/vacuum.c 8bd895d29e7074e78d4e80f948e35ddc9cf2beef
F src/vdbe.c 814dab208a156250bc5e77f827f4e0c8ad734820
F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691
F src/vdbeInt.h 4b19fd8febad3fd14c4c97adaefc06754d323132
F src/vdbeapi.c 1fca7ff056d03f131caa6b1296bb221da65ed7f4
F src/vdbeapi.c 245263aa2d70d87b1201753cddc881996f219843
F src/vdbeaux.c ef59545f53f90394283f2fd003375d3ebbf0bd6e
F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
F src/vdbemem.c 981a113405bd9b80aeb71fe246a2f01708e8a8f7
@ -273,6 +273,7 @@ F test/malloc4.test 59cd02f71b363302a04c4e77b97c0a1572eaa210
F test/malloc5.test f228cb7101ae403327824d327a1f5651d83ef0f2
F test/malloc6.test 025ae0b78542e0ddd000d23f79d93e9be9ba0f15
F test/malloc7.test 1cf52834509eac7ebeb92105dacd4669f9ca9869
F test/malloc8.test ede3231e1d9359b3c618357e49cb1c62267382e7
F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8
F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893
F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
@ -461,7 +462,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
P 16979f4525652bfd6c6e5306eafc883bef3880aa
R 28b0457dabf779eb2bc14720d6227390
P 66646d6fda067e19240808aef65fafd8fa177cdd
R 48b7346bd8cf5e77d4009859f7c6c683
U drh
Z 15c285b6aa265216fe50f5053ca77b43
Z fd08492b87e717e5d32a8be7d387293c

View File

@ -1 +1 @@
66646d6fda067e19240808aef65fafd8fa177cdd
9cb0ed6ee9827bc6884a0195044d5b6ad0de698e

View File

@ -16,7 +16,7 @@
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
** All other code has file scope.
**
** $Id: date.c,v 1.62 2007/04/06 02:32:34 drh Exp $
** $Id: date.c,v 1.63 2007/04/25 18:23:53 drh Exp $
**
** NOTES:
**
@ -655,12 +655,15 @@ static int parseModifier(const char *zMod, DateTime *p){
*/
static int isDate(int argc, sqlite3_value **argv, DateTime *p){
int i;
const unsigned char *z;
if( argc==0 ) return 1;
if( SQLITE_NULL==sqlite3_value_type(argv[0]) ||
parseDateOrTime((char*)sqlite3_value_text(argv[0]), p) ) return 1;
if( (z = sqlite3_value_text(argv[0]))==0 || parseDateOrTime((char*)z, p) ){
return 1;
}
for(i=1; i<argc; i++){
if( SQLITE_NULL==sqlite3_value_type(argv[i]) ||
parseModifier((char*)sqlite3_value_text(argv[i]), p) ) return 1;
if( (z = sqlite3_value_text(argv[i]))==0 || parseModifier((char*)z, p) ){
return 1;
}
}
return 0;
}

View File

@ -16,7 +16,7 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
** $Id: func.c,v 1.139 2007/04/10 13:51:18 drh Exp $
** $Id: func.c,v 1.140 2007/04/25 18:23:53 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -102,6 +102,7 @@ static void lengthFunc(
}
case SQLITE_TEXT: {
const unsigned char *z = sqlite3_value_text(argv[0]);
if( z==0 ) return;
for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; }
sqlite3_result_int(context, len);
break;
@ -212,30 +213,38 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
** Implementation of the upper() and lower() SQL functions.
*/
static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
unsigned char *z;
char *z1;
const char *z2;
int i;
if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1);
if( z==0 ) return;
strcpy((char*)z, (char*)sqlite3_value_text(argv[0]));
for(i=0; z[i]; i++){
z[i] = toupper(z[i]);
z2 = (char*)sqlite3_value_text(argv[0]);
if( z2 ){
z1 = sqlite3_malloc(sqlite3_value_bytes(argv[0])+1);
if( z1 ){
strcpy(z1, z2);
for(i=0; z1[i]; i++){
z1[i] = toupper(z1[i]);
}
sqlite3_result_text(context, z1, -1, sqlite3_free);
}
}
sqlite3_result_text(context, (char*)z, -1, SQLITE_TRANSIENT);
sqliteFree(z);
}
static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
unsigned char *z;
char *z1;
const char *z2;
int i;
if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1);
if( z==0 ) return;
strcpy((char*)z, (char*)sqlite3_value_text(argv[0]));
for(i=0; z[i]; i++){
z[i] = tolower(z[i]);
z2 = (char*)sqlite3_value_text(argv[0]);
if( z2 ){
z1 = sqlite3_malloc(sqlite3_value_bytes(argv[0])+1);
if( z1 ){
strcpy(z1, z2);
for(i=0; z1[i]; i++){
z1[i] = tolower(z1[i]);
}
sqlite3_result_text(context, z1, -1, sqlite3_free);
}
}
sqlite3_result_text(context, (char*)z, -1, SQLITE_TRANSIENT);
sqliteFree(z);
}
/*
@ -523,6 +532,7 @@ static void likeFunc(
** Otherwise, return an error.
*/
const unsigned char *zEsc = sqlite3_value_text(argv[2]);
if( zEsc==0 ) return;
if( sqlite3utf8CharLen((char*)zEsc, -1)!=1 ){
sqlite3_result_error(context,
"ESCAPE expression must be a single character", -1);
@ -625,6 +635,7 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
const unsigned char *zArg = sqlite3_value_text(argv[0]);
char *z;
if( zArg==0 ) return;
for(i=n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; }
z = sqliteMalloc( i+n+3 );
if( z==0 ) return;
@ -692,16 +703,14 @@ static void replaceFunc(
int i, j; /* Loop counters */
assert( argc==3 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ||
sqlite3_value_type(argv[1])==SQLITE_NULL ||
sqlite3_value_type(argv[2])==SQLITE_NULL ){
return;
}
zStr = sqlite3_value_text(argv[0]);
if( zStr==0 ) return;
nStr = sqlite3_value_bytes(argv[0]);
zPattern = sqlite3_value_text(argv[1]);
if( zPattern==0 ) return;
nPattern = sqlite3_value_bytes(argv[1]);
zRep = sqlite3_value_text(argv[2]);
if( zRep==0 ) return;
nRep = sqlite3_value_bytes(argv[2]);
if( nPattern>=nRep ){
nOut = nStr;
@ -746,14 +755,13 @@ static void trimFunc(
return;
}
zIn = sqlite3_value_text(argv[0]);
if( zIn==0 ) return;
nIn = sqlite3_value_bytes(argv[0]);
if( argc==1 ){
static const unsigned char zSpace[] = " ";
zCharSet = zSpace;
}else if( sqlite3_value_type(argv[1])==SQLITE_NULL ){
}else if( (zCharSet = sqlite3_value_text(argv[1]))==0 ){
return;
}else{
zCharSet = sqlite3_value_text(argv[1]);
}
cFirst = zCharSet[0];
if( cFirst ){
@ -834,14 +842,16 @@ static void soundexFunc(
*/
static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
const char *zFile = (const char *)sqlite3_value_text(argv[0]);
const char *zProc = 0;
const char *zProc;
sqlite3 *db = sqlite3_user_data(context);
char *zErrMsg = 0;
if( argc==2 ){
zProc = (const char *)sqlite3_value_text(argv[1]);
}else{
zProc = 0;
}
if( sqlite3_load_extension(db, zFile, zProc, &zErrMsg) ){
if( zFile && sqlite3_load_extension(db, zFile, zProc, &zErrMsg) ){
sqlite3_result_error(context, zErrMsg, -1);
sqlite3_free(zErrMsg);
}

View File

@ -443,7 +443,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
Vdbe *pVm = (Vdbe *)pStmt;
int vals = sqlite3_data_count(pStmt);
if( i>=vals || i<0 ){
static const Mem nullMem = {{0}, 0.0, "", 0, MEM_Null, MEM_Null };
static const Mem nullMem = {{0}, 0.0, "", 0, MEM_Null, SQLITE_NULL };
sqlite3Error(pVm->db, SQLITE_RANGE, 0);
return (Mem*)&nullMem;
}

158
test/malloc8.test Normal file
View File

@ -0,0 +1,158 @@
# 2006 July 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl)
# added to expose a bug in out-of-memory handling for sqlite3_value_text()
#
# $Id: malloc8.test,v 1.1 2007/04/25 18:23:53 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
finish_test
return
}
# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
# -tclprep TCL script to run to prepare test.
# -sqlprep SQL script to run to prepare test.
# -tclbody TCL script to run with malloc failure simulation.
# -sqlbody TCL script to run with malloc failure simulation.
# -cleanup TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
array unset ::mallocopts
array set ::mallocopts $args
set ::go 1
for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
do_test malloc8-$tn.$::n {
sqlite_malloc_fail 0
catch {db close}
sqlite3 db test.db
set ::DB [sqlite3_connection_pointer db]
# Execute any -tclprep and -sqlprep scripts.
#
if {[info exists ::mallocopts(-tclprep)]} {
eval $::mallocopts(-tclprep)
}
if {[info exists ::mallocopts(-sqlprep)]} {
execsql $::mallocopts(-sqlprep)
}
# Now set the ${::n}th malloc() to fail and execute the -tclbody and
# -sqlbody scripts.
#
sqlite_malloc_fail $::n
set ::mallocbody {}
if {[info exists ::mallocopts(-tclbody)]} {
append ::mallocbody "$::mallocopts(-tclbody)\n"
}
if {[info exists ::mallocopts(-sqlbody)]} {
append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
}
set v [catch $::mallocbody msg]
# If the test fails (if $v!=0) and the database connection actually
# exists, make sure the failure code is SQLITE_NOMEM.
if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
&& [db errorcode]!=7} {
set v 999
}
set leftover [lindex [sqlite_malloc_stat] 2]
if {$leftover>0} {
if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"}
set ::go 0
if {$v} {
puts "\nError message returned: $msg"
} else {
set v {1 1}
}
} else {
set v2 [expr {$msg=="" || $msg=="out of memory"}]
if {!$v2} {puts "\nError message returned: $msg"}
lappend v $v2
}
} {1 1}
if {[info exists ::mallocopts(-cleanup)]} {
catch [list uplevel #0 $::mallocopts(-cleanup)] msg
}
}
unset ::mallocopts
}
# The setup is a database with UTF-16 encoding that contains a single
# large string. We will be running lots of queries against this
# database. Because we will be extracting the string as UTF-8, there
# is a type conversion that occurs and thus an opportunity for malloc()
# to fail and for sqlite3_value_text() to return 0 even though
# sqlite3_value_type() returns SQLITE_TEXT.
#
db close
file delete -force test.db test.db-journal
sqlite3 db test.db
db eval {
PRAGMA encoding='UTF-16';
CREATE TABLE t1(a);
INSERT INTO t1
VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
}
do_malloc_test 1 -sqlbody {
SELECT lower(a), upper(a), quote(a), trim(a), trim('x',a) FROM t1;
}
do_malloc_test 2 -sqlbody {
SELECT replace(a,'x','y'), replace('x',a,'y'), replace('x','y',a)
FROM t1;
}
do_malloc_test 3 -sqlbody {
SELECT length(a), substr(a, 4, 4) FROM t1;
}
do_malloc_test 4 -sqlbody {
SELECT julianday(a,a) FROM t1;
}
do_malloc_test 5 -sqlbody {
SELECT 1 FROM t1 WHERE a LIKE 'hello' ESCAPE NULL;
}
# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
catch {db close}
set sqlite_open_file_count
} {0}
sqlite_malloc_fail 0
finish_test