-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent use of stale pointers in item_location #31796
Prevent use of stale pointers in item_location #31796
Conversation
Previously the implementations in impl were essentially the ones for impl::nowhere. Move those to nowhere specifically, and have pure versions for impl. Also, add some debugmsg warnings for misuse of the nowhere locations.
This is a way to keep a reference to a object which is automatically invalidated if that object is destroyed or assigned-to.
This replaces the item* therein and ensures that item_locations cannot end up using dangling pointers to items that no longer exist or have moved. Activities that try to do so should now cleanly segfault rather than cause potentially weird behaviour (like butchering non-corpses). As a side benefit, we get a much neater implementation of item_location::impl::valid().
This test failed before the changes to use safe_reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this, it would have been way messier if I was the one implementing it. Making a wrapper for the weak and shared pointer trick was a great idea.
I'm still thinking that we may not need the lazy loading, though you're right that it's super difficult to validate. As I pointed out earlier, it seems that vehicle item_locations already depend on the map being loaded to be successfully deserialized.
src/item_location.cpp
Outdated
@@ -81,50 +119,33 @@ class item_location::impl | |||
} | |||
|
|||
virtual tripoint position() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this and other functions in item_location::impl::nowhere
need to be marked as override
Jenkins Rebuild |
After CleverRaven#31796 item processing invalidates item_locations, which causes issues with all activities using item_locations. This commit rewrites active_item_cache to store a safe_reference<item> instead of an item_stack::iterator, removing the now unnecessary active_item_set and has() functions. This also fixes a bug with active item reording after processing introduced by CleverRaven#31406 and a minor bug with the submaps_with_active_items cache.
After CleverRaven#31796 item processing invalidates item_locations, which causes issues with all activities using item_locations. This commit rewrites active_item_cache to store a safe_reference<item> instead of an item_stack::iterator, removing the now unnecessary active_item_set and has() functions. This also fixes a bug with active item reordering after processing introduced by CleverRaven#31406 and a minor bug with the submaps_with_active_items cache.
* Rework active_item_cache to use safe references After #31796 item processing invalidates item_locations, which causes issues with all activities using item_locations. This commit rewrites active_item_cache to store a safe_reference<item> instead of an item_stack::iterator, removing the now unnecessary active_item_set and has() functions. This also fixes a bug with active item reordering after processing introduced by #31406 and a minor bug with the submaps_with_active_items cache. * Make changes from code review In particular increment the iterator properly in active_item_cache::get() * Remove redundant get() calls on safe references clang-tidy told me what to do
Summary
SUMMARY: Infrastructure "Prevent use of stale pointers in item_location, which has been a source of various strange bugs over the years"
Purpose of change
item_location
is used to store references to items in activities and various other contexts.It was primarily based on an
item*
member, which was quite unsafe as items could move or be destroyed mid-activity (especially resumable activities like butchering).You can see an example of the simplest sort of way this could go wrong in the
item_location_doesnt_return_stale_map_item
test I added on this PR.We need a safer way to keep track of items.
Describe the solution
Create a new
safe_reference
class template which automatically invalidates itself if a targeted object is destroyed or assigned to.Store a
safe_reference<item>
instead of a rawitem*
initem_location
.Incidentally, this makes checking the validity of
item_location
s much more performant than it used to be.Describe alternatives you've considered
This
safe_reference
is built onshared_ptr
, but using it in a somewhat unorthodox way. The more normal thing to have done would be to store allitem
s usingshared_ptr
s, but I don't think that's actually a good idea, and it would heave required massive surgery to the entire codebase, whereas this is a minimal change.Additional context
With this change, I believe
item_location
is now memory-safe.However, it's still not doing the right thing in all cases.
It can end up pointing to the wrong item through a save-load cycle because of the lazy loading used after load. To fix that we need to ensure all
item_location
objects get unpacked before any items move after game load. @ifreund suggested that we don't need lazy loading at all, and we could just resolve all these items as theitem_location
s are deserialized. Given how widelyitem_location
is used, it would be hard to verify that that would be safe.Alternatively, we could do a pass over all
item_location
objects after the game is loaded and unpack them all then. But even that probably isn't enough. Consider for example how to handle NPCs who are mid-activity when their map gets unloaded. How do we fix that all up properly when the map is reloaded? Because the NPC object isn't serialized but the map is.In any case, that's all out of scope for this PR.