From ba4a92c53bcf7dcff630948b9c57ab72eb05d00b Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 6 Apr 2025 18:48:39 -0600 Subject: [PATCH] [test-object] Fix ubsan issues --- test/api/test-object.c | 426 ++++++++++++++++++++--------------------- 1 file changed, 209 insertions(+), 217 deletions(-) diff --git a/test/api/test-object.c b/test/api/test-object.c index 3689f83c9..dcc9fff57 100644 --- a/test/api/test-object.c +++ b/test/api/test-object.c @@ -38,7 +38,7 @@ create_blob (void) static void * create_blob_from_inert (void) { - return hb_blob_create (NULL, 0, HB_MEMORY_MODE_DUPLICATE, NULL, NULL); + return NULL; } static void * @@ -52,6 +52,17 @@ create_buffer_from_inert (void) return NULL; } +static void * +create_map (void) +{ + return hb_map_create (); +} +static void * +create_map_from_inert (void) +{ + return NULL; +} + static void * create_set (void) { @@ -102,6 +113,22 @@ create_font_funcs_from_inert (void) return NULL; } +static void * +create_shape_plan (void) +{ + hb_segment_properties_t props = HB_SEGMENT_PROPERTIES_DEFAULT; + props.direction = HB_DIRECTION_LTR; + hb_face_t *face = (hb_face_t *) create_face (); + hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, &props, NULL, 0, NULL); + hb_face_destroy (face); + return shape_plan; +} +static void * +create_shape_plan_from_inert (void) +{ + return NULL; +} + static void * create_unicode_funcs (void) { @@ -114,70 +141,6 @@ create_unicode_funcs_from_inert (void) } - -typedef void *(*create_func_t) (void); -typedef void *(*reference_func_t) (void *obj); -typedef void (*destroy_func_t) (void *obj); -typedef hb_bool_t (*set_user_data_func_t) (void *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy, hb_bool_t replace); -typedef void * (*get_user_data_func_t) (void *obj, hb_user_data_key_t *key); -typedef void (*make_immutable_func_t) (void *obj); -typedef hb_bool_t (*is_immutable_func_t) (void *obj); - -typedef struct { - create_func_t create; - create_func_t create_from_inert; - create_func_t get_empty; - reference_func_t reference; - destroy_func_t destroy; - set_user_data_func_t set_user_data; - get_user_data_func_t get_user_data; - make_immutable_func_t make_immutable; - is_immutable_func_t is_immutable; - const char *name; -} object_t; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-function-type" - -#define OBJECT_WITHOUT_IMMUTABILITY(name) \ - { \ - (create_func_t) create_##name, \ - (create_func_t) create_##name##_from_inert, \ - (create_func_t) hb_##name##_get_empty, \ - (reference_func_t) hb_##name##_reference, \ - (destroy_func_t) hb_##name##_destroy, \ - (set_user_data_func_t) hb_##name##_set_user_data, \ - (get_user_data_func_t) hb_##name##_get_user_data, \ - (make_immutable_func_t) NULL, \ - (is_immutable_func_t) NULL, \ - #name, \ - } -#define OBJECT_WITH_IMMUTABILITY(name) \ - { \ - (create_func_t) create_##name, \ - (create_func_t) create_##name##_from_inert, \ - (create_func_t) hb_##name##_get_empty, \ - (reference_func_t) hb_##name##_reference, \ - (destroy_func_t) hb_##name##_destroy, \ - (set_user_data_func_t) hb_##name##_set_user_data, \ - (get_user_data_func_t) hb_##name##_get_user_data, \ - (make_immutable_func_t) hb_##name##_make_immutable, \ - (is_immutable_func_t) hb_##name##_is_immutable, \ - #name, \ - } -static const object_t objects[] = -{ - OBJECT_WITHOUT_IMMUTABILITY (buffer), - OBJECT_WITHOUT_IMMUTABILITY (set), - OBJECT_WITH_IMMUTABILITY (blob), - OBJECT_WITH_IMMUTABILITY (face), - OBJECT_WITH_IMMUTABILITY (font), - OBJECT_WITH_IMMUTABILITY (font_funcs), - OBJECT_WITH_IMMUTABILITY (unicode_funcs) -}; -#undef OBJECT - - #define MAGIC0 0x12345678 #define MAGIC1 0x76543210 @@ -211,169 +174,198 @@ static void free_up1 (void *p) data->freed = TRUE; } - -typedef struct { - const object_t *klass; - void *object; - hb_user_data_key_t key; -} deadlock_test_t; - -static void free_deadlock_test (void *p) -{ - deadlock_test_t *t = (deadlock_test_t *) p; - - g_assert_true (NULL == t->klass->get_user_data (t->object, &t->key)); +#define OBJECT(name, _hb_object_make_immutable, _hb_object_is_immutable) \ +static void \ +test_object_##name (void) \ +{ \ + typedef hb_##name##_t type_t; \ + typedef void *(*create_func_t) (void); \ + typedef type_t *(*reference_func_t) (type_t *obj); \ + typedef void (*destroy_func_t) (type_t *obj); \ + typedef hb_bool_t (*set_user_data_func_t) (type_t *obj, hb_user_data_key_t *key, void *data, hb_destroy_func_t destroy, hb_bool_t replace); \ + typedef void * (*get_user_data_func_t) (type_t *obj, hb_user_data_key_t *key); \ + typedef void (*make_immutable_func_t) (type_t *obj); \ + typedef hb_bool_t (*is_immutable_func_t) (type_t *obj); \ + \ + struct object_t { \ + create_func_t create; \ + create_func_t create_from_inert; \ + create_func_t get_empty; \ + reference_func_t reference; \ + destroy_func_t destroy; \ + set_user_data_func_t set_user_data; \ + get_user_data_func_t get_user_data; \ + make_immutable_func_t make_immutable; \ + is_immutable_func_t is_immutable; \ + const char *name; \ + } o[1] = {{\ + (create_func_t) create_##name, \ + (create_func_t) create_##name##_from_inert, \ + (create_func_t) hb_##name##_get_empty, \ + (reference_func_t) hb_##name##_reference, \ + (destroy_func_t) hb_##name##_destroy, \ + (set_user_data_func_t) hb_##name##_set_user_data, \ + (get_user_data_func_t) hb_##name##_get_user_data, \ + (make_immutable_func_t) _hb_object_make_immutable, \ + (is_immutable_func_t) _hb_object_is_immutable, \ + #name, \ + }}; \ + \ + void *obj; \ + hb_user_data_key_t key[1001]; \ + \ + { \ + unsigned int j; \ + data_t data[1000] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; \ + \ + g_test_message ("Testing object %s", o->name); \ + \ + g_test_message ("->create()"); \ + obj = o->create (); \ + g_assert_true (obj); \ + \ + g_assert_true (obj == o->reference (obj)); \ + o->destroy (obj); \ + \ + if (o->is_immutable) \ + g_assert_true (!o->is_immutable (obj)); \ + \ + g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); \ + g_assert_true (o->get_user_data (obj, &key[0]) == &data[0]); \ + \ + if (o->is_immutable) { \ + o->make_immutable (obj); \ + g_assert_true (o->is_immutable (obj)); \ + } \ + \ + /* Should still work even if object is made immutable */ \ + g_assert_true (o->set_user_data (obj, &key[1], &data[1], free_up1, TRUE)); \ + g_assert_true (o->get_user_data (obj, &key[1]) == &data[1]); \ + \ + g_assert_true (!o->set_user_data (obj, NULL, &data[0], free_up0, TRUE)); \ + g_assert_true (o->get_user_data (obj, &key[0]) == &data[0]); \ + g_assert_true (o->set_user_data (obj, &key[0], &data[1], NULL, TRUE)); \ + g_assert_true (data[0].freed); \ + g_assert_true (o->get_user_data (obj, &key[0]) == &data[1]); \ + g_assert_true (!data[1].freed); \ + \ + data[0].freed = FALSE; \ + g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); \ + g_assert_true (!data[0].freed); \ + g_assert_true (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); \ + g_assert_true (data[0].freed); \ + \ + data[0].freed = FALSE; \ + global_data = 0; \ + g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); \ + g_assert_true (!o->set_user_data (obj, &key[0], &data[0], free_up0, FALSE)); \ + g_assert_cmpuint (global_data, ==, 0); \ + g_assert_true (o->set_user_data (obj, &key[0], NULL, global_free_up, TRUE)); \ + g_assert_cmpuint (global_data, ==, 0); \ + g_assert_true (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); \ + g_assert_cmpuint (global_data, ==, 1); \ + \ + global_data = 0; \ + for (j = 2; j < 1000; j++) \ + g_assert_true (o->set_user_data (obj, &key[j], &data[j], global_free_up, TRUE)); \ + for (j = 2; j < 1000; j++) \ + g_assert_true (o->get_user_data (obj, &key[j]) == &data[j]); \ + for (j = 100; j < 1000; j++) \ + g_assert_true (o->set_user_data (obj, &key[j], NULL, NULL, TRUE)); \ + for (j = 2; j < 100; j++) \ + g_assert_true (o->get_user_data (obj, &key[j]) == &data[j]); \ + for (j = 100; j < 1000; j++) \ + g_assert_true (!o->get_user_data (obj, &key[j])); \ + g_assert_cmpuint (global_data, ==, 900); \ + \ + g_assert_true (!data[1].freed); \ + o->destroy (obj); \ + g_assert_true (data[0].freed); \ + g_assert_true (data[1].freed); \ + g_assert_cmpuint (global_data, ==, 1000-2); \ + } \ + \ + { \ + data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; \ + \ + g_test_message ("->get_empty()"); \ + obj = o->get_empty (); \ + g_assert_true (obj); \ + \ + g_assert_true (obj == o->reference (obj)); \ + o->destroy (obj); \ + \ + if (o->is_immutable) \ + g_assert_true (o->is_immutable (obj)); \ + \ + g_assert_true (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); \ + g_assert_true (!o->get_user_data (obj, &key[0])); \ + \ + o->destroy (obj); \ + o->destroy (obj); \ + o->destroy (obj); \ + o->destroy (obj); \ + o->destroy (obj); \ + \ + g_assert_true (!data[0].freed); \ + } \ + \ + { \ + data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; \ + \ + g_test_message ("->create_from_inert()"); \ + obj = o->create_from_inert (); \ + if (!obj) \ + return; \ + g_assert_true (obj != o->get_empty ()); \ + \ + g_assert_true (obj == o->reference (obj)); \ + o->destroy (obj); \ + \ + if (o->is_immutable) \ + g_assert_true (!o->is_immutable (obj)); \ + \ + g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); \ + g_assert_true (o->get_user_data (obj, &key[0])); \ + \ + o->destroy (obj); \ + \ + g_assert_true (data[0].freed); \ + } \ } +#define OBJECT_WITH_IMMUTABILITY(name) \ + OBJECT (name, hb_##name##_make_immutable, hb_##name##_is_immutable) +#define OBJECT_WITHOUT_IMMUTABILITY(name) \ + OBJECT (name, NULL, NULL) -static void -test_object (void) -{ - unsigned int i; - for (i = 0; i < G_N_ELEMENTS (objects); i++) { - const object_t *o = &objects[i]; - void *obj; - hb_user_data_key_t key[1001]; +OBJECT_WITHOUT_IMMUTABILITY (buffer) +OBJECT_WITHOUT_IMMUTABILITY (map) +OBJECT_WITHOUT_IMMUTABILITY (set) +OBJECT_WITHOUT_IMMUTABILITY (shape_plan) - { - unsigned int j; - data_t data[1000] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; - deadlock_test_t deadlock_test; - - g_test_message ("Testing object %s", o->name); - - g_test_message ("->create()"); - obj = o->create (); - g_assert_true (obj); - - g_assert_true (obj == o->reference (obj)); - o->destroy (obj); - - if (o->is_immutable) - g_assert_true (!o->is_immutable (obj)); - - g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); - g_assert_true (o->get_user_data (obj, &key[0]) == &data[0]); - - if (o->is_immutable) { - o->make_immutable (obj); - g_assert_true (o->is_immutable (obj)); - } - - /* Should still work even if object is made immutable */ - g_assert_true (o->set_user_data (obj, &key[1], &data[1], free_up1, TRUE)); - g_assert_true (o->get_user_data (obj, &key[1]) == &data[1]); - - g_assert_true (!o->set_user_data (obj, NULL, &data[0], free_up0, TRUE)); - g_assert_true (o->get_user_data (obj, &key[0]) == &data[0]); - g_assert_true (o->set_user_data (obj, &key[0], &data[1], NULL, TRUE)); - g_assert_true (data[0].freed); - g_assert_true (o->get_user_data (obj, &key[0]) == &data[1]); - g_assert_true (!data[1].freed); - - data[0].freed = FALSE; - g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); - g_assert_true (!data[0].freed); - g_assert_true (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); - g_assert_true (data[0].freed); - - data[0].freed = FALSE; - global_data = 0; - g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); - g_assert_true (!o->set_user_data (obj, &key[0], &data[0], free_up0, FALSE)); - g_assert_cmpuint (global_data, ==, 0); - g_assert_true (o->set_user_data (obj, &key[0], NULL, global_free_up, TRUE)); - g_assert_cmpuint (global_data, ==, 0); - g_assert_true (o->set_user_data (obj, &key[0], NULL, NULL, TRUE)); - g_assert_cmpuint (global_data, ==, 1); - - global_data = 0; - for (j = 2; j < 1000; j++) - g_assert_true (o->set_user_data (obj, &key[j], &data[j], global_free_up, TRUE)); - for (j = 2; j < 1000; j++) - g_assert_true (o->get_user_data (obj, &key[j]) == &data[j]); - for (j = 100; j < 1000; j++) - g_assert_true (o->set_user_data (obj, &key[j], NULL, NULL, TRUE)); - for (j = 2; j < 100; j++) - g_assert_true (o->get_user_data (obj, &key[j]) == &data[j]); - for (j = 100; j < 1000; j++) - g_assert_true (!o->get_user_data (obj, &key[j])); - g_assert_cmpuint (global_data, ==, 900); - - /* Test set_user_data where the destroy() func calls user_data functions. - * Make sure it doesn't deadlock or corrupt memory. */ - deadlock_test.klass = o; - deadlock_test.object = obj; - g_assert_true (o->set_user_data (obj, &deadlock_test.key, &deadlock_test, free_deadlock_test, TRUE)); - g_assert_true (o->set_user_data (obj, &deadlock_test.key, NULL, NULL, TRUE)); - - g_assert_true (!data[1].freed); - o->destroy (obj); - g_assert_true (data[0].freed); - g_assert_true (data[1].freed); - g_assert_cmpuint (global_data, ==, 1000-2); - } - - { - data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; - - g_test_message ("->get_empty()"); - obj = o->get_empty (); - g_assert_true (obj); - - g_assert_true (obj == o->reference (obj)); - o->destroy (obj); - - if (o->is_immutable) - g_assert_true (o->is_immutable (obj)); - - g_assert_true (!o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); - g_assert_true (!o->get_user_data (obj, &key[0])); - - o->destroy (obj); - o->destroy (obj); - o->destroy (obj); - o->destroy (obj); - o->destroy (obj); - - g_assert_true (!data[0].freed); - } - - { - data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}}; - - g_test_message ("->create_from_inert()"); - obj = o->create_from_inert (); - if (!obj) - continue; - if (obj == o->get_empty ()) - continue; /* Tested already */ - - g_assert_true (obj == o->reference (obj)); - o->destroy (obj); - - if (o->is_immutable) - g_assert_true (!o->is_immutable (obj)); - - g_assert_true (o->set_user_data (obj, &key[0], &data[0], free_up0, TRUE)); - g_assert_true (o->get_user_data (obj, &key[0])); - - o->destroy (obj); - - g_assert_true (data[0].freed); - } - } -} - -#pragma GCC diagnostic pop +OBJECT_WITH_IMMUTABILITY (blob) +OBJECT_WITH_IMMUTABILITY (face) +OBJECT_WITH_IMMUTABILITY (font) +OBJECT_WITH_IMMUTABILITY (font_funcs) +OBJECT_WITH_IMMUTABILITY (unicode_funcs) int main (int argc, char **argv) { hb_test_init (&argc, &argv); - hb_test_add (test_object); + hb_test_add (test_object_buffer); + hb_test_add (test_object_map); + hb_test_add (test_object_shape_plan); + hb_test_add (test_object_set); + + hb_test_add (test_object_blob); + hb_test_add (test_object_face); + hb_test_add (test_object_font); + hb_test_add (test_object_font_funcs); + hb_test_add (test_object_unicode_funcs); return hb_test_run (); }