diff --git a/plugins/devel/check-structures-sanity.cpp b/plugins/devel/check-structures-sanity.cpp index 8a5ae8b2e..ece998fc5 100644 --- a/plugins/devel/check-structures-sanity.cpp +++ b/plugins/devel/check-structures-sanity.cpp @@ -151,6 +151,7 @@ private: void queue_union(const ToCheck &, const ToCheck &); void queue_union_vector(const ToCheck &, const ToCheck &); void queue_union_bitvector(const ToCheck &, const ToCheck &); + void queue_df_linked_list(const ToCheck &); void check_dispatch(ToCheck &); void check_global(const ToCheck &); void check_primitive(const ToCheck &); @@ -551,7 +552,10 @@ void Checker::queue_field(ToCheck && item, const struct_field_info *field) queue.push_back(std::move(item)); break; case struct_field_info::CONTAINER: - queue.push_back(std::move(item)); + if (field->type && field->type->type() == IDTYPE_STRUCT) + queue_df_linked_list(item); + else + queue.push_back(std::move(item)); break; case struct_field_info::STL_VECTOR_PTR: item.temp_identity = std::unique_ptr(new df::stl_ptr_vector_identity(field->type, field->eid)); @@ -751,6 +755,96 @@ void Checker::queue_union_bitvector(const ToCheck & item, const ToCheck & tag_it } } +void Checker::queue_df_linked_list(const ToCheck & item) +{ + if (item.identity->type() != IDTYPE_STRUCT) + { + UNEXPECTED; + return; + } + + const struct_field_info *prev_field = nullptr, *next_field = nullptr, *item_field = nullptr; + auto fields = static_cast(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; + void *prev_ptr = nullptr; + void *cur_ptr = item.ptr; + + while (cur_ptr) + { + auto cur_prev_ptr = *reinterpret_cast(PTR_ADD(cur_ptr, prev_field->offset)); + 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)); + return; + } + + auto item_ptr_ptr = PTR_ADD(cur_ptr, item_field->offset); + std::unique_ptr item_ptr_identity(new df::pointer_identity(item_field->type)); + 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); + queue.push_back(std::move(item_item)); + + auto next_ptr = *reinterpret_cast(PTR_ADD(cur_ptr, next_field->offset)); + ToCheck next_item(item, stl_sprintf("[%d].next", index), next_ptr, item.identity); + if (check_access(next_item, next_ptr, item.identity)) + { + prev_ptr = cur_ptr; + cur_ptr = next_ptr; + } + else + { + cur_ptr = nullptr; + } + + index++; + } +} + void Checker::check_dispatch(ToCheck & item) { if (reinterpret_cast(item.ptr) == UNINIT_PTR) @@ -1347,10 +1441,6 @@ void Checker::check_struct(const ToCheck & item) FAIL("allocated structure size (" << allocated_size << ") does not match expected size (" << expected_size << ")"); } } - else if (item.path.at(item.path.size() - 1) == "prev" || item.path.at(item.path.size() - 1) == "next") - { - // ignore unknown DfLinkedList sizes for now - } else { FAIL("unknown allocation size; possibly bad"); @@ -1373,6 +1463,12 @@ void Checker::check_struct(const ToCheck & item) continue; } + if (!strcmp(field->name, "link") && field->mode == struct_field_info::POINTER && field->type->getFullName() == item.identity->getFullName() + "_list_link") + { + // skip linked list pointers + continue; + } + ToCheck child(item, std::string(".") + field->name, PTR_ADD(item.ptr, field->offset), field->type); queue_field(std::move(child), field);