mirror of
https://github.com/harfbuzz/harfbuzz.git
synced 2025-04-13 08:42:59 +00:00
[test-object] Fix ubsan issues
This commit is contained in:
parent
8a1ba4d151
commit
ba4a92c53b
1 changed files with 209 additions and 217 deletions
|
@ -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 ();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue