Protect the JsonNode.u union using nearby assert()s and branches.

FossilOrigin-Name: 7b8ea2298927fd34f27b3345add3ce751ed728387fe3d9207b601ba6449d5af9
This commit is contained in:
drh 2021-10-15 16:15:04 +00:00
parent 33cf194218
commit 285f2ef4ad
3 changed files with 87 additions and 16 deletions

View 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 ){

View File

@ -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

View File

@ -1 +1 @@
69a3ff0cc159cdf84a5367eaf708882ddeda4fa65c96a5b546ae4a0114f02cb7
7b8ea2298927fd34f27b3345add3ce751ed728387fe3d9207b601ba6449d5af9