From ecbf92a58687d82de42e942eeb81c92835047f6d Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 29 Sep 2025 10:50:25 -0700 Subject: [PATCH] girnode: Fix computation of union member offsets Regression from 501ff95c. Union member offsets are always 0, but we need to mark them as 'COMPUTED' otherwise they are not written to the typelib, which results in gi_field_info_get_offset() returning 0xffff. Since gi_field_info_get/set_field() cannot check that the offset is within the size of the struct or union, that means poking into invalid memory addresses. This also adds some basic tests for GIFieldInfo which would have caught this bug. Closes: #3745 --- girepository/giroffsets.c | 2 + girepository/tests/field-info.c | 168 ++++++++++++++++++++++++++++++++ girepository/tests/meson.build | 3 + 3 files changed, 173 insertions(+) create mode 100644 girepository/tests/field-info.c diff --git a/girepository/giroffsets.c b/girepository/giroffsets.c index 60e6e341f..8a5b95c39 100644 --- a/girepository/giroffsets.c +++ b/girepository/giroffsets.c @@ -465,6 +465,8 @@ compute_union_field_offsets (GIIrTypelibBuild *build, { size = MAX (size, member_size); alignment = MAX (alignment, member_alignment); + field->offset = 0; + field->offset_state = GI_IR_OFFSETS_COMPUTED; } else have_error = TRUE; diff --git a/girepository/tests/field-info.c b/girepository/tests/field-info.c new file mode 100644 index 000000000..ffde8eff4 --- /dev/null +++ b/girepository/tests/field-info.c @@ -0,0 +1,168 @@ +/* + * Copyright 2025 Philip Chimento + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + */ + +#include "config.h" + +#include "girepository.h" +#include "glib.h" +#include "test-common.h" + +static void +test_basic_struct_field (RepositoryFixture *fx, + const void *unused) +{ + GIStructInfo *struct_info = NULL; + GIFieldInfo *field_info = NULL; + GITypeInfo *type_info = NULL; + GIFieldInfoFlags flags; + + g_test_summary ("Test basic properties of a GIFieldInfo from a C struct"); + + struct_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GLib", "DebugKey")); + g_assert_nonnull (struct_info); + + field_info = gi_struct_info_get_field (struct_info, 0); + g_assert_true (GI_IS_FIELD_INFO (field_info)); + g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (field_info)), ==, "key"); + + flags = gi_field_info_get_flags (field_info); + g_assert_cmpuint (flags, ==, GI_FIELD_IS_READABLE | GI_FIELD_IS_WRITABLE); + + /* Guaranteed across platforms, because it's the first field */ + g_assert_cmpuint (gi_field_info_get_offset (field_info), ==, 0); + + type_info = gi_field_info_get_type_info (field_info); + g_assert_cmpuint (gi_type_info_get_tag (type_info), ==, GI_TYPE_TAG_UTF8); + g_assert_true (gi_type_info_is_pointer (type_info)); + + g_clear_pointer (&type_info, gi_base_info_unref); + g_clear_pointer (&field_info, gi_base_info_unref); + g_clear_pointer (&struct_info, gi_base_info_unref); +} + +static void +test_basic_union_field (RepositoryFixture *fx, + const void *unused) +{ + GIUnionInfo *union_info = NULL; + GIFieldInfo *field_info = NULL; + GITypeInfo *type_info = NULL; + GIFieldInfoFlags flags; + + g_test_summary ("Test basic properties of a GIFieldInfo from a C union"); + + union_info = GI_UNION_INFO (gi_repository_find_by_name (fx->repository, "GLib", "DoubleIEEE754")); + g_assert_nonnull (union_info); + + field_info = gi_union_info_get_field (union_info, 0); + g_assert_true (GI_IS_FIELD_INFO (field_info)); + g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (field_info)), ==, "v_double"); + + flags = gi_field_info_get_flags (field_info); + g_assert_cmpuint (flags, ==, GI_FIELD_IS_READABLE | GI_FIELD_IS_WRITABLE); + + /* Guaranteed across platforms, because union offsets are always 0 */ + g_assert_cmpuint (gi_field_info_get_offset (field_info), ==, 0); + + type_info = gi_field_info_get_type_info (field_info); + g_assert_cmpuint (gi_type_info_get_tag (type_info), ==, GI_TYPE_TAG_DOUBLE); + g_assert_false (gi_type_info_is_pointer (type_info)); + + g_clear_pointer (&type_info, gi_base_info_unref); + g_clear_pointer (&field_info, gi_base_info_unref); + g_clear_pointer (&union_info, gi_base_info_unref); +} + +static void +test_read_write_struct_field (RepositoryFixture *fx, + const void *unused) +{ + GIStructInfo *struct_info = NULL; + GIFieldInfo *field_info = NULL; + GDebugKey instance; + GIArgument arg; + gboolean ok; + + g_test_summary ("Test reading and writing of a GIFieldInfo from a C union"); + + struct_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GLib", "DebugKey")); + g_assert_nonnull (struct_info); + + field_info = gi_struct_info_get_field (struct_info, 1); + g_assert_nonnull (field_info); + + instance.value = 0xfeed; + ok = gi_field_info_get_field (field_info, &instance, &arg); + g_assert_true (ok); + g_assert_cmpuint (arg.v_uint, ==, 0xfeed); + + arg.v_uint = 0x6502; + ok = gi_field_info_set_field (field_info, &instance, &arg); + g_assert_true (ok); + g_assert_cmpuint (instance.value, ==, 0x6502); + + g_clear_pointer (&field_info, gi_base_info_unref); + g_clear_pointer (&struct_info, gi_base_info_unref); +} + +static void +test_read_write_union_field (RepositoryFixture *fx, + const void *unused) +{ + GIUnionInfo *union_info = NULL; + GIFieldInfo *field_info = NULL; + GDoubleIEEE754 instance; + GIArgument arg; + gboolean ok; + + g_test_summary ("Test reading and writing of a GIFieldInfo from a C union"); + + union_info = GI_UNION_INFO (gi_repository_find_by_name (fx->repository, "GLib", "DoubleIEEE754")); + g_assert_nonnull (union_info); + + field_info = gi_union_info_get_field (union_info, 0); + g_assert_nonnull (field_info); + + instance.v_double = G_PI; + ok = gi_field_info_get_field (field_info, &instance, &arg); + g_assert_true (ok); + g_assert_cmpfloat (arg.v_double, ==, G_PI); + + arg.v_double = G_E; + ok = gi_field_info_set_field (field_info, &instance, &arg); + g_assert_true (ok); + g_assert_cmpfloat (instance.v_double, ==, G_E); + + g_clear_pointer (&field_info, gi_base_info_unref); + g_clear_pointer (&union_info, gi_base_info_unref); +} + +int +main (int argc, + char *argv[]) +{ + repository_init (&argc, &argv); + + ADD_REPOSITORY_TEST ("/field-info/basic-struct-field", test_basic_struct_field, &typelib_load_spec_glib); + ADD_REPOSITORY_TEST ("/field-info/basic-union-field", test_basic_union_field, &typelib_load_spec_glib); + ADD_REPOSITORY_TEST ("/field-info/read-write-struct-field", test_read_write_struct_field, &typelib_load_spec_glib); + ADD_REPOSITORY_TEST ("/field-info/read-write-union-field", test_read_write_union_field, &typelib_load_spec_glib); + + return g_test_run (); +} diff --git a/girepository/tests/meson.build b/girepository/tests/meson.build index 2a75aac57..b804346fb 100644 --- a/girepository/tests/meson.build +++ b/girepository/tests/meson.build @@ -46,6 +46,9 @@ if enable_gir 'callable-info' : { 'depends': gio_gir_testing_dep, }, + 'field-info' : { + 'depends': glib_gir_testing_dep, + }, 'function-info' : { 'dependencies': [libffi_dep], 'depends': glib_gir_testing_dep,