Repository navigation
Host Safety Controls
Frank edited this page Jan 26, 2026
·
2 revisions
- Overview
- Instruction Limit
- Timeout
- Interrupt
- Checking Instruction Count
- Best Practices
- Implementation Details
- Testing
- Error Handling
CupidScript provides comprehensive safety controls to protect the host application from malicious or buggy scripts that could hang the system.
When embedding CupidScript (e.g., in CupidFM for user plugins), it's critical to prevent scripts from:
- Running infinite loops that freeze the application
- Consuming excessive CPU time
- Blocking the UI thread indefinitely
The VM provides three layers of protection:
- Instruction Limit - caps total operations
- Timeout - caps wall-clock execution time
- Interrupt - allows host to cancel execution
Safety controls can be configured in two ways:
- Host-level (C API) - Set default limits for all scripts
- Script-level - Scripts can adjust their own limits within bounds
cs_vm_set_instruction_limit(vm, 10000000); // 10 million instructions// Scripts can configure their own limits
set_instruction_limit(50000000); // Request 50M instructions
// Check current limit
let limit = get_instruction_limit();
print("Instruction limit:", limit);
// Check current count
let count = get_instruction_count();
print("Instructions executed:", count);- Counts every expression evaluation in the VM
- When limit is exceeded, script aborts with error
- Error message:
"instruction limit exceeded (N instructions)" - Default:
0(unlimited)
- Predictable CPU budgets
- Scripts with known complexity bounds
- Testing/debugging script performance
cs_vm* vm = cs_vm_new();
cs_register_stdlib(vm);
// Allow up to 50 million operations
cs_vm_set_instruction_limit(vm, 50000000);
int rc = cs_vm_run_file(vm, "plugin.cs");
if (rc != 0) {
fprintf(stderr, "%s\n", cs_vm_last_error(vm));
fprintf(stderr, "Instructions: %llu\n",
(unsigned long long)cs_vm_get_instruction_count(vm));
}cs_vm_set_timeout(vm, 5000); // 5 second timeout// Heavy processing script can request more time
set_timeout(30000); // Request 30 seconds
// Check current timeout
let timeout = get_timeout();
print("Timeout:", timeout, "ms");
// Quick operations can set shorter timeout
set_timeout(1000); // 1 second for fast response- Measures wall-clock time from script start
- Checked every 1000 instructions (minimal overhead)
- When exceeded, script aborts with error
- Error message:
"execution timeout exceeded (N ms)" - Default:
0(unlimited)
- Real-time constraints (e.g., UI responsiveness)
- Scripts doing I/O or system calls
- User-facing plugin execution
- UI plugins: 1-5 seconds (keeps interface responsive)
- Batch processing: 30-60 seconds (handles larger datasets)
- Background tasks: 5-10 minutes (heavy operations)
// Plugin must complete within 10 seconds
cs_vm_set_timeout(vm, 10000);
int rc = cs_vm_run_string(vm, user_code, "user_plugin");
if (rc != 0) {
printf("Plugin timed out: %s\n", cs_vm_last_error(vm));
}// From main thread:
cs_vm_run_file(vm, "long_script.cs");
// From UI thread (e.g., cancel button):
cs_vm_interrupt(vm);- Sets a thread-safe flag checked on every expression
- Script aborts immediately when flag is detected
- Error message:
"execution interrupted by host" - Flag auto-resets at start of each script execution
- User-initiated cancellation
- Application shutdown
- Emergency abort scenarios
#include <pthread.h>
typedef struct {
cs_vm* vm;
const char* script_path;
} vm_thread_args;
void* run_script_thread(void* arg) {
vm_thread_args* args = (vm_thread_args*)arg;
cs_vm_run_file(args->vm, args->script_path);
return NULL;
}
int main() {
cs_vm* vm = cs_vm_new();
cs_register_stdlib(vm);
vm_thread_args args = { vm, "long_script.cs" };
pthread_t thread;
pthread_create(&thread, NULL, run_script_thread, &args);
// Wait for user input
printf("Press Enter to cancel script...\n");
getchar();
// Interrupt from main thread
cs_vm_interrupt(vm);
pthread_join(thread, NULL);
printf("Script status: %s\n", cs_vm_last_error(vm));
cs_vm_free(vm);
return 0;
}Retrieve the current count for profiling or debugging:
uint64_t count = cs_vm_get_instruction_count(vm);
printf("Script executed %llu instructions\n", (unsigned long long)count);This is useful for:
- Profiling script complexity
- Comparing algorithm efficiency
- Debugging performance issues
// Reasonable defaults for user plugins:
cs_vm_set_instruction_limit(vm, 100000000); // 100M instructions
cs_vm_set_timeout(vm, 30000); // 30 second timeout
// Allow user to cancel long operations
on_cancel_button_click() {
cs_vm_interrupt(vm);
}// Higher limits for non-interactive scripts
cs_vm_set_instruction_limit(vm, 1000000000); // 1B instructions
cs_vm_set_timeout(vm, 300000); // 5 minute timeout// Strict limits for sandboxed execution
cs_vm_set_instruction_limit(vm, 10000000); // 10M instructions
cs_vm_set_timeout(vm, 5000); // 5 second timeout// Use both instruction limit AND timeout for maximum safety
cs_vm_set_instruction_limit(vm, 50000000); // CPU budget
cs_vm_set_timeout(vm, 10000); // Wall-clock budget
// Script will abort when EITHER limit is exceeded
int rc = cs_vm_run_file(vm, "script.cs");- Instruction counting: negligible overhead (simple increment)
- Timeout checking: only every 1000 instructions
- Interrupt flag: single atomic read per expression
-
cs_vm_interrupt()is the only thread-safe VM function - All other VM operations must run on a single thread
- Interrupt flag uses volatile semantics for visibility
All safety counters automatically reset when calling:
cs_vm_run_file()cs_vm_run_string()-
cs_call()/cs_call_value()
This ensures each script execution starts with a fresh budget.
See examples/safety_demo.c for a complete working demonstration of all safety controls.
Run tests:
# Compile safety demo
gcc -std=c99 -O2 -Isrc examples/safety_demo.c bin/libcupidscript.a -lm -o bin/safety_demo
# Run demonstrations
./bin/safety_demoWhen a safety limit is exceeded, the VM:
- Sets
vm->last_errorwith descriptive message - Returns error code from
cs_vm_run_*function - Preserves instruction count for debugging
- Includes script location in error message
Example error messages:
"Runtime error at script.cs:10:5: instruction limit exceeded (10000000 instructions)""Runtime error at plugin.cs:45:12: execution timeout exceeded (5000 ms)""Runtime error at user.cs:23:8: execution interrupted by host"