diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/vdbe.h | 2 | ||||
-rw-r--r-- | src/vdbeaux.c | 92 | ||||
-rw-r--r-- | src/where.c | 1 | ||||
-rw-r--r-- | src/whereInt.h | 1 | ||||
-rw-r--r-- | src/wherecode.c | 2 |
5 files changed, 98 insertions, 0 deletions
diff --git a/src/vdbe.h b/src/vdbe.h index e251dd666..5909d3995 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -198,8 +198,10 @@ void sqlite3VdbeEndCoroutine(Vdbe*,int); #endif #if defined(SQLITE_DEBUG) void sqlite3VdbeVerifyAbortable(Vdbe *p, int); + void sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int); #else # define sqlite3VdbeVerifyAbortable(A,B) +# define sqlite3VdbeNoJumpsOutsideSubrtn(A,B,C,D) #endif VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); #ifndef SQLITE_OMIT_EXPLAIN diff --git a/src/vdbeaux.c b/src/vdbeaux.c index eaf5780ab..3569fa994 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -880,6 +880,98 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) ); } +#ifdef SQLITE_DEBUG +/* +** Check to see if a subroutine contains a jump to a location outside of +** the subroutine. If a jump outside the subroutine is detected, add code +** that will cause the program to halt with an error message. +** +** The subroutine consists of opcodes between iFirst and iLast. Jumps to +** locations within the subroutine are acceptable. iRetReg is a register +** that contains the return address. Jumps to outside the range of iFirst +** through iLast are also acceptable as long as the jump destination is +** an OP_Return to iReturnAddr. +** +** A jump to an unresolved label is considered to be a jump outside of the +** subroutine. +** +** This routine only runs during debug builds. The purpose is (of course) +** to detect invalid escapes out of a subroutine. The OP_Halt opcode +** is generated rather than an assert() or other error, so that ".eqp full" +** will still work to show the original bytecode, to aid in debugging. +*/ +void sqlite3VdbeNoJumpsOutsideSubrtn( + Vdbe *v, /* The byte-code program under construction */ + int iFirst, /* First opcode of the subroutine */ + int iLast, /* Last opcode of the subroutine */ + int iRetReg /* Subroutine return address register */ +){ + VdbeOp *pOp; + Parse *pParse; + int i; + sqlite3_str *pErr = 0; + assert( v!=0 ); + pParse = v->pParse; + assert( pParse!=0 ); + if( pParse->nErr ) return; + assert( iLast>=iFirst ); + assert( iLast<v->nOp ); + pOp = &v->aOp[iFirst]; + for(i=iFirst; i<=iLast; i++, pOp++){ + if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ){ + int iDest = pOp->p2; /* Jump destination */ + if( iDest==0 ) continue; + if( iDest<0 ){ + int j = ADDR(iDest); + assert( j>=0 ); + if( j>=-pParse->nLabel || pParse->aLabel[j]<0 ){ + if( pErr==0 ){ + pErr = sqlite3_str_new(0); + }else{ + sqlite3_str_appendchar(pErr, 1, '\n'); + } + sqlite3_str_appendf(pErr, + "Opcode at %d within the " + "subroutine at %d..%d jumps to an unresolved " + "address (%d)\n", + i, iFirst, iLast, iDest); + continue; + } + iDest = pParse->aLabel[j]; + } + if( iDest<iFirst || iDest>iLast ){ + int j = iDest; + for(; j<v->nOp; j++){ + VdbeOp *pX = &v->aOp[j]; + if( pX->opcode==OP_Return ){ + if( pX->p1==iRetReg ) break; + continue; + } + if( pX->opcode==OP_Noop ) continue; + if( pX->opcode==OP_Explain ) continue; + if( pErr==0 ){ + pErr = sqlite3_str_new(0); + }else{ + sqlite3_str_appendchar(pErr, 1, '\n'); + } + sqlite3_str_appendf(pErr, + "Opcode at %d jumps to %d which is outside the " + "subroutine at %d..%d", + i, iDest, iFirst, iLast); + break; + } + } + } + } + if( pErr ){ + char *zErr = sqlite3_str_finish(pErr); + sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_INTERNAL, OE_Abort, 0, zErr, 0); + sqlite3_free(zErr); + sqlite3MayAbort(pParse); + } +} +#endif /* SQLITE_DEBUG */ + /* ** Return the address of the next instruction to be inserted. */ diff --git a/src/where.c b/src/where.c index 8526f7c5a..a72441932 100644 --- a/src/where.c +++ b/src/where.c @@ -6024,6 +6024,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ WhereRightJoin *pRJ = pLevel->pRJ; sqlite3VdbeResolveLabel(v, pLevel->addrCont); pLevel->addrCont = 0; + pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1); VdbeCoverage(v); assert( pParse->withinRJSubrtn>0 ); diff --git a/src/whereInt.h b/src/whereInt.h index acc9ec3dd..93ab937c8 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -52,6 +52,7 @@ struct WhereRightJoin { int regBloom; /* Bloom filter for iRJMatch */ int regReturn; /* Return register for the interior subroutine */ int addrSubrtn; /* Starting address for the interior subroutine */ + int endSubrtn; /* The last opcode in the interior subroutine */ }; /* diff --git a/src/wherecode.c b/src/wherecode.c index 4f525a8fb..3ea6c5d8e 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -2819,6 +2819,8 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( int k; ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName)); + sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, + pRJ->regReturn); for(k=0; k<iLevel; k++){ int iIdxCur; mAll |= pWInfo->a[k].pWLoop->maskSelf; |