See previous similar bugs for more details.
op_pushscope is marked as not throwing in opcodes.tbl an exception, yet looking at the verifier:
opcode.tbl:
// opCount throw stack internal name hex
ABC_OP( 0, 0, -1, 0, pushscope) // 0x30
Verifier.cpp:
case OP_pushscope:
{
checkStack(1,0);
if (state->scopeDepth + 1 > ms->max_scope())
verifyFailed(kScopeStackOverflowError);
Traits* scopeTraits = state->peek().traits;
const ScopeTypeChain* scope = info->declaringScope();
if (scope->fullsize > (scope->size+state->scopeDepth))
{
// extra constraints on type of pushscope allowed
Traits* requiredType = scope->getScopeTraitsAt(scope->size+state->scopeDepth);
if (!scopeTraits || !scopeTraits->subtypeof(requiredType))
{
verifyFailed(kIllegalOperandTypeError, core->toErrorString(scopeTraits), core->toErrorString(requiredType));
}
}
emitCheckNull(sp); <-- this will emit code which will throw a catchable exception
coder->writeOp1(state, pc, opcode, ms->scope_base() + state->scopeDepth);
state->pop();
state->setType(ms->scope_base() + state->scopeDepth, scopeTraits, true, false);
state->scopeDepth++;
break;
}
Invoking pushscope with a NULL atom argument will throw an exception at runtime and invoke a control-flow path not seen by the verifier which can be abused to achieve code execution.
Attached PoC for avmshell.