Skip to content

Commit c4983ce

Browse files
lucylqGithub Executorch
andauthored
Add instruction execution limit to prevent infinite loops (#18679)
JumpFalseCall instructions can set destination_instruction to themselves, creating an infinite loop that hangs the runtime. This adds a configurable instruction counter (default 10M, overridable via -DET_MAX_INSTRUCTIONS) to Method::execute() that returns Error::InvalidState if exceeded. This PR was authored with the assistance of Claude. ### Test plan Existing tests ```bash cmake -B build -DEXECUTORCH_BUILD_TESTS=ON cmake --build build --target method_test ctest --test-dir build -R method_test --output-on-failure ``` Co-authored-by: Github Executorch <github_executorch@arm.com>
1 parent 78a6689 commit c4983ce

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

runtime/executor/method.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ namespace ET_RUNTIME_NAMESPACE {
3737

3838
using internal::PlatformMemoryAllocator;
3939

40+
// Maximum number of instructions that Method::execute() will run before
41+
// returning an error. Prevents infinite loops caused by malformed programs
42+
// (e.g., JumpFalseCall instructions whose destination_instruction points to
43+
// themselves). Override at compile time via -DET_MAX_INSTRUCTIONS=<value>.
44+
#ifndef ET_MAX_INSTRUCTIONS
45+
#define ET_MAX_INSTRUCTIONS 10000000
46+
#endif
47+
static_assert(
48+
(ET_MAX_INSTRUCTIONS) > 0,
49+
"ET_MAX_INSTRUCTIONS must be positive. 0 would reject every program on "
50+
"its first instruction; negative values wrap to SIZE_MAX when assigned "
51+
"to size_t, silently disabling the infinite-loop guard.");
52+
static constexpr size_t kMaxInstructions = ET_MAX_INSTRUCTIONS;
53+
4054
/**
4155
* Runtime state for a backend delegate.
4256
*/
@@ -1664,6 +1678,7 @@ Error Method::execute() {
16641678

16651679
// Chains are executed sequentially today, but future async designs may
16661680
// branch and run many in parallel or out of order.
1681+
size_t instruction_count = 0;
16671682
for (step_state_.chain_idx = 0; step_state_.chain_idx < n_chains_;
16681683
++step_state_.chain_idx) {
16691684
Chain& chain = chains_[step_state_.chain_idx];
@@ -1677,6 +1692,21 @@ Error Method::execute() {
16771692
// Loop over instructions
16781693
step_state_.instr_idx = 0;
16791694
while (step_state_.instr_idx < chain.s_chain_->instructions()->size()) {
1695+
if (instruction_count >= kMaxInstructions) {
1696+
ET_LOG(
1697+
Error,
1698+
"Instruction execution limit (%" ET_PRIsize_t
1699+
") exceeded at chain %" ET_PRIsize_t ", instruction %" ET_PRIsize_t
1700+
". Possible infinite loop detected. If this is a legitimate "
1701+
"large model, raise the limit by rebuilding with "
1702+
"-DET_MAX_INSTRUCTIONS=<value>.",
1703+
kMaxInstructions,
1704+
step_state_.chain_idx,
1705+
step_state_.instr_idx);
1706+
step_state_ = StepState{0, 0};
1707+
return Error::InvalidProgram;
1708+
}
1709+
++instruction_count;
16801710
EXECUTORCH_PROFILE_INSTRUCTION_SCOPE(
16811711
static_cast<int32_t>(step_state_.chain_idx),
16821712
static_cast<uint32_t>(step_state_.instr_idx));

0 commit comments

Comments
 (0)