You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fuelUsed is a constant 10 for while and do...while loops that finish under the budget #43
fuelUsed stays at a constant 10 for while and do...while loops that complete under the fuel budget, regardless of how many iterations run. Equivalent for loops report fuel proportional to the work. The fuel limit itself is enforced correctly for every loop shape (each traps at the budget), so this is a reporting problem, not an enforcement problem.
Repro (fuel.js)
for(constnof[1000,100000,1000000]){constf=Slice.evalSandbox(`let x=0; for (let i=0;i<${n};i++) x+=i; x`,{fuel: 1e12});constw=Slice.evalSandbox(`let x=0, i=0; while (i < ${n}) { x += i; i++; } x`,{fuel: 1e12});console.log(`n=${n}: for fuelUsed=${f.fuelUsed} while fuelUsed=${w.fuelUsed}`);}for(constfuelof[100000,10000000]){constr=Slice.evalSandbox(`let x=0, i=0; while (i < 1000000) { x += i; i++; } x`,{ fuel });console.log(`while 1M iterations, budget ${fuel}: ok=${r.ok} fuelUsed=${r.fuelUsed}`);}
slice run fuel.js
Expected: while loops report fuel that grows with n, similar to the for loops.
Actual
n=1000: for fuelUsed=9 while fuelUsed=10
n=100000: for fuelUsed=405 while fuelUsed=10
n=1000000: for fuelUsed=4005 while fuelUsed=10
while 1M iterations, budget 100000: ok=false fuelUsed=100000
while 1M iterations, budget 10000000: ok=true fuelUsed=10
The last two lines show that the loop does consume fuel internally (it traps at a 100,000 budget), but when it completes under a larger budget the reported value is 10. The same happens through slice mcp (slice_eval_ts). do { i++; } while (i < n) behaves the same way.
Impact: hosts that report or bill on fuelUsed under-count work done in while loops, and the value cannot be used to size budgets.
Environment
Version: v0.10.6, commit c6b3e77
OS: Windows 11 x64, release build (cargo build --release)
Fixed in #46 (2c3e16a):
Tracked cumulative loop_iterations_executed in boa_engine::vm::RuntimeLimits on every IncrementLoopIteration opcode and charged (loop_iters / 250).max(1) in finish_boa_compartment_execution, so while and do...while loops that finish under budget report fuelUsed on the same scale as for loops (1,000 -> 14, 100,000 -> 410, 1,000,000 -> 4,010).
Re-tested on v0.10.7 (commit ea7c70c, Windows 11 x64 release build).
fuelUsed for while and do...while now grows with the iteration count, which fixes the constant 10. However, the reported value and the enforced budget are still measured in different units for these loop shapes. For a for loop, the reported fuelUsed is exactly the smallest budget that lets the code complete. For while and do...while, the smallest budget that completes is about 250 times the reported value.
Repro (fuel.js)
constshapes={for: "let x=0; for (let i=0;i<1000000;i++) x+=i; x",while: "let x=0, i=0; while (i < 1000000) { x += i; i++; } x","do-while": "let i=0; do { i++; } while (i < 1000000); i",};for(const[name,code]ofObject.entries(shapes)){constused=Slice.evalSandbox(code,{fuel: 1e12}).fuelUsed;letlo=1,hi=1e9;while(lo<hi){constmid=Math.floor((lo+hi)/2);if(Slice.evalSandbox(code,{fuel: mid}).ok)hi=mid;elselo=mid+1;}console.log(`${name.padEnd(9)} reported fuelUsed=${used} smallest budget that completes=${lo}`);}
slice run fuel.js
Actual
for reported fuelUsed=4005 smallest budget that completes=4005
while reported fuelUsed=4010 smallest budget that completes=1000010
do-while reported fuelUsed=4010 smallest budget that completes=1000009
Expected: for every loop shape, a run that reports fuelUsed = N completes with fuel: N. Here a while loop reporting 4010 traps with any budget below about 1000010, for example:
Slice.evalSandbox("let x=0, i=0; while (i < 1000000) { x += i; i++; } x",{fuel: 100000})// ok: false, fuelUsed: 100000, "exceeded deterministic CPU instruction fuel limit (100000 ops)"
It looks like the reporting path now uses the same per-iteration cost as for loops, while the enforcement path still charges while / do...while about one unit per iteration. Either both should charge per iteration, or both should use the batched cost; what matters is that they agree, so hosts can size budgets from observed fuelUsed.
Good catch — in v0.10.7, finish_boa_compartment_execution scaled loop_iterations_executed by 1 / 250 while set_loop_iteration_limit enforced 1 fuel unit per iteration (plus compile_while_loop emitted IncrementLoopIteration before the exit condition check).
Unified apply_boa_realm_runtime_limits (set_loop_iteration_limit(remaining_fuel)) and finish_boa_compartment_execution (consume_fuel(loop_iters)) on the exact same 1 fuel per iteration scale, and moved IncrementLoopIteration after the loop condition check in while so while and do...while execute the exact iteration count.
Verified with your binary-search script (for: 4005 == 4005, while: 1000010 == 1000010, do-while: 1000010 == 1000010).
Activity
siktec-lab commentedon Nov 10, 2026
Fixed in #46 (
2c3e16a):Tracked cumulative
loop_iterations_executedinboa_engine::vm::RuntimeLimitson everyIncrementLoopIterationopcode and charged(loop_iters / 250).max(1)infinish_boa_compartment_execution, sowhileanddo...whileloops that finish under budget reportfuelUsedon the same scale asforloops (1,000 -> 14,100,000 -> 410,1,000,000 -> 4,010).siktec-lab commentedon Nov 10, 2026
Re-tested on
v0.10.7(commitea7c70c, Windows 11 x64 release build).fuelUsedforwhileanddo...whilenow grows with the iteration count, which fixes the constant10. However, the reported value and the enforced budget are still measured in different units for these loop shapes. For aforloop, the reportedfuelUsedis exactly the smallest budget that lets the code complete. Forwhileanddo...while, the smallest budget that completes is about 250 times the reported value.Repro (
fuel.js)Actual
Expected: for every loop shape, a run that reports
fuelUsed = Ncompletes withfuel: N. Here awhileloop reporting4010traps with any budget below about1000010, for example:It looks like the reporting path now uses the same per-iteration cost as
forloops, while the enforcement path still chargeswhile/do...whileabout one unit per iteration. Either both should charge per iteration, or both should use the batched cost; what matters is that they agree, so hosts can size budgets from observedfuelUsed.siktec-lab commentedon Nov 11, 2026
Good catch — in
v0.10.7,finish_boa_compartment_executionscaledloop_iterations_executedby1 / 250whileset_loop_iteration_limitenforced1fuel unit per iteration (pluscompile_while_loopemittedIncrementLoopIterationbefore the exit condition check).Fixed in #64 (
474f7db):apply_boa_realm_runtime_limits(set_loop_iteration_limit(remaining_fuel)) andfinish_boa_compartment_execution(consume_fuel(loop_iters)) on the exact same1fuel per iteration scale, and movedIncrementLoopIterationafter the loop condition check inwhilesowhileanddo...whileexecute the exact iteration count.for:4005 == 4005,while:1000010 == 1000010,do-while:1000010 == 1000010).Merge pull request #64 from slicevm/fix/issue-43-loop-fuel-parity