correctly handle arrays of linked lists

develop
Ben Lubar 2020-03-06 19:28:14 -06:00
parent 9b724666f7
commit 07aceb1078
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
1 changed files with 51 additions and 57 deletions

@ -73,6 +73,36 @@ static void build_size_table()
} }
} }
static bool is_df_linked_list(type_identity *type)
{
if (type->type() != IDTYPE_STRUCT)
return false;
auto struct_type = static_cast<struct_identity *>(type);
auto fields = struct_type->getFields();
if (fields[0].mode != struct_field_info::POINTER)
return false;
if (strcmp(fields[0].name, "item"))
return false;
if (fields[1].mode != struct_field_info::POINTER)
return false;
if (fields[1].type != type)
return false;
if (strcmp(fields[1].name, "prev"))
return false;
if (fields[2].mode != struct_field_info::POINTER)
return false;
if (fields[2].type != type)
return false;
if (strcmp(fields[2].name, "next"))
return false;
return fields[3].mode == struct_field_info::END;
}
static const char *const *get_enum_item_key(enum_identity *identity, int64_t value) static const char *const *get_enum_item_key(enum_identity *identity, int64_t value)
{ {
size_t index; size_t index;
@ -572,6 +602,7 @@ void Checker::queue_field(ToCheck && item, const struct_field_info *field)
void Checker::queue_static_array(const ToCheck & array, void *base, type_identity *type, size_t count, bool pointer, enum_identity *ienum) void Checker::queue_static_array(const ToCheck & array, void *base, type_identity *type, size_t count, bool pointer, enum_identity *ienum)
{ {
size_t size = pointer ? sizeof(void *) : type->byte_size(); size_t size = pointer ? sizeof(void *) : type->byte_size();
bool is_linked_list = type && is_df_linked_list(type);
for (size_t i = 0; i < count; i++, base = PTR_ADD(base, size)) for (size_t i = 0; i < count; i++, base = PTR_ADD(base, size))
{ {
@ -600,6 +631,11 @@ void Checker::queue_static_array(const ToCheck & array, void *base, type_identit
item.temp_identity = std::unique_ptr<pointer_identity>(new pointer_identity(type)); item.temp_identity = std::unique_ptr<pointer_identity>(new pointer_identity(type));
item.identity = item.temp_identity.get(); item.identity = item.temp_identity.get();
} }
else if (is_linked_list)
{
queue_df_linked_list(item);
continue;
}
queue.push_back(std::move(item)); queue.push_back(std::move(item));
} }
} }
@ -757,84 +793,42 @@ void Checker::queue_union_bitvector(const ToCheck & item, const ToCheck & tag_it
void Checker::queue_df_linked_list(const ToCheck & item) void Checker::queue_df_linked_list(const ToCheck & item)
{ {
if (item.identity->type() != IDTYPE_STRUCT) if (!is_df_linked_list(item.identity))
{ {
UNEXPECTED; UNEXPECTED;
return; return;
} }
const struct_field_info *prev_field = nullptr, *next_field = nullptr, *item_field = nullptr; auto item_type = static_cast<struct_identity *>(item.identity)->getFields()[2].type;
auto fields = static_cast<struct_identity *>(item.identity)->getFields();
for (auto field = fields; field->mode != struct_field_info::END; field++)
{
#define WANT_FIELD(fieldname, sametype) \
if (!strcmp(#fieldname, field->name)) \
{ \
if (field->mode != struct_field_info::POINTER) \
{ \
FAIL("internal error: expected field " #fieldname " to be a pointer"); \
UNEXPECTED; \
return; \
} \
if (sametype && field->type != item.identity) \
{ \
FAIL("internal error: expected field " #fieldname " to have a matching type"); \
UNEXPECTED; \
return; \
} \
if (fieldname ## _field) \
{ \
FAIL("internal error: duplicate field " #fieldname); \
UNEXPECTED; \
return; \
} \
fieldname ## _field = field; \
continue; \
}
WANT_FIELD(prev, true);
WANT_FIELD(next, true);
WANT_FIELD(item, false);
FAIL("internal error: unexpected extra field " << field->name);
UNEXPECTED;
return;
}
if (!prev_field || !next_field || !item_field)
{
FAIL("internal error: missing field(s) in DfLinkedList");
UNEXPECTED;
return;
}
// static verification of the type succeeded; continue to the actual list.
int index = -1; int index = -1;
void *prev_ptr = nullptr; struct df_linked_list_entry
void *cur_ptr = item.ptr; {
df_linked_list_entry *prev;
df_linked_list_entry *next;
void *item;
} *prev_ptr = nullptr, *cur_ptr = reinterpret_cast<df_linked_list_entry *>(item.ptr);
while (cur_ptr) while (cur_ptr)
{ {
auto cur_prev_ptr = *reinterpret_cast<void **>(PTR_ADD(cur_ptr, prev_field->offset)); if (prev_ptr != cur_ptr->prev)
if (prev_ptr != cur_prev_ptr)
{ {
FAIL("linked list element " << index << " previous element pointer " << stl_sprintf("%p", cur_prev_ptr) << " does not match actual previous element " << stl_sprintf("%p", prev_ptr)); FAIL("linked list element " << index << " previous element pointer " << stl_sprintf("%p", cur_ptr->prev) << " does not match actual previous element " << stl_sprintf("%p", prev_ptr));
return; return;
} }
auto item_ptr_ptr = PTR_ADD(cur_ptr, item_field->offset); auto item_ptr_ptr = reinterpret_cast<void *>(&cur_ptr->item);
std::unique_ptr<df::pointer_identity> item_ptr_identity(new df::pointer_identity(item_field->type)); std::unique_ptr<df::pointer_identity> item_ptr_identity(new df::pointer_identity(item_type));
ToCheck item_item(item, stl_sprintf("[%d].item", index), item_ptr_ptr, item_ptr_identity.get()); ToCheck item_item(item, stl_sprintf("[%d].item", index), item_ptr_ptr, item_ptr_identity.get());
item_item.temp_identity = std::move(item_ptr_identity); item_item.temp_identity = std::move(item_ptr_identity);
queue.push_back(std::move(item_item)); queue.push_back(std::move(item_item));
auto next_ptr = *reinterpret_cast<void **>(PTR_ADD(cur_ptr, next_field->offset)); auto next_ptr = reinterpret_cast<void *>(cur_ptr->next);
ToCheck next_item(item, stl_sprintf("[%d].next", index), next_ptr, item.identity); ToCheck next_item(item, stl_sprintf("[%d].next", index), next_ptr, item.identity);
if (check_access(next_item, next_ptr, item.identity)) if (check_access(next_item, next_ptr, item.identity))
{ {
prev_ptr = cur_ptr; prev_ptr = cur_ptr;
cur_ptr = next_ptr; cur_ptr = cur_ptr->next;
} }
else else
{ {
@ -1463,7 +1457,7 @@ void Checker::check_struct(const ToCheck & item)
continue; continue;
} }
if (!strcmp(field->name, "link") && field->mode == struct_field_info::POINTER && field->type->getFullName() == item.identity->getFullName() + "_list_link") if (field->mode == struct_field_info::POINTER && is_df_linked_list(field->type))
{ {
// skip linked list pointers // skip linked list pointers
continue; continue;