Skip to content

Commit f4b032a

Browse files
fix: heap-allocate interface values in array/variadic contexts
When casting lvalues to interface types inside array literals or variadic argument packing, the generated C code used stack pointers (&local_var) for the interface object. These pointers become dangling when the stack frame ends, causing parameter corruption on Linux (confirmed on CI: string values read as empty or pointed to wrong stack data like the query string). Fix: set inside_cast_in_heap when generating interface/sumtype array elements and variadic args so that call_cfn_for_casting_expr uses HEAP() instead of bare &. This does not affect direct interface assignments (mut ii := II(aa)) which correctly share memory. Fixes #26760 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 899fc7d commit f4b032a

3 files changed

Lines changed: 22 additions & 1 deletion

File tree

vlib/v/gen/c/array.v

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ fn (mut g Gen) array_init(node ast.ArrayInit, var_name string) {
5959
g.write('\t\t')
6060
}
6161
is_iface_or_sumtype := elem_sym.kind in [.sum_type, .interface]
62+
if is_iface_or_sumtype {
63+
g.inside_cast_in_heap++
64+
}
6265
for i, expr in node.exprs {
6366
expr_type := if node.expr_types.len > i { node.expr_types[i] } else { node.elem_type }
6467
if expr_type == ast.string_type
@@ -91,6 +94,9 @@ fn (mut g Gen) array_init(node ast.ArrayInit, var_name string) {
9194
}
9295
}
9396
}
97+
if is_iface_or_sumtype {
98+
g.inside_cast_in_heap--
99+
}
94100
g.write('}))')
95101
if g.is_shared {
96102
g.write('}, sizeof(${shared_styp}))')

vlib/v/gen/c/cgen.v

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3143,10 +3143,17 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Ty
31433143
false
31443144
}
31453145

3146+
// When casting an lvalue to an interface inside an array context
3147+
// (e.g. variadic args, array init), the interface stores a pointer
3148+
// that may outlive the source variable. Heap-allocate to prevent
3149+
// dangling pointers. (fixes #26760)
3150+
is_interface_in_heap_context := fname.contains('_to_Interface_')
3151+
&& g.inside_cast_in_heap > 0
3152+
31463153
if !is_cast_fixed_array_init && (is_comptime_variant || !expr.is_lvalue()
31473154
|| (expr is ast.Ident && (expr.obj.is_simple_define_const()
31483155
|| (expr.obj is ast.Var && expr.obj.is_index_var)))
3149-
|| is_primitive_to_interface || is_fn_arg) {
3156+
|| is_primitive_to_interface || is_fn_arg || is_interface_in_heap_context) {
31503157
// Note: the `_to_sumtype_` family of functions do call memdup internally, making
31513158
// another duplicate with the HEAP macro is redundant, so use ADDR instead:
31523159
if expr.is_as_cast() {

vlib/v/gen/c/fn.v

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,6 +2862,11 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
28622862
g.writeln('${g.styp(varg_type)} ${tmp_var};')
28632863
g.write('builtin___option_ok((${base_type}[]) {')
28642864
}
2865+
elem_sym := g.table.sym(arr_info.elem_type)
2866+
is_iface_or_sumtype := elem_sym.kind in [.sum_type, .interface]
2867+
if is_iface_or_sumtype {
2868+
g.inside_cast_in_heap++
2869+
}
28652870
g.write('builtin__new_array_from_c_array${noscan}(${variadic_count}, ${variadic_count}, sizeof(${elem_type}), _MOV((${elem_type}[${variadic_count}]){')
28662871
for j in arg_nr .. args.len {
28672872
g.ref_or_deref_arg(args[j], arr_info.elem_type, node.language,
@@ -2871,6 +2876,9 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
28712876
}
28722877
}
28732878
g.write('}))')
2879+
if is_iface_or_sumtype {
2880+
g.inside_cast_in_heap--
2881+
}
28742882
if is_option {
28752883
g.writeln(' }, (${option_name}*)&${tmp_var}, sizeof(${base_type}));')
28762884
g.write(tmp)

0 commit comments

Comments
 (0)