VM: endless loop prevention
This commit is contained in:
parent
e855f6c073
commit
ee1192679e
4 changed files with 33 additions and 1213 deletions
|
|
@ -68,7 +68,9 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
|
||||||
msg: TMsgKind, arg = "") =
|
msg: TMsgKind, arg = "") =
|
||||||
msgWriteln("stack trace: (most recent call last)")
|
msgWriteln("stack trace: (most recent call last)")
|
||||||
stackTraceAux(c, tos, pc)
|
stackTraceAux(c, tos, pc)
|
||||||
localError(c.debug[pc], msg, arg)
|
# XXX test if we want 'globalError' for every mode
|
||||||
|
if c.mode == emRepl: globalError(c.debug[pc], msg, arg)
|
||||||
|
else: localError(c.debug[pc], msg, arg)
|
||||||
|
|
||||||
proc bailOut(c: PCtx; tos: PStackFrame) =
|
proc bailOut(c: PCtx; tos: PStackFrame) =
|
||||||
stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
|
stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
|
||||||
|
|
@ -333,6 +335,16 @@ proc compile(c: PCtx, s: PSym): int =
|
||||||
when debugEchoCode: c.echoCode result
|
when debugEchoCode: c.echoCode result
|
||||||
#c.echoCode
|
#c.echoCode
|
||||||
|
|
||||||
|
template handleJmpBack() {.dirty.} =
|
||||||
|
if c.loopIterations <= 0:
|
||||||
|
if allowInfiniteLoops in c.features:
|
||||||
|
c.loopIterations = MaxLoopIterations
|
||||||
|
else:
|
||||||
|
msgWriteln("stack trace: (most recent call last)")
|
||||||
|
stackTraceAux(c, tos, pc)
|
||||||
|
globalError(c.debug[pc], errTooManyIterations)
|
||||||
|
dec(c.loopIterations)
|
||||||
|
|
||||||
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
var pc = start
|
var pc = start
|
||||||
var tos = tos
|
var tos = tos
|
||||||
|
|
@ -735,6 +747,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
|
globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
|
||||||
elif prc.kind != skTemplate:
|
elif prc.kind != skTemplate:
|
||||||
let newPc = compile(c, prc)
|
let newPc = compile(c, prc)
|
||||||
|
# tricky: a recursion is also a jump back, so we use the same
|
||||||
|
# logic as for loops:
|
||||||
|
if newPc < pc: handleJmpBack()
|
||||||
#echo "new pc ", newPc, " calling: ", prc.name.s
|
#echo "new pc ", newPc, " calling: ", prc.name.s
|
||||||
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
|
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
|
||||||
newSeq(newFrame.slots, prc.offset)
|
newSeq(newFrame.slots, prc.offset)
|
||||||
|
|
@ -778,6 +793,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
# jump Bx
|
# jump Bx
|
||||||
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
|
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
|
||||||
inc pc, rbx
|
inc pc, rbx
|
||||||
|
of opcJmpBack:
|
||||||
|
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
|
||||||
|
inc pc, rbx
|
||||||
|
handleJmpBack()
|
||||||
of opcBranch:
|
of opcBranch:
|
||||||
# we know the next instruction is a 'fjmp':
|
# we know the next instruction is a 'fjmp':
|
||||||
let branch = c.constants[instr.regBx-wordExcess]
|
let branch = c.constants[instr.regBx-wordExcess]
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ const
|
||||||
byteExcess* = 128 # we use excess-K for immediates
|
byteExcess* = 128 # we use excess-K for immediates
|
||||||
wordExcess* = 32768
|
wordExcess* = 32768
|
||||||
|
|
||||||
|
MaxLoopIterations* = 500_000 # max iterations of all loops
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TRegister* = range[0..255]
|
TRegister* = range[0..255]
|
||||||
TDest* = range[-1 .. 255]
|
TDest* = range[-1 .. 255]
|
||||||
|
|
@ -110,6 +113,7 @@ type
|
||||||
opcTJmp, # jump Bx if A != 0
|
opcTJmp, # jump Bx if A != 0
|
||||||
opcFJmp, # jump Bx if A == 0
|
opcFJmp, # jump Bx if A == 0
|
||||||
opcJmp, # jump Bx
|
opcJmp, # jump Bx
|
||||||
|
opcJmpBack, # jump Bx; resulting from a while loop
|
||||||
opcBranch, # branch for 'case'
|
opcBranch, # branch for 'case'
|
||||||
opcTry,
|
opcTry,
|
||||||
opcExcept,
|
opcExcept,
|
||||||
|
|
@ -182,6 +186,7 @@ type
|
||||||
mode*: TEvalMode
|
mode*: TEvalMode
|
||||||
features*: TSandboxFlags
|
features*: TSandboxFlags
|
||||||
traceActive*: bool
|
traceActive*: bool
|
||||||
|
loopIterations*: int
|
||||||
|
|
||||||
TPosition* = distinct int
|
TPosition* = distinct int
|
||||||
|
|
||||||
|
|
@ -190,7 +195,7 @@ type
|
||||||
proc newCtx*(module: PSym): PCtx =
|
proc newCtx*(module: PSym): PCtx =
|
||||||
PCtx(code: @[], debug: @[],
|
PCtx(code: @[], debug: @[],
|
||||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||||
prc: PProc(blocks: @[]), module: module)
|
prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations)
|
||||||
|
|
||||||
proc refresh*(c: PCtx, module: PSym) =
|
proc refresh*(c: PCtx, module: PSym) =
|
||||||
c.module = module
|
c.module = module
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,10 @@ proc genLabel(c: PCtx): TPosition =
|
||||||
result = TPosition(c.code.len)
|
result = TPosition(c.code.len)
|
||||||
#c.jumpTargets.incl(c.code.len)
|
#c.jumpTargets.incl(c.code.len)
|
||||||
|
|
||||||
proc jmpBack(c: PCtx, n: PNode, opc: TOpcode, p = TPosition(0)) =
|
proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) =
|
||||||
let dist = p.int - c.code.len
|
let dist = p.int - c.code.len
|
||||||
internalAssert(-0x7fff < dist and dist < 0x7fff)
|
internalAssert(-0x7fff < dist and dist < 0x7fff)
|
||||||
gABx(c, n, opc, 0, dist)
|
gABx(c, n, opcJmpBack, 0, dist)
|
||||||
|
|
||||||
proc patch(c: PCtx, p: TPosition) =
|
proc patch(c: PCtx, p: TPosition) =
|
||||||
# patch with current index
|
# patch with current index
|
||||||
|
|
@ -229,20 +229,20 @@ proc genWhile(c: PCtx; n: PNode) =
|
||||||
withBlock(nil):
|
withBlock(nil):
|
||||||
if isTrue(n.sons[0]):
|
if isTrue(n.sons[0]):
|
||||||
c.gen(n.sons[1])
|
c.gen(n.sons[1])
|
||||||
c.jmpBack(n, opcJmp, L1)
|
c.jmpBack(n, L1)
|
||||||
elif isNotOpr(n.sons[0]):
|
elif isNotOpr(n.sons[0]):
|
||||||
var tmp = c.genx(n.sons[0].sons[1])
|
var tmp = c.genx(n.sons[0].sons[1])
|
||||||
let L2 = c.xjmp(n, opcTJmp, tmp)
|
let L2 = c.xjmp(n, opcTJmp, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
c.gen(n.sons[1])
|
c.gen(n.sons[1])
|
||||||
c.jmpBack(n, opcJmp, L1)
|
c.jmpBack(n, L1)
|
||||||
c.patch(L2)
|
c.patch(L2)
|
||||||
else:
|
else:
|
||||||
var tmp = c.genx(n.sons[0])
|
var tmp = c.genx(n.sons[0])
|
||||||
let L2 = c.xjmp(n, opcFJmp, tmp)
|
let L2 = c.xjmp(n, opcFJmp, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
c.gen(n.sons[1])
|
c.gen(n.sons[1])
|
||||||
c.jmpBack(n, opcJmp, L1)
|
c.jmpBack(n, L1)
|
||||||
c.patch(L2)
|
c.patch(L2)
|
||||||
|
|
||||||
proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
|
|
@ -1518,7 +1518,7 @@ proc optimizeJumps(c: PCtx; start: int) =
|
||||||
var d = i + c.code[i].jmpDiff
|
var d = i + c.code[i].jmpDiff
|
||||||
for iters in countdown(maxIterations, 0):
|
for iters in countdown(maxIterations, 0):
|
||||||
case c.code[d].opcode
|
case c.code[d].opcode
|
||||||
of opcJmp:
|
of opcJmp, opcJmpBack:
|
||||||
d = d + c.code[d].jmpDiff
|
d = d + c.code[d].jmpDiff
|
||||||
of opcTJmp, opcFJmp:
|
of opcTJmp, opcFJmp:
|
||||||
if c.code[d].regA != reg: break
|
if c.code[d].regA != reg: break
|
||||||
|
|
@ -1536,7 +1536,7 @@ proc optimizeJumps(c: PCtx; start: int) =
|
||||||
else: break
|
else: break
|
||||||
if d != i + c.code[i].jmpDiff:
|
if d != i + c.code[i].jmpDiff:
|
||||||
c.finalJumpTarget(i, d - i)
|
c.finalJumpTarget(i, d - i)
|
||||||
of opcJmp:
|
of opcJmp, opcJmpBack:
|
||||||
var d = i + c.code[i].jmpDiff
|
var d = i + c.code[i].jmpDiff
|
||||||
var iters = maxIterations
|
var iters = maxIterations
|
||||||
while c.code[d].opcode == opcJmp and iters > 0:
|
while c.code[d].opcode == opcJmp and iters > 0:
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue