From cbe233a2fa3ad9aebc0248df853f7b2d40d6bf94 Mon Sep 17 00:00:00 2001 From: Rafal Kupiec Date: Thu, 29 Oct 2026 23:06:08 +0100 Subject: [PATCH] Reject local shadowing across auxiliary VM frames --- engine/vm.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/engine/vm.c b/engine/vm.c index 595ac06..cfe837f 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -55,6 +55,19 @@ struct SyhttpHeader { #define HTTP_PROTO_11 2 /* HTTP/1.1 */ #define HTTP_PROTO_20 3 /* HTTP/2.0 */ static void VmScopeRecord(ph7_vm *pVm, const SyString *pName, sxi32 bConst); +/* Lookup visible local names across auxiliary frames, never caller invocations. + * Scope exit removes names from these tables without releasing retained storage. */ +static SyHashEntry *VmLookupLocal(VmFrame *pFrame, const SyString *pName, sxi32 bConst) { + for(;;) { + SyHashEntry *pEntry = SyHashGet(bConst ? &pFrame->hConst : &pFrame->hVar, + pName->zString, pName->nByte); + if(pEntry || pFrame->pParent == 0 || + (pFrame->iFlags & (VM_FRAME_LOOP | VM_FRAME_EXCEPTION | VM_FRAME_CATCH | VM_FRAME_FINALLY)) == 0) { + return pEntry; + } + pFrame = pFrame->pParent; + } +} /* * Register a constant and it's associated expansion callback so that * it can be expanded from the target PHP program. @@ -88,6 +101,9 @@ PH7_PRIVATE sxi32 PH7_VmRegisterConstant( pFrame = pFrame->pParent; } pCollection = &pFrame->hConst; + if(VmLookupLocal(pFrame, pName, TRUE)) { + return SXERR_EXISTS; + } } pEntry = SyHashGet(pCollection, (const void *)pName->zString, pName->nByte); if(pEntry) { @@ -2262,8 +2278,8 @@ static ph7_value *VmCreateMemObj( while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { pFrame = pFrame->pParent; } - /* Query the top active frame */ - pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte); + /* Auxiliary frames do not permit shadowing a visible local. */ + pEntry = VmLookupLocal(pFrame, pName, FALSE); if(pEntry) { /* Variable already exists */ return 0; @@ -3293,7 +3309,7 @@ Resume: while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { pFrame = pFrame->pParent; } - if(SyHashGet(&pFrame->hVar, pStatic->sName.zString, pStatic->sName.nByte)) { + if(VmLookupLocal(pFrame, &pStatic->sName, FALSE)) { PH7_VmThrowError(pVm, PH7_CTX_ERR, "Redeclaration of ā€˜$%z’ variable", &pStatic->sName); } if(pStatic->nIdx == SXU32_HIGH) { @@ -3440,7 +3456,7 @@ Resume: /* Candidate for expansion via user defined callbacks */ for(;;) { pEntry = SyHashGet(&pFrame->hConst, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - if(pEntry == 0 && pFrame->iFlags & VM_FRAME_LOOP && pFrame->pParent) { + if(pEntry == 0 && pFrame->iFlags & (VM_FRAME_LOOP | VM_FRAME_EXCEPTION | VM_FRAME_CATCH | VM_FRAME_FINALLY) && pFrame->pParent) { pFrame = pFrame->pParent; } else { break; @@ -9335,13 +9351,15 @@ static sxi32 VmExecExceptionBlock(ph7_vm *pVm, ph7_exception *pException, rc = VmEnterFrame(pVm, 0, 0, &pFrame); if(rc == SXRET_OK) { if(pCatch) { - ph7_value *pObj = VmCreateMemObj(pVm, &pCatch->sThis, FALSE); + ph7_value *pObj; pFrame->iFlags |= VM_FRAME_CATCH; - if(pObj) { - pThis->iRef++; - pObj->x.pOther = pThis; - MemObjSetType(pObj, MEMOBJ_OBJ); + pObj = VmCreateMemObj(pVm, &pCatch->sThis, FALSE); + if(pObj == 0) { + PH7_VmThrowError(pVm, PH7_CTX_ERR, "Redeclaration of ā€˜$%z’ variable", &pCatch->sThis); } + pThis->iRef++; + pObj->x.pOther = pThis; + MemObjSetType(pObj, MEMOBJ_OBJ); } else { pFrame->iFlags |= VM_FRAME_FINALLY; }