bytevm: fix a possible segmentation fault in OP_PUSHKEYS

The issue was revealed by registering long (EQL LIST) elements as cons types --
essentially we've reached the frame size limit in the middle of the loop, the
frame was resized, but the pointer `first' was relative to the old frame base.

The solution is to reinitialize the pointer before each iteration.
This commit is contained in:
Daniel Kochmański 2025-08-27 09:49:21 +02:00
parent b9e0a7f949
commit f04f0ac160
2 changed files with 17 additions and 16 deletions

View file

@ -653,12 +653,10 @@ ecl_interpret(cl_object frame, cl_object closure, cl_object bytecodes)
Checks the stack frame for keyword arguments. Checks the stack frame for keyword arguments.
*/ */
CASE(OP_PUSHKEYS); { CASE(OP_PUSHKEYS); {
cl_object keys_list, aok, *first, *last; cl_object keys_list, aok, *ptr, *end;
cl_index count; cl_index count, limit;
GET_DATA(keys_list, vector, data); GET_DATA(keys_list, vector, data);
first = ECL_STACK_FRAME_PTR(frame) + frame_index; limit = count = frame->frame.size - frame_index;
count = frame->frame.size - frame_index;
last = first + count;
if (ecl_unlikely(count & 1)) { if (ecl_unlikely(count & 1)) {
VEbad_lambda_odd_keys(bytecodes, frame); VEbad_lambda_odd_keys(bytecodes, frame);
} }
@ -667,28 +665,32 @@ ecl_interpret(cl_object frame, cl_object closure, cl_object bytecodes)
cl_object name = ECL_CONS_CAR(keys_list); cl_object name = ECL_CONS_CAR(keys_list);
cl_object flag = ECL_NIL; cl_object flag = ECL_NIL;
cl_object value = ECL_NIL; cl_object value = ECL_NIL;
cl_object *p = first; ptr = ECL_STACK_FRAME_PTR(frame) + frame_index;
for (; p != last; ++p) { end = ptr + limit;
if (*(p++) == name) { for (; ptr != end; ptr++) {
if (*(ptr++) == name) {
count -= 2; count -= 2;
if (flag == ECL_NIL) { if (flag == ECL_NIL) {
flag = ECL_T; flag = ECL_T;
value = *p; value = *ptr;
} }
} }
} }
/* Pushing to the stack may resize it, so be careful to reinitialize
pointers using the new value of ECL_STACK_FRAME_PTR. */
if (flag != ECL_NIL) ECL_STACK_PUSH(the_env, value); if (flag != ECL_NIL) ECL_STACK_PUSH(the_env, value);
ECL_STACK_PUSH(the_env, flag); ECL_STACK_PUSH(the_env, flag);
} }
if (count && Null(aok)) { if (count && Null(aok)) {
cl_object *p = first; ptr = ECL_STACK_FRAME_PTR(frame) + frame_index;
for (; p != last; ++p) { end = ptr + limit;
if (*(p++) == @':allow-other-keys') { for (; ptr != end; ptr++) {
aok = *p; if (*(ptr++) == @':allow-other-keys') {
aok = *ptr;
count -= 2; count -= 2;
/* only the first :allow-other-keys argument is considered */ /* only the first :allow-other-keys argument is considered */
for (++p; p != last; ++p) { for (ptr++; ptr != end; ptr++) {
if (*(p++) != @':allow-other-keys') if (*(ptr++) != @':allow-other-keys')
break; break;
count -= 2; count -= 2;
} }

View file

@ -392,7 +392,6 @@ ecl_bds_overflow(void)
cl_env_ptr env = ecl_process_env(); cl_env_ptr env = ecl_process_env();
cl_index margin = ecl_option_values[ECL_OPT_BIND_STACK_SAFETY_AREA]; cl_index margin = ecl_option_values[ECL_OPT_BIND_STACK_SAFETY_AREA];
cl_index size = env->bds_stack.size; cl_index size = env->bds_stack.size;
cl_index limit_size = env->bds_stack.limit_size;
ecl_bds_ptr org = env->bds_stack.org; ecl_bds_ptr org = env->bds_stack.org;
ecl_bds_ptr last = org + size; ecl_bds_ptr last = org + size;
if (env->bds_stack.limit >= last) { if (env->bds_stack.limit >= last) {