Bug#864088: unblock (pre-approval): sqlite3/3.6.12-4
Package: release.debian.org
User: release.debian.org@packages.debian.org
Usertags: unblock
Hi Release Team,
I would like to upload a security related update for sqlite3. It contains:
- Prevent a possible NULL pointer dereference in the OP_Found opcode
that can follow an OOM error. Problem found by OSS-Fuzz[1],
- Stack overflow while parsing deeply nested JSON[2],
- JSON allows unescaped control characters in strings[3],
- JSON extension accepts invalid numeric values[4].
Upstream tagged these as 'code defect' and severity 'severe'. The
changes itself are small and the 3.19.2-1 version in experimental
contains these fixes.
Debdiff is attached. Thanks for consideration.
Regards,
Laszlo/GCS
[1] http://www.sqlite.org/src/info/c2de178fe7e2e4e0
[2] https://www.sqlite.org/src/info/981329adeef51011052
[3] https://www.sqlite.org/src/info/6c9b5514077fed34551
[4] https://www.sqlite.org/src/info/b93be8729a895a528e2
diff -Nru sqlite3-3.16.2/debian/changelog sqlite3-3.16.2/debian/changelog
--- sqlite3-3.16.2/debian/changelog 2017-02-13 17:31:26.000000000 +0000
+++ sqlite3-3.16.2/debian/changelog 2017-06-04 07:58:54.000000000 +0000
@@ -1,3 +1,13 @@
+sqlite3 (3.16.2-4) unstable; urgency=high
+
+ * Backport fix for a possible NULL pointer dereference in the OP_Found
+ opcode that can follow an OOM error.
+ * Backport fix for stack overflow while parsing deeply nested JSON.
+ * Backport fix for JSON allows unescaped control characters in strings.
+ * Backport fix for JSON extension accepts invalid numeric values.
+
+ -- Laszlo Boszormenyi (GCS) <gcs@debian.org> Sun, 04 Jun 2017 07:58:54 +0000
+
sqlite3 (3.16.2-3) unstable; urgency=medium
* Backport upstream fix to ensure that sqlite3_blob_reopen() correctly
diff -Nru sqlite3-3.16.2/debian/patches/36-OSS-Fuzz.patch sqlite3-3.16.2/debian/patches/36-OSS-Fuzz.patch
--- sqlite3-3.16.2/debian/patches/36-OSS-Fuzz.patch 1970-01-01 00:00:00.000000000 +0000
+++ sqlite3-3.16.2/debian/patches/36-OSS-Fuzz.patch 2017-06-04 07:58:54.000000000 +0000
@@ -0,0 +1,24 @@
+Index: sqlite3/src/vdbe.c
+==================================================================
+--- sqlite3/src/vdbe.c
++++ sqlite3/src/vdbe.c
+@@ -4017,14 +4017,16 @@
+ }
+ #endif
+ pIdxKey = &r;
+ pFree = 0;
+ }else{
++ assert( pIn3->flags & MEM_Blob );
++ rc = ExpandBlob(pIn3);
++ assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
++ if( rc ) goto no_mem;
+ pFree = pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
+ if( pIdxKey==0 ) goto no_mem;
+- assert( pIn3->flags & MEM_Blob );
+- (void)ExpandBlob(pIn3);
+ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
+ }
+ pIdxKey->default_rc = 0;
+ takeJump = 0;
+ if( pOp->opcode==OP_NoConflict ){
+
diff -Nru sqlite3-3.16.2/debian/patches/40-JSON-1.patch sqlite3-3.16.2/debian/patches/40-JSON-1.patch
--- sqlite3-3.16.2/debian/patches/40-JSON-1.patch 1970-01-01 00:00:00.000000000 +0000
+++ sqlite3-3.16.2/debian/patches/40-JSON-1.patch 2017-06-04 07:58:54.000000000 +0000
@@ -0,0 +1,205 @@
+Index: sqlite3/ext/misc/json1.c
+==================================================================
+--- sqlite3/ext/misc/json1.c
++++ sqlite3/ext/misc/json1.c
+@@ -726,17 +726,18 @@
+ char c;
+ u32 j;
+ int iThis;
+ int x;
+ JsonNode *pNode;
+- while( safe_isspace(pParse->zJson[i]) ){ i++; }
+- if( (c = pParse->zJson[i])=='{' ){
++ const char *z = pParse->zJson;
++ while( safe_isspace(z[i]) ){ i++; }
++ if( (c = z[i])=='{' ){
+ /* Parse object */
+ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+- while( safe_isspace(pParse->zJson[j]) ){ j++; }
++ while( safe_isspace(z[j]) ){ j++; }
+ x = jsonParseValue(pParse, j);
+ if( x<0 ){
+ if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+@@ -743,18 +744,18 @@
+ if( pParse->oom ) return -1;
+ pNode = &pParse->aNode[pParse->nNode-1];
+ if( pNode->eType!=JSON_STRING ) return -1;
+ pNode->jnFlags |= JNODE_LABEL;
+ j = x;
+- while( safe_isspace(pParse->zJson[j]) ){ j++; }
+- if( pParse->zJson[j]!=':' ) return -1;
++ while( safe_isspace(z[j]) ){ j++; }
++ if( z[j]!=':' ) return -1;
+ j++;
+ x = jsonParseValue(pParse, j);
+ if( x<0 ) return -1;
+ j = x;
+- while( safe_isspace(pParse->zJson[j]) ){ j++; }
+- c = pParse->zJson[j];
++ while( safe_isspace(z[j]) ){ j++; }
++ c = z[j];
+ if( c==',' ) continue;
+ if( c!='}' ) return -1;
+ break;
+ }
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+@@ -762,19 +763,19 @@
+ }else if( c=='[' ){
+ /* Parse array */
+ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+- while( safe_isspace(pParse->zJson[j]) ){ j++; }
++ while( safe_isspace(z[j]) ){ j++; }
+ x = jsonParseValue(pParse, j);
+ if( x<0 ){
+ if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+ j = x;
+- while( safe_isspace(pParse->zJson[j]) ){ j++; }
+- c = pParse->zJson[j];
++ while( safe_isspace(z[j]) ){ j++; }
++ c = z[j];
+ if( c==',' ) continue;
+ if( c!=']' ) return -1;
+ break;
+ }
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+@@ -782,75 +783,80 @@
+ }else if( c=='"' ){
+ /* Parse string */
+ u8 jnFlags = 0;
+ j = i+1;
+ for(;;){
+- c = pParse->zJson[j];
++ c = z[j];
+ if( c==0 ) return -1;
+ if( c=='\\' ){
+- c = pParse->zJson[++j];
++ c = z[++j];
+ if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
+ || c=='n' || c=='r' || c=='t'
+- || (c=='u' && jsonIs4Hex(pParse->zJson+j+1)) ){
++ || (c=='u' && jsonIs4Hex(z+j+1)) ){
+ jnFlags = JNODE_ESCAPE;
+ }else{
+ return -1;
+ }
+ }else if( c=='"' ){
+ break;
+ }
+ j++;
+ }
+- jsonParseAddNode(pParse, JSON_STRING, j+1-i, &pParse->zJson[i]);
++ jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]);
+ if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
+ return j+1;
+ }else if( c=='n'
+- && strncmp(pParse->zJson+i,"null",4)==0
+- && !safe_isalnum(pParse->zJson[i+4]) ){
++ && strncmp(z+i,"null",4)==0
++ && !safe_isalnum(z[i+4]) ){
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ return i+4;
+ }else if( c=='t'
+- && strncmp(pParse->zJson+i,"true",4)==0
+- && !safe_isalnum(pParse->zJson[i+4]) ){
++ && strncmp(z+i,"true",4)==0
++ && !safe_isalnum(z[i+4]) ){
+ jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
+ return i+4;
+ }else if( c=='f'
+- && strncmp(pParse->zJson+i,"false",5)==0
+- && !safe_isalnum(pParse->zJson[i+5]) ){
++ && strncmp(z+i,"false",5)==0
++ && !safe_isalnum(z[i+5]) ){
+ jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
+ return i+5;
+ }else if( c=='-' || (c>='0' && c<='9') ){
+ /* Parse number */
+ u8 seenDP = 0;
+ u8 seenE = 0;
++ assert( '-' < '0' );
++ if( c<='0' ){
++ j = c=='-' ? i+1 : i;
++ if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1;
++ }
+ j = i+1;
+ for(;; j++){
+- c = pParse->zJson[j];
++ c = z[j];
+ if( c>='0' && c<='9' ) continue;
+ if( c=='.' ){
+- if( pParse->zJson[j-1]=='-' ) return -1;
++ if( z[j-1]=='-' ) return -1;
+ if( seenDP ) return -1;
+ seenDP = 1;
+ continue;
+ }
+ if( c=='e' || c=='E' ){
+- if( pParse->zJson[j-1]<'0' ) return -1;
++ if( z[j-1]<'0' ) return -1;
+ if( seenE ) return -1;
+ seenDP = seenE = 1;
+- c = pParse->zJson[j+1];
++ c = z[j+1];
+ if( c=='+' || c=='-' ){
+ j++;
+- c = pParse->zJson[j+1];
++ c = z[j+1];
+ }
+ if( c<'0' || c>'9' ) return -1;
+ continue;
+ }
+ break;
+ }
+- if( pParse->zJson[j-1]<'0' ) return -1;
++ if( z[j-1]<'0' ) return -1;
+ jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
+- j - i, &pParse->zJson[i]);
++ j - i, &z[i]);
+ return j;
+ }else if( c=='}' ){
+ return -2; /* End of {...} */
+ }else if( c==']' ){
+ return -3; /* End of [...] */
+
+Index: sqlite3/test/json102.test
+==================================================================
+--- sqlite3/test/json102.test
++++ sqlite3/test/json102.test
+@@ -295,7 +295,28 @@
+ set str abcdef[string repeat \" [expr {$i+50}]]uvwxyz
+ do_test json102-[format %d [expr {$i+1300}]] {
+ db eval {SELECT json_extract(json_array($::str),'$[0]')==$::str}
+ } {1}
+ }
++
++#-------------------------------------------------------------------------
++# 2017-04-08 ticket b93be8729a895a528e2849fca99f7
++# JSON extension accepts invalid numeric values
++#
++# JSON does not allow leading zeros. But the JSON extension was
++# allowing them. The following tests verify that the problem is now
++# fixed.
++#
++do_execsql_test json102-1401 { SELECT json_valid('{"x":01}') } 0
++do_execsql_test json102-1402 { SELECT json_valid('{"x":-01}') } 0
++do_execsql_test json102-1403 { SELECT json_valid('{"x":0}') } 1
++do_execsql_test json102-1404 { SELECT json_valid('{"x":-0}') } 1
++do_execsql_test json102-1405 { SELECT json_valid('{"x":0.1}') } 1
++do_execsql_test json102-1406 { SELECT json_valid('{"x":-0.1}') } 1
++do_execsql_test json102-1407 { SELECT json_valid('{"x":0.0000}') } 1
++do_execsql_test json102-1408 { SELECT json_valid('{"x":-0.0000}') } 1
++do_execsql_test json102-1409 { SELECT json_valid('{"x":01.5}') } 0
++do_execsql_test json102-1410 { SELECT json_valid('{"x":-01.5}') } 0
++do_execsql_test json102-1411 { SELECT json_valid('{"x":00}') } 0
++do_execsql_test json102-1412 { SELECT json_valid('{"x":-00}') } 0
+
+ finish_test
+
diff -Nru sqlite3-3.16.2/debian/patches/41-JSON-2_1.patch sqlite3-3.16.2/debian/patches/41-JSON-2_1.patch
--- sqlite3-3.16.2/debian/patches/41-JSON-2_1.patch 1970-01-01 00:00:00.000000000 +0000
+++ sqlite3-3.16.2/debian/patches/41-JSON-2_1.patch 2017-06-04 07:58:54.000000000 +0000
@@ -0,0 +1,42 @@
+Index: sqlite3/ext/misc/json1.c
+==================================================================
+--- sqlite3/ext/misc/json1.c
++++ sqlite3/ext/misc/json1.c
+@@ -784,11 +784,11 @@
+ /* Parse string */
+ u8 jnFlags = 0;
+ j = i+1;
+ for(;;){
+ c = z[j];
+- if( c==0 ) return -1;
++ if( c<=0x1f ) return -1; /* Control characters not allowed in strings */
+ if( c=='\\' ){
+ c = z[++j];
+ if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
+ || c=='n' || c=='r' || c=='t'
+ || (c=='u' && jsonIs4Hex(z+j+1)) ){
+
+Index: sqlite3/test/json102.test
+==================================================================
+--- sqlite3/test/json102.test
++++ sqlite3/test/json102.test
+@@ -316,7 +316,18 @@
+ do_execsql_test json102-1408 { SELECT json_valid('{"x":-0.0000}') } 1
+ do_execsql_test json102-1409 { SELECT json_valid('{"x":01.5}') } 0
+ do_execsql_test json102-1410 { SELECT json_valid('{"x":-01.5}') } 0
+ do_execsql_test json102-1411 { SELECT json_valid('{"x":00}') } 0
+ do_execsql_test json102-1412 { SELECT json_valid('{"x":-00}') } 0
++
++#------------------------------------------------------------------------
++# 2017-04-10 ticket 6c9b5514077fed34551f98e64c09a10dc2fc8e16
++# JSON extension accepts strings containing control characters.
++#
++# The JSON spec requires that all control characters be escaped.
++#
++do_execsql_test json102-1500 {
++ WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<0x20)
++ SELECT x FROM c WHERE json_valid(printf('{"a":"x%sz"}', char(x))) ORDER BY x;
++} {32}
+
+ finish_test
+
diff -Nru sqlite3-3.16.2/debian/patches/42-JSON-2_2.patch sqlite3-3.16.2/debian/patches/42-JSON-2_2.patch
--- sqlite3-3.16.2/debian/patches/42-JSON-2_2.patch 1970-01-01 00:00:00.000000000 +0000
+++ sqlite3-3.16.2/debian/patches/42-JSON-2_2.patch 2017-06-04 07:58:54.000000000 +0000
@@ -0,0 +1,46 @@
+Index: sqlite3/ext/misc/json1.c
+==================================================================
+--- sqlite3/ext/misc/json1.c
++++ sqlite3/ext/misc/json1.c
+@@ -784,11 +784,14 @@
+ /* Parse string */
+ u8 jnFlags = 0;
+ j = i+1;
+ for(;;){
+ c = z[j];
+- if( c<=0x1f ) return -1; /* Control characters not allowed in strings */
++ if( (c & ~0x1f)==0 ){
++ /* Control characters are not allowed in strings */
++ return -1;
++ }
+ if( c=='\\' ){
+ c = z[++j];
+ if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
+ || c=='n' || c=='r' || c=='t'
+ || (c=='u' && jsonIs4Hex(z+j+1)) ){
+
+Index: sqlite3/test/json101.test
+==================================================================
+--- sqlite3/test/json101.test
++++ sqlite3/test/json101.test
+@@ -353,10 +353,19 @@
+ SELECT b FROM t8;
+ } {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}}
+ do_execsql_test json-8.2 {
+ SELECT a=json_extract(b,'$[0]') FROM t8;
+ } {1}
++
++# 2017-04-12. Regression reported on the mailing list by Rolf Ade
++#
++do_execsql_test json-8.3 {
++ SELECT json_valid(char(0x22,0xe4,0x22));
++} {1}
++do_execsql_test json-8.4 {
++ SELECT unicode(json_extract(char(0x22,228,0x22),'$'));
++} {228}
+
+ # The json_quote() function transforms an SQL value into a JSON value.
+ # String values are quoted and interior quotes are escaped. NULL values
+ # are rendered as the unquoted string "null".
+ #
+
diff -Nru sqlite3-3.16.2/debian/patches/43-JSON-3.patch sqlite3-3.16.2/debian/patches/43-JSON-3.patch
--- sqlite3-3.16.2/debian/patches/43-JSON-3.patch 1970-01-01 00:00:00.000000000 +0000
+++ sqlite3-3.16.2/debian/patches/43-JSON-3.patch 2017-06-04 07:58:54.000000000 +0000
@@ -0,0 +1,130 @@
+Index: sqlite3/ext/misc/json1.c
+==================================================================
+--- sqlite3/ext/misc/json1.c
++++ sqlite3/ext/misc/json1.c
+@@ -88,10 +88,11 @@
+ #ifndef SQLITE_AMALGAMATION
+ /* Unsigned integer types. These are already defined in the sqliteInt.h,
+ ** but the definitions need to be repeated for separate compilation. */
+ typedef sqlite3_uint64 u64;
+ typedef unsigned int u32;
++ typedef unsigned short int u16;
+ typedef unsigned char u8;
+ #endif
+
+ /* Objects */
+ typedef struct JsonString JsonString;
+@@ -165,12 +166,22 @@
+ JsonNode *aNode; /* Array of nodes containing the parse */
+ const char *zJson; /* Original JSON string */
+ u32 *aUp; /* Index of parent of each node */
+ u8 oom; /* Set to true if out of memory */
+ u8 nErr; /* Number of errors seen */
++ u16 iDepth; /* Nesting depth */
+ };
+
++/*
++** Maximum nesting depth of JSON for this implementation.
++**
++** This limit is needed to avoid a stack overflow in the recursive
++** descent parser. A depth of 2000 is far deeper than any sane JSON
++** should go.
++*/
++#define JSON_MAX_DEPTH 2000
++
+ /**************************************************************************
+ ** Utility routines for dealing with JsonString objects
+ **************************************************************************/
+
+ /* Set the JsonString object to an empty string
+@@ -734,12 +745,14 @@
+ /* Parse object */
+ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+ while( safe_isspace(z[j]) ){ j++; }
++ if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
+ x = jsonParseValue(pParse, j);
+ if( x<0 ){
++ pParse->iDepth--;
+ if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+ if( pParse->oom ) return -1;
+ pNode = &pParse->aNode[pParse->nNode-1];
+@@ -748,10 +761,11 @@
+ j = x;
+ while( safe_isspace(z[j]) ){ j++; }
+ if( z[j]!=':' ) return -1;
+ j++;
+ x = jsonParseValue(pParse, j);
++ pParse->iDepth--;
+ if( x<0 ) return -1;
+ j = x;
+ while( safe_isspace(z[j]) ){ j++; }
+ c = z[j];
+ if( c==',' ) continue;
+@@ -764,11 +778,13 @@
+ /* Parse array */
+ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+ while( safe_isspace(z[j]) ){ j++; }
++ if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
+ x = jsonParseValue(pParse, j);
++ pParse->iDepth--;
+ if( x<0 ){
+ if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+ j = x;
+@@ -887,10 +903,11 @@
+ if( zJson==0 ) return 1;
+ pParse->zJson = zJson;
+ i = jsonParseValue(pParse, 0);
+ if( pParse->oom ) i = -1;
+ if( i>0 ){
++ assert( pParse->iDepth==0 );
+ while( safe_isspace(zJson[i]) ) i++;
+ if( zJson[i] ) i = -1;
+ }
+ if( i<=0 ){
+ if( pCtx!=0 ){
+
+Index: sqlite3/test/json101.test
+==================================================================
+--- sqlite3/test/json101.test
++++ sqlite3/test/json101.test
+@@ -695,7 +695,31 @@
+ } {0}
+ do_execsql_test json-10.95 {
+ SELECT json_valid('" \~ "');
+ } {0}
+
++#--------------------------------------------------------------------------
++# 2017-04-11. https://www.sqlite.org/src/info/981329adeef51011
++# Stack overflow on deeply nested JSON.
++#
++# The following tests confirm that deeply nested JSON is considered invalid.
++#
++do_execsql_test json-11.0 {
++ /* Shallow enough to be parsed */
++ SELECT json_valid(printf('%.2000c0%.2000c','[',']'));
++} {1}
++do_execsql_test json-11.1 {
++ /* Too deep by one */
++ SELECT json_valid(printf('%.2001c0%.2001c','[',']'));
++} {0}
++do_execsql_test json-11.2 {
++ /* Shallow enough to be parsed { */
++ SELECT json_valid(replace(printf('%.2000c0%.2000c','[','}'),'[','{"a":'));
++ /* } */
++} {1}
++do_execsql_test json-11.3 {
++ /* Too deep by one { */
++ SELECT json_valid(replace(printf('%.2001c0%.2001c','[','}'),'[','{"a":'));
++ /* } */
++} {0}
+
+ finish_test
+
diff -Nru sqlite3-3.16.2/debian/patches/series sqlite3-3.16.2/debian/patches/series
--- sqlite3-3.16.2/debian/patches/series 2017-02-13 17:31:26.000000000 +0000
+++ sqlite3-3.16.2/debian/patches/series 2017-06-04 07:58:54.000000000 +0000
@@ -7,3 +7,8 @@
02-use-packaged-lempar.c.patch
32-fix_an_uninitialized_variable_in_the_command-line_shell.patch
35-fix-sqlite3_blob_reopen.patch
+36-OSS-Fuzz.patch
+40-JSON-1.patch
+41-JSON-2_1.patch
+42-JSON-2_2.patch
+43-JSON-3.patch
Reply to: