add jailed exec which raises timeout after a number of instructions have been interpreted

This commit is contained in:
Benoit Favre 2025-02-28 13:31:10 +01:00
parent e1373766b1
commit efacdad731
6 changed files with 40 additions and 3 deletions

View File

@ -26,6 +26,9 @@ typedef struct VM {
py_TValue builtins; // builtins module py_TValue builtins; // builtins module
py_TValue main; // __main__ module py_TValue main; // __main__ module
py_i64 max_steps;
py_i64 used_steps;
py_Callbacks callbacks; py_Callbacks callbacks;
py_TValue ascii_literals[128+1]; py_TValue ascii_literals[128+1];
@ -136,4 +139,4 @@ py_Type pk_code__register();
py_TValue pk_builtins__register(); py_TValue pk_builtins__register();
/* mappingproxy */ /* mappingproxy */
void pk_mappingproxy__namedict(py_Ref out, py_Ref object); void pk_mappingproxy__namedict(py_Ref out, py_Ref object);

View File

@ -735,6 +735,7 @@ enum py_PredefinedTypes {
tp_ImportError, tp_ImportError,
tp_AssertionError, tp_AssertionError,
tp_KeyError, tp_KeyError,
tp_Timeout,
/* linalg */ /* linalg */
tp_vec2, tp_vec2,
tp_vec3, tp_vec3,

View File

@ -109,6 +109,15 @@ FrameResult VM__run_top_frame(VM* self) {
goto __ERROR; goto __ERROR;
} }
if (self->max_steps > 0) {
if (self->used_steps >= self->max_steps) {
py_exception(tp_Timeout, "execution timed out");
goto __ERROR;
}
self->used_steps++;
}
switch((Opcode)byte.op) { switch((Opcode)byte.op) {
case OP_NO_OP: DISPATCH(); case OP_NO_OP: DISPATCH();
/*****************************************/ /*****************************************/
@ -1440,4 +1449,4 @@ static bool stack_format_object(VM* self, c11_sv spec) {
#undef POPX #undef POPX
#undef SP #undef SP
#undef INSERT_THIRD #undef INSERT_THIRD
#undef vectorcall_opcall #undef vectorcall_opcall

View File

@ -176,6 +176,7 @@ void VM__ctor(VM* self) {
INJECT_BUILTIN_EXC(ImportError, tp_Exception); INJECT_BUILTIN_EXC(ImportError, tp_Exception);
INJECT_BUILTIN_EXC(AssertionError, tp_Exception); INJECT_BUILTIN_EXC(AssertionError, tp_Exception);
INJECT_BUILTIN_EXC(KeyError, tp_Exception); INJECT_BUILTIN_EXC(KeyError, tp_Exception);
INJECT_BUILTIN_EXC(Timeout, tp_Exception);
#undef INJECT_BUILTIN_EXC #undef INJECT_BUILTIN_EXC
#undef validate #undef validate
@ -812,4 +813,4 @@ int py_replinput(char* buf, int max_size) {
buf[size] = '\0'; buf[size] = '\0';
return size; return size;
} }

View File

@ -612,6 +612,20 @@ static bool builtins_exec(int argc, py_Ref argv) {
return ok; return ok;
} }
static bool builtins_exec_jailed(int argc, py_Ref argv) {
VM* vm = pk_current_vm;
PY_CHECK_ARG_TYPE(0, tp_int);
int was_jailed = (vm->max_steps > 0);
if (!was_jailed) {
vm->max_steps = py_toint(py_arg(0));
vm->used_steps = 0;
}
bool ok = _builtins_execdyn("exec", argc - 1, argv + 1, EXEC_MODE);
if (!was_jailed) vm->max_steps = 0;
py_newnone(py_retval());
return ok;
}
static bool builtins_eval(int argc, py_Ref argv) { static bool builtins_eval(int argc, py_Ref argv) {
return _builtins_execdyn("eval", argc, argv, EVAL_MODE); return _builtins_execdyn("eval", argc, argv, EVAL_MODE);
} }
@ -755,6 +769,7 @@ py_TValue pk_builtins__register() {
py_bindfunc(builtins, "globals", builtins_globals); py_bindfunc(builtins, "globals", builtins_globals);
py_bindfunc(builtins, "locals", builtins_locals); py_bindfunc(builtins, "locals", builtins_locals);
py_bindfunc(builtins, "exec", builtins_exec); py_bindfunc(builtins, "exec", builtins_exec);
py_bindfunc(builtins, "exec_jailed", builtins_exec_jailed);
py_bindfunc(builtins, "eval", builtins_eval); py_bindfunc(builtins, "eval", builtins_eval);
py_bindfunc(builtins, "compile", builtins_compile); py_bindfunc(builtins, "compile", builtins_compile);

View File

@ -74,3 +74,11 @@ exec(code, {'x': 42, 'res': res})
assert res == [42, 42] assert res == [42, 42]
assert x == 33 assert x == 33
code = '''
while True:
pass
'''
try:
exec_jailed(100000, code)
except Timeout:
pass