Skip to content

Block uninterruptible big-int operations in the local Python executor - #2551

Open
himanshu748 wants to merge 3 commits into
huggingface:mainfrom
himanshu748:fix-uninterruptible-bigint-ops
Open

Block uninterruptible big-int operations in the local Python executor#2551
himanshu748 wants to merge 3 commits into
huggingface:mainfrom
himanshu748:fix-uninterruptible-bigint-ops

Conversation

@himanshu748

Copy link
Copy Markdown

Fixes #2473 and complements #2464.

What happens today

A single model-generated expression like 10 ** 10 ** 8 freezes the entire
process, and timeout_seconds cannot save you. CPython computes arbitrary
precision **, << and * in one C call that holds the GIL without ever
crossing a bytecode boundary, so the thread-based timeout() watchdog never
gets scheduled. A faulthandler dump from the repro shows the main thread stuck
inside ThreadPoolExecutor.submit -> Thread.start: the timeout never even
armed. MAX_OPERATIONS does not help because the entire cost sits inside a
single AST operation.

Repro (freezes forever on main, needs an external kill):

from smolagents.local_python_executor import LocalPythonExecutor

executor = LocalPythonExecutor(additional_authorized_imports=[], timeout_seconds=2)
executor.send_tools({})
executor("10 ** 10 ** 8")  # never returns, never times out

The fix

Since the operation cannot be interrupted once started, refuse to start it.
A new check_safe_int_operation estimates the result bit length from operand
bit lengths and raises an informative InterpreterError above MAX_INT_BITS
(1,000,000 bits, roughly 300k digits):

  • **: left.bit_length() * right, an upper bound
  • <<: left.bit_length() + right
  • *: left.bit_length() + right.bit_length()

The guard is applied in evaluate_binop, evaluate_augassign and the exposed
pow builtin, covering **, <<, *, the augmented variants and
pow(a, b). Modular pow(a, b, m) stays unrestricted. The error message
points the model at smaller operands or modular exponentiation, so an agent
recovers in the next step instead of hanging the host process.

Repeated multiplication (a = 10**300000 then b = a * a * a * ...), the
other vector from the issue, is caught by the * guard once operands grow
past the cap.

Tests

15 new parametrized tests in tests/test_local_python_executor.py: 7 explosive
patterns now raise InterpreterError (they hang forever on main) and 8
legitimate operations keep working, including 100! via repeated *=,
pow(7, 2**64, 97), float pow and 1 ** 10**9. Full file: 412 passed,
2 skipped. Ruff check and format are clean.

Copilot AI review requested due to automatic review settings July 19, 2026 07:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce9da1297f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/smolagents/local_python_executor.py Outdated
Comment on lines +75 to +76
"""
if type(left) is not int or type(right) is not int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include bool/int subclasses in the big-int guard

When either operand is bool or another int subclass, this exact-type check returns before applying the limit even though CPython still dispatches to inherited integer arithmetic. That leaves trivial bypasses such as True << 10**9 or class I(int): pass; I(10) ** (10**8) that still enter the same uninterruptible big-int C operations and can hang LocalPythonExecutor despite this mitigation.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Thread-based timeout cannot interrupt single big-integer operations

2 participants