From: drh Date: Sat, 29 Aug 2015 00:54:49 +0000 (+0000) Subject: Change the json1.c module so that it throws an error if any of the X-Git-Tag: version-3.9.0~177 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a771402e558b3adcb201a84cbefe1ed5b7bd8076;p=thirdparty%2Fsqlite.git Change the json1.c module so that it throws an error if any of the JSON selector paths are malformed. FossilOrigin-Name: 3aa0855fd463076fc3277f1d9fe00d2f30e6b449 --- diff --git a/ext/misc/json1.c b/ext/misc/json1.c index a8b9ed539d..b4506c773f 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -108,6 +108,7 @@ struct JsonParse { 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 */ }; /************************************************************************** @@ -786,7 +787,7 @@ static int jsonParseFindParents(JsonParse *pParse){ } /* forward declaration */ -static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*); +static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); /* ** Search along zPath to find the node specified. Return a pointer @@ -797,11 +798,12 @@ static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*); ** possible to do so and if no existing node corresponds to zPath. If ** new nodes are appended *pApnd is set to 1. */ -static JsonNode *jsonLookup( +static JsonNode *jsonLookupStep( JsonParse *pParse, /* The JSON to search */ u32 iRoot, /* Begin the search at this node */ const char *zPath, /* The path to search */ - int *pApnd /* Append nodes to complete path if not NULL */ + int *pApnd, /* Append nodes to complete path if not NULL */ + const char **pzErr /* Make *pzErr point to any syntax error in zPath */ ){ u32 i, j, nKey; const char *zKey; @@ -820,14 +822,17 @@ static JsonNode *jsonLookup( for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} nKey = i; } - if( nKey==0 ) return 0; + if( nKey==0 ){ + *pzErr = zPath; + return 0; + } j = 1; for(;;){ while( j<=pRoot->n ){ if( pRoot[j].n==nKey+2 && strncmp(&pRoot[j].u.zJContent[1],zKey,nKey)==0 ){ - return jsonLookup(pParse, iRoot+j+1, &zPath[i], pApnd); + return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr); } j++; j += jsonNodeSize(&pRoot[j]); @@ -843,7 +848,7 @@ static JsonNode *jsonLookup( iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath); zPath += i; - pNode = jsonLookupAppend(pParse, zPath, pApnd); + pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; @@ -861,7 +866,10 @@ static JsonNode *jsonLookup( i = i*10 + zPath[0] - '0'; zPath++; } - if( zPath[0]!=']' ) return 0; + if( zPath[0]!=']' ){ + *pzErr = zPath; + return 0; + } zPath++; j = 1; for(;;){ @@ -875,13 +883,13 @@ static JsonNode *jsonLookup( j = 1; } if( j<=pRoot->n ){ - return jsonLookup(pParse, iRoot+j, zPath, pApnd); + return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr); } if( i==0 && pApnd ){ u32 iStart; JsonNode *pNode; iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); - pNode = jsonLookupAppend(pParse, zPath, pApnd); + pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); if( pParse->oom ) return 0; if( pNode ){ pRoot = &pParse->aNode[iRoot]; @@ -890,6 +898,8 @@ static JsonNode *jsonLookup( } return pNode; } + }else if( zPath[0]!=0 ){ + *pzErr = zPath; } return 0; } @@ -901,7 +911,8 @@ static JsonNode *jsonLookup( static JsonNode *jsonLookupAppend( JsonParse *pParse, /* Append content to the JSON parse */ const char *zPath, /* Description of content to append */ - int *pApnd /* Set this flag to 1 */ + int *pApnd, /* Set this flag to 1 */ + const char **pzErr /* Make this point to any syntax error */ ){ *pApnd = 1; if( zPath[0]==0 ){ @@ -916,9 +927,76 @@ static JsonNode *jsonLookupAppend( return 0; } if( pParse->oom ) return 0; - return jsonLookup(pParse, pParse->nNode-1, zPath, pApnd); + return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr); +} + +/* +** Return the text of a syntax error message on a JSON path. Space is +** obtained from sqlite3_malloc(). +*/ +static char *jsonPathSyntaxError(const char *zErr){ + return sqlite3_mprintf("JSON path error near '%q'", zErr); } +/* +** Do a node lookup using zPath. Return a pointer to the node on success. +** Return NULL if not found or if there is an error. +** +** On an error, write an error message into pCtx and increment the +** pParse->nErr counter. +** +** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if +** nodes are appended. +** +** If the path starts with $$ then set *pFlags to JNODE_REPLACE|JNODE_JSON +** as a single to the caller that the input text to be inserted should be +** interpreted as JSON rather than as ordinary text. +*/ +static JsonNode *jsonLookup( + JsonParse *pParse, /* The JSON to search */ + const char *zPath, /* The path to search */ + int *pApnd, /* Append nodes to complete path if not NULL */ + sqlite3_context *pCtx, /* Report errors here, if not NULL */ + u8 *pFlags /* Write JNODE_REPLACE or _REPLACE|_JSON here */ +){ + const char *zErr = 0; + JsonNode *pNode = 0; + u8 fg = JNODE_REPLACE; + + if( zPath==0 ) return 0; + if( zPath[0]!='$' ){ + zErr = zPath; + goto lookup_err; + } + zPath++; + if( zPath[0]=='$' ){ + if( pFlags==0 ){ + zErr = zPath; + goto lookup_err; + } + zPath++; + fg = JNODE_REPLACE|JNODE_JSON; + } + if( pFlags ) *pFlags = fg; + pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr); + return pNode; + +lookup_err: + pParse->nErr++; + if( zErr!=0 && pCtx!=0 ){ + char *z = jsonPathSyntaxError(zErr); + if( z ){ + sqlite3_result_error(pCtx, z, -1); + sqlite3_free(z); + }else{ + sqlite3_result_error_nomem(pCtx); + } + } + if( pFlags ) *pFlags = fg; + return 0; +} + + /* ** Report the wrong number of arguments for json_insert(), json_replace() ** or json_set(). @@ -933,6 +1011,7 @@ static void jsonWrongNumArgs( sqlite3_free(zMsg); } + /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ @@ -1042,29 +1121,27 @@ static void jsonArrayLengthFunc( JsonParse x; /* The parse */ sqlite3_int64 n = 0; u32 i; - const char *zPath; - if( argc==2 ){ - zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath==0 ) return; - if( zPath[0]!='$' ) return; - zPath++; - }else{ - zPath = 0; - } if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; if( x.nNode ){ - JsonNode *pNode = x.aNode; - if( zPath ) pNode = jsonLookup(&x, 0, zPath, 0); - if( pNode->eType==JSON_ARRAY ){ + JsonNode *pNode; + if( argc==2 ){ + const char *zPath = (const char*)sqlite3_value_text(argv[1]); + pNode = jsonLookup(&x, zPath, 0, ctx, 0); + }else{ + pNode = x.aNode; + } + if( pNode==0 ){ + x.nErr = 1; + }else if( pNode->eType==JSON_ARRAY ){ assert( (pNode->jnFlags & JNODE_APPEND)==0 ); for(i=1; i<=pNode->n; n++){ i += jsonNodeSize(&pNode[i]); } } } + if( x.nErr==0 ) sqlite3_result_int64(ctx, n); jsonParseReset(&x); - sqlite3_result_int64(ctx, n); } /* @@ -1083,11 +1160,8 @@ static void jsonExtractFunc( const char *zPath; assert( argc==2 ); zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath==0 ) return; - if( zPath[0]!='$' ) return; - zPath++; if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; - pNode = jsonLookup(&x, 0, zPath, 0); + pNode = jsonLookup(&x, zPath, 0, ctx, 0); if( pNode ){ jsonReturn(pNode, ctx, 0); } @@ -1156,15 +1230,16 @@ static void jsonRemoveFunc( if( x.nNode ){ for(i=1; i<(u32)argc; i++){ zPath = (const char*)sqlite3_value_text(argv[i]); - if( zPath==0 ) continue; - if( zPath[0]!='$' ) continue; - pNode = jsonLookup(&x, 0, &zPath[1], 0); + if( zPath==0 ) goto remove_done; + pNode = jsonLookup(&x, zPath, 0, ctx, 0); + if( x.nErr ) goto remove_done; if( pNode ) pNode->jnFlags |= JNODE_REMOVE; } if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){ jsonReturnJson(x.aNode, ctx, 0); } } +remove_done: jsonParseReset(&x); } @@ -1194,13 +1269,8 @@ static void jsonReplaceFunc( for(i=1; i<(u32)argc; i+=2){ u8 jnFlags = JNODE_REPLACE; zPath = (const char*)sqlite3_value_text(argv[i]); - if( zPath==0 ) continue; - if( zPath[0]!='$' ) continue; - if( zPath[1]=='$' ){ - zPath++; - jnFlags = JNODE_REPLACE|JNODE_JSON; - } - pNode = jsonLookup(&x, 0, &zPath[1], 0); + pNode = jsonLookup(&x, zPath, 0, ctx, &jnFlags); + if( x.nErr ) goto replace_err; if( pNode ){ pNode->jnFlags &= ~JNODE_JSON; pNode->jnFlags |= jnFlags; @@ -1213,6 +1283,7 @@ static void jsonReplaceFunc( jsonReturnJson(x.aNode, ctx, argv); } } +replace_err: jsonParseReset(&x); } @@ -1250,17 +1321,13 @@ static void jsonSetFunc( for(i=1; i<(u32)argc; i+=2){ u8 jnFlags = JNODE_REPLACE; zPath = (const char*)sqlite3_value_text(argv[i]); - if( zPath==0 ) continue; - if( zPath[0]!='$' ) continue; - if( zPath[1]=='$' ){ - zPath++; - jnFlags = JNODE_REPLACE|JNODE_JSON; - } bApnd = 0; - pNode = jsonLookup(&x, 0, &zPath[1], &bApnd); + pNode = jsonLookup(&x, zPath, &bApnd, ctx, &jnFlags); if( x.oom ){ sqlite3_result_error_nomem(ctx); goto jsonSetDone; + }else if( x.nErr ){ + goto jsonSetDone; }else if( pNode && (bApnd || bIsSet) ){ pNode->jnFlags &= ~JNODE_JSON; pNode->jnFlags |= jnFlags; @@ -1292,18 +1359,15 @@ static void jsonTypeFunc( JsonParse x; /* The parse */ const char *zPath; - if( argc==2 ){ - zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath==0 ) return; - if( zPath[0]!='$' ) return; - zPath++; - }else{ - zPath = 0; - } if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; if( x.nNode ){ - JsonNode *pNode = x.aNode; - if( zPath ) pNode = jsonLookup(&x, 0, zPath, 0); + JsonNode *pNode; + if( argc==2 ){ + zPath = (const char*)sqlite3_value_text(argv[1]); + pNode = jsonLookup(&x, zPath, 0, ctx, 0); + }else{ + pNode = x.aNode; + } if( pNode ){ sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC); } @@ -1680,27 +1744,45 @@ static int jsonEachFilter( if( z==0 ) return SQLITE_OK; if( idxNum&2 ){ zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath==0 || zPath[0]!='$' ) return SQLITE_OK; + if( zPath==0 ) return SQLITE_OK; + if( zPath[0]!='$' ){ + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = jsonPathSyntaxError(zPath); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + } } n = sqlite3_value_bytes(argv[0]); p->zJson = sqlite3_malloc64( n+1 ); if( p->zJson==0 ) return SQLITE_NOMEM; memcpy(p->zJson, z, (size_t)n+1); - if( jsonParse(&p->sParse, 0, p->zJson) - || (p->bRecursive && jsonParseFindParents(&p->sParse)) - ){ + if( jsonParse(&p->sParse, 0, p->zJson) ){ + int rc = SQLITE_NOMEM; + if( p->sParse.oom==0 ){ + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); + if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR; + } + jsonEachCursorReset(p); + return rc; + }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){ jsonEachCursorReset(p); + return SQLITE_NOMEM; }else{ JsonNode *pNode; if( idxNum==3 ){ + const char *zErr = 0; p->bRecursive = 0; n = sqlite3_value_bytes(argv[1]); p->zPath = sqlite3_malloc64( n+1 ); if( p->zPath==0 ) return SQLITE_NOMEM; memcpy(p->zPath, zPath, (size_t)n+1); - pNode = jsonLookup(&p->sParse, 0, p->zPath+1, 0); - if( pNode==0 ){ + pNode = jsonLookupStep(&p->sParse, 0, p->zPath+1, 0, &zErr); + if( p->sParse.nErr ){ + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr); jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + }else if( pNode==0 ){ return SQLITE_OK; } }else{ diff --git a/manifest b/manifest index 630423411b..dda3e678a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Back\sout\sthe\sjson_check()\sroutine.\s\sInstead,\sthrow\san\serror\sif\sthe\sinput\sto\na\sjson\sfunction\s(other\sthan\sjson_valid())\sis\snot\svalid\sJSON. -D 2015-08-28T20:07:40.320 +C Change\sthe\sjson1.c\smodule\sso\sthat\sit\sthrows\san\serror\sif\sany\sof\sthe\nJSON\sselector\spaths\sare\smalformed. +D 2015-08-29T00:54:49.924 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e2218eb228374422969de7b1680eda6864affcef F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -192,7 +192,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767 F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e -F ext/misc/json1.c aa344845c2c211e8a7fd57ccd92901506dacdf5a +F ext/misc/json1.c 063bf62fd44a06aa06fd22854fff09679cbb855f F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc @@ -1380,7 +1380,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 0fdc36fe35ae2fc8e9688fe6c53437f4d47502d9 -R 72483a04f1004ed82fe324bdb4440aeb +P dc9ce7b18cbe23d065317757234ef9fb8792da7a +R 7515083280f6e706dffbefc132c1a59a U drh -Z 66ee5efb60acb350500ed2a1605628ef +Z f18bb212545a9a98b8ef3bc5262369aa diff --git a/manifest.uuid b/manifest.uuid index b5b08b89f4..19c1a06207 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dc9ce7b18cbe23d065317757234ef9fb8792da7a \ No newline at end of file +3aa0855fd463076fc3277f1d9fe00d2f30e6b449 \ No newline at end of file