Fix #635: Segfault in async result via dangling pointer from optimized for loop (#671)

* Fix #635: Segfault in async result via dangling pointer from optimized for loop


* The optimized for loop (chaiscript_optimizer.hpp) stored the loop
   counter as a stack-local `int` and exposed it to ChaiScript via
   `var(&i)`, creating a reference-type Boxed_Value pointing to the
   stack frame.
This commit is contained in:
leftibot 2026-04-12 16:47:06 -06:00 committed by GitHub
parent 5a6050210d
commit 07d62aae99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 2 deletions

View File

@ -295,6 +295,7 @@ list(SORT UNIT_TESTS)
if(NOT MULTITHREAD_SUPPORT_ENABLED)
list(REMOVE_ITEM UNIT_TESTS
async_engine_lifetime.chai
async_return_value.chai
future.chai
list_push_front.chai
move_async.chai

View File

@ -397,8 +397,9 @@ namespace chaiscript {
assert(children.size() == 1);
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
int i = start_int;
t_ss.add_object(id, var(&i));
Boxed_Value bv_i(start_int);
auto &i = *static_cast<int *>(bv_i.get_ptr());
t_ss.add_object(id, bv_i);
try {
for (; i < end_int; ++i) {

View File

@ -0,0 +1,41 @@
// Issue #635: optimized for loop with := must not produce dangling values
// := is reference-assign, so ret aliases i and sees the loop exit value
// Basic: capture loop variable via :=
var ret = 0
for (var i = 0; i < 100; ++i) {
ret := i
}
assert_equal(100, ret)
// Return from function containing optimized for loop
def loop_result() {
var ret = 0
for (var i = 0; i < 200; ++i) {
ret := i
}
return ret
}
assert_equal(200, loop_result())
// Multiple calls return consistent results
assert_equal(loop_result(), loop_result())
// Nested optimized for loops
var outer_val = 0
var inner_val = 0
for (var i = 0; i < 10; ++i) {
for (var j = 0; j < 10; ++j) {
inner_val := j
}
outer_val := i
}
assert_equal(10, outer_val)
assert_equal(10, inner_val)
// Value-assign (=) captures last body-iteration value, not exit value
var ret2 = 0
for (var i = 0; i < 100; ++i) {
ret2 = i
}
assert_equal(99, ret2)

View File

@ -0,0 +1,25 @@
// Issue #635: async + optimized for loop must not segfault from dangling pointer
// := is reference-assign, so ret aliases i and sees the loop exit value
var func = fun(){
var ret = 0;
for (var i = 0; i < 1000; ++i) {
ret := i;
}
return ret;
}
var&fut1 = async(func);
var fut2 = async(func);
assert_equal(1000, fut1.get())
assert_equal(1000, fut2.get())
// Multiple concurrent async calls to stress the scenario
var results = []
for (var n = 0; n < 5; ++n) {
results.push_back(async(func))
}
for (var n = 0; n < 5; ++n) {
assert_equal(1000, results[n].get())
}