From 97e894747f27f4076891b66421460465b486c2b7 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 06:24:06 +0300 Subject: [PATCH] cgen: fix invalid memory access printing union containing interface member (fixes #25953) --- vlib/v/gen/c/cgen.v | 12 ++++--- .../c/testdata/iface_method_order.c.must_have | 3 +- .../union_implementing_interface_test.v | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 281b49a97..710a1ecb9 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -11632,10 +11632,12 @@ fn (mut g Gen) interface_table() string { // msvc can not process `static struct x[0] = {};` methods_struct.writeln('${g.static_modifier}${methods_struct_name} ${interface_name}_name_table[1];') } else { + name_table_len := iname_table_length + 1 if g.pref.build_mode != .build_module { - methods_struct.writeln('${g.static_modifier}${methods_struct_name} ${interface_name}_name_table[${iname_table_length}] = {') + methods_struct.writeln('${g.static_modifier}${methods_struct_name} ${interface_name}_name_table[${name_table_len}] = {') + methods_struct.writeln('\t{0},') } else { - methods_struct.writeln('${g.static_modifier}${methods_struct_name} ${interface_name}_name_table[${iname_table_length}];') + methods_struct.writeln('${g.static_modifier}${methods_struct_name} ${interface_name}_name_table[${name_table_len}];') } } mut cast_functions := strings.new_builder(100) @@ -11683,7 +11685,9 @@ fn (mut g Gen) interface_table() string { $if debug_interface_table ? { eprintln('>> interface name: ${isym.name} | concrete type: ${st.debug()} | st symname: ${st_sym.name}') } - // Speaker_Cat_index = 0 + // Reserve interface type index 0 as an invalid/uninitialized sentinel. + // That keeps stray bytes from overlapping storage, like unions, from + // aliasing a valid concrete interface variant. interface_index_name := '_${interface_name}_${cctype}_index' if already_generated_mwrappers[interface_index_name] > 0 { continue @@ -12021,7 +12025,7 @@ return ${cast_shared_struct_str}; if g.pref.build_mode != .build_module { methods_struct.writeln('\t},') } - iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base + iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base + 1 if g.pref.build_mode != .build_module { sb.writeln('${g.static_modifier}const u32 ${interface_index_name} = ${iin_idx};') } else { diff --git a/vlib/v/gen/c/testdata/iface_method_order.c.must_have b/vlib/v/gen/c/testdata/iface_method_order.c.must_have index b0540a9ad..a50639861 100644 --- a/vlib/v/gen/c/testdata/iface_method_order.c.must_have +++ b/vlib/v/gen/c/testdata/iface_method_order.c.must_have @@ -2,7 +2,8 @@ struct _main__Foo_interface_methods { void (*_method_a)(void* _); void (*_method_b)(void* _); }; -struct _main__Foo_interface_methods main__Foo_name_table[3] = { +struct _main__Foo_interface_methods main__Foo_name_table[4] = { +{0}, { ._method_a = main__Bar_a_Interface_main__Foo_method_adapter, ._method_b = main__Bar_b_Interface_main__Foo_method_adapter, diff --git a/vlib/v/tests/unions/union_implementing_interface_test.v b/vlib/v/tests/unions/union_implementing_interface_test.v index 00134d476..295b685bf 100644 --- a/vlib/v/tests/unions/union_implementing_interface_test.v +++ b/vlib/v/tests/unions/union_implementing_interface_test.v @@ -18,3 +18,36 @@ fn test_union_implementing_interface() { }) assert s.speak() == 'hi, u.vint: 123' } + +interface Any {} + +struct Thing {} + +union Bad { + f f64 + a Any +} + +union BadBits { + f f64 + bad Bad +} + +fn bad_from_float(x f64) Bad { + bits := BadBits{ + f: x + } + return unsafe { bits.bad } +} + +fn test_union_str_with_invalid_interface_member_does_not_crash() { + x := [bad_from_float(2.0)] + assert x.str() == '[Bad{\n f: 2.0\n a: unknown interface value\n}]' +} + +fn test_union_str_with_valid_interface_member_still_works() { + x := Bad{ + a: Thing{} + } + assert x.str().contains('a: Any(Thing{})') +} -- 2.39.5