rhai limits #8

Closed
opened 2026-04-05 17:29:08 +00:00 by despiegk · 4 comments
Owner

image

see RHAI_LIMITS_INVESTIGATION.md

![image](/attachments/a549a229-73c6-4a0f-98b7-ec947492df20) see RHAI_LIMITS_INVESTIGATION.md
347 KiB
Owner

Root Cause Analysis

Smoking gun found: crates/os_rhai/src/profiling.rs:182 sets engine.set_max_operations(10000) on the shared hero_do engine.

Call chain:

  1. hero_do/src/main.rscreate_engine_with_base_path()Engine::new() (default: unlimited operations)
  2. herolib_os_rhai::register_os_system_module(&mut engine) (os_rhai/src/lib.rs:63)
  3. register_profiling_module(engine) (os_rhai/src/profiling.rs:7)
  4. configure_engine_security(engine) (os_rhai/src/profiling.rs:82)
  5. engine.set_max_operations(10000) (os_rhai/src/profiling.rs:182) ← THE BUG

This 10,000 operation limit is extremely restrictive. Any non-trivial script hits it quickly.

Additionally, configure_engine_security disables symbols (File, Dir, Env, Process, Command, Http, Tcp, Udp, debug, inspect) on the shared engine — these should NOT be disabled globally.

Why the investigation found set_max_operations(0) did not help:

The hero_do engine creation does NOT call set_max_operations(0). So even if you set it to 0 in hero_do, the profiling module registration (called AFTER) overrides it back to 10,000.

Fix Strategy

  1. Remove security overrides from profiling module registration — Module registration functions should ONLY register types and functions, never modify global engine settings (limits, disabled symbols). The configure_engine_security() and configure_engine_security_strict() calls will be removed from the register_ functions.

  2. Add centralized configure_engine_limits() in hero_do — Both main.rs and lib.rs will get a shared limits function called AFTER all module registrations:

    • set_max_expr_depths(0, 0) — unlimited
    • set_max_call_levels(0) — unlimited
    • set_max_operations(0) — unlimited
    • set_max_modules(0) — unlimited
    • set_max_string_size(0) — unlimited
    • set_max_array_size(0) — unlimited
    • set_max_map_size(0) — unlimited
  3. Keep security functions available for standalone profiling use — Export them as public functions so callers who want sandboxed execution can opt in, but they will not be called during normal module registration.

  4. Apply the same fix pattern to foundry engine — Ensure the foundry engine limits are set AFTER all registrations.

## Root Cause Analysis **Smoking gun found:** `crates/os_rhai/src/profiling.rs:182` sets `engine.set_max_operations(10000)` on the shared hero_do engine. ### Call chain: 1. `hero_do/src/main.rs` → `create_engine_with_base_path()` → `Engine::new()` (default: unlimited operations) 2. → `herolib_os_rhai::register_os_system_module(&mut engine)` (`os_rhai/src/lib.rs:63`) 3. → `register_profiling_module(engine)` (`os_rhai/src/profiling.rs:7`) 4. → `configure_engine_security(engine)` (`os_rhai/src/profiling.rs:82`) 5. → **`engine.set_max_operations(10000)`** (`os_rhai/src/profiling.rs:182`) ← THE BUG This 10,000 operation limit is extremely restrictive. Any non-trivial script hits it quickly. Additionally, `configure_engine_security` disables symbols (`File`, `Dir`, `Env`, `Process`, `Command`, `Http`, `Tcp`, `Udp`, `debug`, `inspect`) on the shared engine — these should NOT be disabled globally. ### Why the investigation found `set_max_operations(0)` did not help: The hero_do engine creation does NOT call `set_max_operations(0)`. So even if you set it to 0 in hero_do, the profiling module registration (called AFTER) overrides it back to 10,000. ## Fix Strategy 1. **Remove security overrides from profiling module registration** — Module registration functions should ONLY register types and functions, never modify global engine settings (limits, disabled symbols). The `configure_engine_security()` and `configure_engine_security_strict()` calls will be removed from the `register_` functions. 2. **Add centralized `configure_engine_limits()` in hero_do** — Both `main.rs` and `lib.rs` will get a shared limits function called AFTER all module registrations: - `set_max_expr_depths(0, 0)` — unlimited - `set_max_call_levels(0)` — unlimited - `set_max_operations(0)` — unlimited - `set_max_modules(0)` — unlimited - `set_max_string_size(0)` — unlimited - `set_max_array_size(0)` — unlimited - `set_max_map_size(0)` — unlimited 3. **Keep security functions available** for standalone profiling use — Export them as public functions so callers who want sandboxed execution can opt in, but they will not be called during normal module registration. 4. **Apply the same fix pattern to foundry engine** — Ensure the foundry engine limits are set AFTER all registrations.
Owner

Fix Pushed — development_8 branch

