]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Protect the JsonNode.u union using nearby assert()s and branches.
authordrh <>
Fri, 15 Oct 2021 16:15:04 +0000 (16:15 +0000)
committerdrh <>
Fri, 15 Oct 2021 16:15:04 +0000 (16:15 +0000)
FossilOrigin-Name: 7b8ea2298927fd34f27b3345add3ce751ed728387fe3d9207b601ba6449d5af9

ext/misc/json1.c
manifest
manifest.uuid

index 0a3e907be6f65088fb2b1dbe7632ddfd30e2e8b7..df6dcba76df4b57c66a45fe818541dd29e2dabf4 100644 (file)
@@ -109,7 +109,21 @@ static const char jsonIsSpace[] = {
 #   define ALWAYS(X)      (X)
 #   define NEVER(X)       (X)
 # endif
+# define testcase(X)
 #endif
+#if defined(NDEBUG)
+#  define VVA(X)
+#else
+#  define VVA(X) X
+#endif
+
+/*
+** Some of the testcase() macros in this file are problematic for gcov
+** in that they generate false-miss errors randomly.  This is a gcov problem,
+** not a problem in this case.  But to work around it, we disable the
+** problematic test cases for production builds.
+*/
+#define json_testcase(X)
 
 /* Objects */
 typedef struct JsonString JsonString;
@@ -167,13 +181,14 @@ static const char * const jsonType[] = {
 struct JsonNode {
   u8 eType;              /* One of the JSON_ type values */
   u8 jnFlags;            /* JNODE flags */
+  u8 eU;                 /* Which union element to use */
   u32 n;                 /* Bytes of content, or number of sub-nodes */
   union {
-    const char *zJContent; /* Content for INT, REAL, and STRING */
-    u32 iAppend;           /* More terms for ARRAY and OBJECT */
-    u32 iKey;              /* Key for ARRAY objects in json_tree() */
-    u32 iReplace;          /* Replacement content for JNODE_REPLACE */
-    JsonNode *pPatch;      /* Node chain of patch for JNODE_PATCH */
+    const char *zJContent; /* 1: Content for INT, REAL, and STRING */
+    u32 iAppend;           /* 2: More terms for ARRAY and OBJECT */
+    u32 iKey;              /* 3: Key for ARRAY objects in json_tree() */
+    u32 iReplace;          /* 4: Replacement content for JNODE_REPLACE */
+    JsonNode *pPatch;      /* 5: Node chain of patch for JNODE_PATCH */
   } u;
 };
 
@@ -454,9 +469,11 @@ static void jsonRenderNode(
   assert( pNode!=0 );
   if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){
     if( (pNode->jnFlags & JNODE_REPLACE)!=0 && ALWAYS(aReplace!=0) ){
+      assert( pNode->eU==4 );
       jsonAppendValue(pOut, aReplace[pNode->u.iReplace]);
       return;
     }
+    assert( pNode->eU==5 );
     pNode = pNode->u.pPatch;
   }
   switch( pNode->eType ){
@@ -475,6 +492,7 @@ static void jsonRenderNode(
     }
     case JSON_STRING: {
       if( pNode->jnFlags & JNODE_RAW ){
+        assert( pNode->eU==1 );
         jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
         break;
       }
@@ -482,6 +500,7 @@ static void jsonRenderNode(
     }
     case JSON_REAL:
     case JSON_INT: {
+      assert( pNode->eU==1 );
       jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
       break;
     }
@@ -497,6 +516,7 @@ static void jsonRenderNode(
           j += jsonNodeSize(&pNode[j]);
         }
         if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+        assert( pNode->eU==2 );
         pNode = &pNode[pNode->u.iAppend];
         j = 1;
       }
@@ -517,6 +537,7 @@ static void jsonRenderNode(
           j += 1 + jsonNodeSize(&pNode[j+1]);
         }
         if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+        assert( pNode->eU==2 );
         pNode = &pNode[pNode->u.iAppend];
         j = 1;
       }
@@ -596,7 +617,9 @@ static void jsonReturn(
     }
     case JSON_INT: {
       sqlite3_int64 i = 0;
-      const char *z = pNode->u.zJContent;
+      const char *z;
+      assert( pNode->eU==1 );
+      z = pNode->u.zJContent;
       if( z[0]=='-' ){ z++; }
       while( z[0]>='0' && z[0]<='9' ){
         unsigned v = *(z++) - '0';
@@ -624,9 +647,12 @@ static void jsonReturn(
     case JSON_REAL: {
       double r;
 #ifdef SQLITE_AMALGAMATION
-      const char *z = pNode->u.zJContent;
+      const char *z;
+      assert( pNode->eU==1 );
+      z = pNode->u.zJContent;
       sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
 #else
+      assert( pNode->eU==1 );
       r = strtod(pNode->u.zJContent, 0);
 #endif
       sqlite3_result_double(pCtx, r);
@@ -637,6 +663,7 @@ static void jsonReturn(
       ** json_insert() and json_replace() and those routines do not
       ** call jsonReturn() */
       if( pNode->jnFlags & JNODE_RAW ){
+        assert( pNode->eU==1 );
         sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
                             SQLITE_TRANSIENT);
       }else 
@@ -644,15 +671,18 @@ static void jsonReturn(
       assert( (pNode->jnFlags & JNODE_RAW)==0 );
       if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
         /* JSON formatted without any backslash-escapes */
+        assert( pNode->eU==1 );
         sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
                             SQLITE_TRANSIENT);
       }else{
         /* Translate JSON formatted string into raw text */
         u32 i;
         u32 n = pNode->n;
-        const char *z = pNode->u.zJContent;
+        const char *z;
         char *zOut;
         u32 j;
+        assert( pNode->eU==1 );
+        z = pNode->u.zJContent;
         zOut = sqlite3_malloc( n+1 );
         if( zOut==0 ){
           sqlite3_result_error_nomem(pCtx);
@@ -779,6 +809,7 @@ static int jsonParseAddNode(
   p = &pParse->aNode[pParse->nNode];
   p->eType = (u8)eType;
   p->jnFlags = 0;
+  VVA( p->eU = zContent ? 1 : 0 );
   p->n = n;
   p->u.zJContent = zContent;
   return pParse->nNode++;
@@ -846,6 +877,7 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
     /* Parse array */
     iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
     if( iThis<0 ) return -1;
+    memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u));
     for(j=i+1;;j++){
       while( safe_isspace(z[j]) ){ j++; }
       if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
@@ -1110,6 +1142,7 @@ static JsonParse *jsonParseCached(
 ** a match.
 */
 static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
+  assert( pNode->eU==1 );
   if( pNode->jnFlags & JNODE_RAW ){
     if( pNode->n!=nKey ) return 0;
     return strncmp(pNode->u.zJContent, zKey, nKey)==0;
@@ -1175,6 +1208,7 @@ static JsonNode *jsonLookupStep(
         j += jsonNodeSize(&pRoot[j]);
       }
       if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+      assert( pRoot->eU==2 );
       iRoot += pRoot->u.iAppend;
       pRoot = &pParse->aNode[iRoot];
       j = 1;
@@ -1189,8 +1223,10 @@ static JsonNode *jsonLookupStep(
       if( pParse->oom ) return 0;
       if( pNode ){
         pRoot = &pParse->aNode[iRoot];
+        assert( pRoot->eU==0 );
         pRoot->u.iAppend = iStart - iRoot;
         pRoot->jnFlags |= JNODE_APPEND;
+        VVA( pRoot->eU = 2 );
         pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
       }
       return pNode;
@@ -1213,6 +1249,7 @@ static JsonNode *jsonLookupStep(
             j += jsonNodeSize(&pBase[j]);
           }
           if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
+          assert( pBase->eU==2 );
           iBase += pBase->u.iAppend;
           pBase = &pParse->aNode[iBase];
           j = 1;
@@ -1246,6 +1283,7 @@ static JsonNode *jsonLookupStep(
         j += jsonNodeSize(&pRoot[j]);
       }
       if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+      assert( pRoot->eU==2 );
       iRoot += pRoot->u.iAppend;
       pRoot = &pParse->aNode[iRoot];
       j = 1;
@@ -1261,8 +1299,10 @@ static JsonNode *jsonLookupStep(
       if( pParse->oom ) return 0;
       if( pNode ){
         pRoot = &pParse->aNode[iRoot];
+        assert( pRoot->eU==0 );
         pRoot->u.iAppend = iStart - iRoot;
         pRoot->jnFlags |= JNODE_APPEND;
+        VVA( pRoot->eU = 2 );
       }
       return pNode;
     }
@@ -1416,9 +1456,13 @@ static void jsonParseFunc(
     }
     jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
                i, zType, x.aNode[i].n, x.aUp[i]);
+    assert( x.aNode[i].eU==0 || x.aNode[i].eU==1 );
     if( x.aNode[i].u.zJContent!=0 ){
+      assert( x.aNode[i].eU==1 );
       jsonAppendRaw(&s, " ", 1);
       jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
+    }else{
+      assert( x.aNode[i].eU==0 );
     }
     jsonAppendRaw(&s, "\n", 1);
   }
@@ -1601,6 +1645,7 @@ static JsonNode *jsonMergePatch(
     const char *zKey;
     assert( pPatch[i].eType==JSON_STRING );
     assert( pPatch[i].jnFlags & JNODE_LABEL );
+    assert( pPatch[i].eU==1 );
     nKey = pPatch[i].n;
     zKey = pPatch[i].u.zJContent;
     assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
@@ -1617,6 +1662,9 @@ static JsonNode *jsonMergePatch(
           if( pNew==0 ) return 0;
           pTarget = &pParse->aNode[iTarget];
           if( pNew!=&pTarget[j+1] ){
+            assert( pTarget[j+1].eU==0 || pTarget[j+1].eU==1 );
+            testcase( pTarget[j+1].eU==1 );
+            VVA( pTarget[j+1].eU = 5 );
             pTarget[j+1].u.pPatch = pNew;
             pTarget[j+1].jnFlags |= JNODE_PATCH;
           }
@@ -1632,9 +1680,13 @@ static JsonNode *jsonMergePatch(
       if( pParse->oom ) return 0;
       jsonRemoveAllNulls(pPatch);
       pTarget = &pParse->aNode[iTarget];
+      assert( pParse->aNode[iRoot].eU==0 );
       pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
+      VVA( pParse->aNode[iRoot].eU = 2 );
       pParse->aNode[iRoot].u.iAppend = iStart - iRoot;
       iRoot = iStart;
+      assert( pParse->aNode[iPatch].eU==0 );
+      VVA( pParse->aNode[iPatch].eU = 5 );
       pParse->aNode[iPatch].jnFlags |= JNODE_PATCH;
       pParse->aNode[iPatch].u.pPatch = &pPatch[i+1];
     }
@@ -1776,11 +1828,15 @@ static void jsonReplaceFunc(
     pNode = jsonLookup(&x, zPath, 0, ctx);
     if( x.nErr ) goto replace_err;
     if( pNode ){
+      assert( pNode->eU==0 || pNode->eU==1 || pNode->eU==4 );
+      json_testcase( pNode->eU!=0 && pNode->eU!=1 );
       pNode->jnFlags |= (u8)JNODE_REPLACE;
+      VVA( pNode->eU =  4 );
       pNode->u.iReplace = i + 1;
     }
   }
   if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+    assert( x.aNode[0].eU==4 );
     sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
   }else{
     jsonReturnJson(x.aNode, ctx, argv);
@@ -1830,11 +1886,15 @@ static void jsonSetFunc(
     }else if( x.nErr ){
       goto jsonSetDone;
     }else if( pNode && (bApnd || bIsSet) ){
+      json_testcase( pNode->eU!=0 && pNode->eU!=1 && pNode->eU!=4 );
+      assert( pNode->eU!=3 || pNode->eU!=5 );
+      VVA( pNode->eU = 4 );
       pNode->jnFlags |= (u8)JNODE_REPLACE;
       pNode->u.iReplace = i + 1;
     }
   }
   if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+    assert( x.aNode[0].eU==4 );
     sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
   }else{
     jsonReturnJson(x.aNode, ctx, argv);
@@ -2185,6 +2245,9 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){
       JsonNode *pUp = &p->sParse.aNode[iUp];
       p->eType = pUp->eType;
       if( pUp->eType==JSON_ARRAY ){
+        assert( pUp->eU==0 || pUp->eU==3 );
+        json_testcase( pUp->eU==3 );
+        VVA( pUp->eU = 3 );
         if( iUp==p->i-1 ){
           pUp->u.iKey = 0;
         }else{
@@ -2231,12 +2294,15 @@ static void jsonEachComputePath(
   pNode = &p->sParse.aNode[i];
   pUp = &p->sParse.aNode[iUp];
   if( pUp->eType==JSON_ARRAY ){
+    assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) );
+    testcase( pUp->eU==0 );
     jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
   }else{
     assert( pUp->eType==JSON_OBJECT );
     if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
     assert( pNode->eType==JSON_STRING );
     assert( pNode->jnFlags & JNODE_LABEL );
+    assert( pNode->eU==1 );
     jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
   }
 }
@@ -2258,6 +2324,7 @@ static int jsonEachColumn(
         u32 iKey;
         if( p->bRecursive ){
           if( p->iRowid==0 ) break;
+          assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 );
           iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
         }else{
           iKey = p->iRowid;
@@ -2307,6 +2374,7 @@ static int jsonEachColumn(
         if( p->eType==JSON_ARRAY ){
           jsonPrintf(30, &x, "[%d]", p->iRowid);
         }else if( p->eType==JSON_OBJECT ){
+          assert( pThis->eU==1 );
           jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
         }
       }
@@ -2374,6 +2442,7 @@ static int jsonEachBestIndex(
     if( pConstraint->iColumn < JEACH_JSON ) continue;
     iCol = pConstraint->iColumn - JEACH_JSON;
     assert( iCol==0 || iCol==1 );
+    testcase( iCol==0 );
     iMask = 1 << iCol;
     if( pConstraint->usable==0 ){
       unusableMask |= iMask;
@@ -2471,6 +2540,8 @@ static int jsonEachFilter(
     p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
     p->eType = pNode->eType;
     if( p->eType>=JSON_ARRAY ){
+      assert( pNode->eU==0 );
+      VVA( pNode->eU = 3 );
       pNode->u.iKey = 0;
       p->iEnd = p->i + pNode->n + 1;
       if( p->bRecursive ){
index 99053eb5a55fc4c1e6bf36879b68ae845c925975..b126365f502e379758c097c7cf8cc57bba7ae314 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\ssigned\sinteger\soverflow\sin\sfts5\sleading\sto\sa\ssegfault\sthat\scould\soccur\swhen\sprocessing\scorrupt\srecords.
-D 2021-10-14T21:13:02.676
+C Protect\sthe\sJsonNode.u\sunion\susing\snearby\sassert()s\sand\sbranches.
+D 2021-10-15T16:15:04.901
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -306,7 +306,7 @@ F ext/misc/fileio.c 57fefd0efc535e62bb8b07fa146875171481da81a759bbfbe2fc91bab900
 F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5
 F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d
 F ext/misc/ieee754.c 91a5594071143a4ab79c638fe9f059af1db09932faf2e704c3e29216a7d4f511
-F ext/misc/json1.c 1a278d675235c1ccf096e8b83e1999aabbd2941edf96c9cfe00bd58386596aa0
+F ext/misc/json1.c 9344f0c08fe74119022466384557c849f21fb4a15e8314f70847b1831c500f29
 F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d
 F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b
 F ext/misc/memvfs.c 7dffa8cc89c7f2d73da4bd4ccea1bcbd2bd283e3bb4cea398df7c372a197291b
@@ -1929,7 +1929,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P bdd840216cc4c5293c112f182a189f7562b3cc0f6270e3c4af5eb2e8bd61ccc9
-R 939d0f43dd70818ca1c9a853dcd409e7
-U dan
-Z 5e299d1974a85528c5adc593776bc4dd
+P 69a3ff0cc159cdf84a5367eaf708882ddeda4fa65c96a5b546ae4a0114f02cb7
+R ede285ffb2c5c992e5f7bd66e443ce38
+U drh
+Z 01fdfdb7db3b89a4154d6f89726d1593
index 2c492716bdfdb80e7f8376c6d9aa4c8377ca2853..f8707c5c16a22274d04b75be2ac624c2c3e07b7f 100644 (file)
@@ -1 +1 @@
-69a3ff0cc159cdf84a5367eaf708882ddeda4fa65c96a5b546ae4a0114f02cb7
\ No newline at end of file
+7b8ea2298927fd34f27b3345add3ce751ed728387fe3d9207b601ba6449d5af9
\ No newline at end of file