Commit: fdb8a22 — Fix Rhai engine operation limits causing script failures (#8)

Changes:

crates/os_rhai/src/profiling.rs

  • Removed configure_engine_security() call from register_profiling_module (this was the bug — it set max_operations(10000) on the shared engine)
  • Removed configure_engine_security_strict() call from register_profiling_module_strict
  • Removed duplicate sub-module registrations that were being done a second time after the security config
  • Made both security functions public for opt-in use by callers who need sandboxed execution

crates/hero_do/src/main.rs

  • Added configure_engine_limits() function that sets all Rhai limits to 0 (unlimited)
  • Called AFTER all module registrations to prevent any module from overriding limits

crates/hero_do/src/lib.rs

  • Same configure_engine_limits() function added, called AFTER all module registrations
  • Removed leftover debug eprintln statements

Note:

The workspace has a pre-existing build issue (herolib_crypt missing rhai feature flag) that prevents cargo check from completing. The fix itself is syntactically correct and follows the established patterns.

## Fix Pushed — `development_8` branch **Commit:** `fdb8a22` — Fix Rhai engine operation limits causing script failures (#8) ### Changes: **`crates/os_rhai/src/profiling.rs`** - Removed `configure_engine_security()` call from `register_profiling_module` (this was the bug — it set `max_operations(10000)` on the shared engine) - Removed `configure_engine_security_strict()` call from `register_profiling_module_strict` - Removed duplicate sub-module registrations that were being done a second time after the security config - Made both security functions public for opt-in use by callers who need sandboxed execution **`crates/hero_do/src/main.rs`** - Added `configure_engine_limits()` function that sets all Rhai limits to 0 (unlimited) - Called AFTER all module registrations to prevent any module from overriding limits **`crates/hero_do/src/lib.rs`** - Same `configure_engine_limits()` function added, called AFTER all module registrations - Removed leftover debug eprintln statements ### Note: The workspace has a pre-existing build issue (herolib_crypt missing rhai feature flag) that prevents cargo check from completing. The fix itself is syntactically correct and follows the established patterns.
Owner

Merged and closing

Branch development_8 merged into development (fast-forward).

Verification

  • Confirmed bug on current installed hero_do: a 50,000-iteration loop fails with Too many operations
  • Confirmed root cause: os_rhai/profiling.rs was calling configure_engine_security() during module registration, setting max_operations(10000) on the shared engine
  • Standalone Rhai test proves set_max_operations(0) after the override correctly removes the limit

Fix summary (3 files)

  1. os_rhai/src/profiling.rs - Removed configure_engine_security() / configure_engine_security_strict() calls from module registration functions. Module registration should only register types and functions, never modify engine limits.
  2. hero_do/src/main.rs - Added configure_engine_limits() (all limits = 0/unlimited), called AFTER all module registrations.
  3. hero_do/src/lib.rs - Same configure_engine_limits() added for the library engine path.

Note: Workspace cannot currently build due to pre-existing dependency issues (herolib_crypt missing rhai feature, sqlite3 version conflict). Those are separate from this fix.

## Merged and closing Branch `development_8` merged into `development` (fast-forward). ### Verification - Confirmed bug on current installed `hero_do`: a 50,000-iteration loop fails with `Too many operations` - Confirmed root cause: `os_rhai/profiling.rs` was calling `configure_engine_security()` during module registration, setting `max_operations(10000)` on the shared engine - Standalone Rhai test proves `set_max_operations(0)` after the override correctly removes the limit ### Fix summary (3 files) 1. **`os_rhai/src/profiling.rs`** - Removed `configure_engine_security()` / `configure_engine_security_strict()` calls from module registration functions. Module registration should only register types and functions, never modify engine limits. 2. **`hero_do/src/main.rs`** - Added `configure_engine_limits()` (all limits = 0/unlimited), called AFTER all module registrations. 3. **`hero_do/src/lib.rs`** - Same `configure_engine_limits()` added for the library engine path. **Note:** Workspace cannot currently build due to pre-existing dependency issues (herolib_crypt missing `rhai` feature, sqlite3 version conflict). Those are separate from this fix.
timur closed this issue 2026-04-07 08:14:19 +00:00
Owner

Fully fixed and verified

Build now passes, hero_do installed and tested

Test result:

$ echo 'let count = 0; for i in 0..50000 { count += 1; } print(count);' | hero_do -i
Done: 50000 iterations

Previously this failed with Too many operations at ~10,000 ops.

Commits on development:

  1. fdb8a22 - Fix Rhai engine operation limits (the core #8 fix)

    • Removed configure_engine_security() from profiling module registration
    • Added configure_engine_limits() in hero_do, called after all module registrations
  2. a0e955e - Fix build: update bindings for hero_lib API changes

    • Fixed herolib_crypt dependency (removed non-existent rhai feature)
    • Fixed sqlite3 version conflict (rusqlite made optional in herolib_ai via hero_lib change)
    • Updated all broken Rhai bindings (forge, secrets, services, init, httpsig, git)
    • Removed legacy module (removed upstream)
    • Regenerated Cargo.lock

Also pushed to hero_lib (910b43d2): made rusqlite optional behind usage feature, bumped webserver rusqlite to 0.32.

## Fully fixed and verified ### Build now passes, hero_do installed and tested **Test result:** ``` $ echo 'let count = 0; for i in 0..50000 { count += 1; } print(count);' | hero_do -i Done: 50000 iterations ``` Previously this failed with `Too many operations` at ~10,000 ops. ### Commits on `development`: 1. **`fdb8a22`** - Fix Rhai engine operation limits (the core #8 fix) - Removed `configure_engine_security()` from profiling module registration - Added `configure_engine_limits()` in hero_do, called after all module registrations 2. **`a0e955e`** - Fix build: update bindings for hero_lib API changes - Fixed herolib_crypt dependency (removed non-existent `rhai` feature) - Fixed sqlite3 version conflict (rusqlite made optional in herolib_ai via hero_lib change) - Updated all broken Rhai bindings (forge, secrets, services, init, httpsig, git) - Removed legacy module (removed upstream) - Regenerated Cargo.lock Also pushed to **hero_lib** (`910b43d2`): made rusqlite optional behind `usage` feature, bumped webserver rusqlite to 0.32.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_lib_rhai#8
No description provided.