This is an archive of the discontinued LLVM Phabricator instance.

[ASan] Unpoisoning vectors memory before deallocation
AbandonedPublic

Authored by AdvenamTacet on Feb 15 2023, 6:42 PM.

Details

Reviewers
philnik
vitalybuka
Group Reviewers
Restricted Project
Summary

This revision fixes unpoisoning in std::vector.

A call to __annotate_delete() in operator() (in __destroy_vector class) is moved after a call to clear(), because clear may poison memory (as container overflow - fc).
It also adds unpoisoning memory before passing it to the deallocator in __vdeallocate() and __copy_assign_alloc(const vector& __c, true_type).

It guarantees that __alloc_traits::deallocate may access returned memory.

Change is motivated by false positives after committing D136765 (currently reverted):

Standard deallocator does not access the memory, so it does not produce an error, but a fix is necessary to turn on support for non-standard allocators.

Additionally, with that change, revision D136765 should not cause false positives, as returned memory will always be unpoisoned.

Diff Detail

Event Timeline

AdvenamTacet created this revision.Feb 15 2023, 6:42 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 15 2023, 6:42 PM
AdvenamTacet requested review of this revision.Feb 15 2023, 6:42 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 15 2023, 6:42 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript
AdvenamTacet retitled this revision from [ASan]Unpoisoning vectors memory before deallocation to [ASan] Unpoisoning vectors memory before deallocation.Feb 15 2023, 6:50 PM
AdvenamTacet added reviewers: philnik, vitalybuka.
AdvenamTacet added a project: Restricted Project.
AdvenamTacet added a subscriber: hans.
MaskRay added a subscriber: MaskRay.EditedFeb 15 2023, 10:41 PM

clear() calls __annotate_shrink(__old_size); which eventually calls __sanitizer_annotate_contiguous_container (with new size as 0). The inserted __annotate_delete calls __sanitizer_annotate_contiguous_container (with new size as the capacity) as well. Isn't it wasteful? Notably the second __sanitizer_annotate_contiguous_container call will unpoison the full capacity bytes.

The point of __annotate_delete is to return memory in the same state as it was just after allocation. And yes, clear() is poisoning memory of all present objects in a vector (at the end, whole buffer is poisoned as container overflow).

  1. Therefore, there is no point to unpoison memory before a call to clear.
  2. I don't think clear() is called there because of ASan, as from ASan point of view, container does not have to be empty.
  3. The goal is to deallocate unpoisoned memory and there is no easy way to turn off poisoning inside a single call to the clear function (at least I don't know it).

If complexity is the main concern, I can add additional if before every __annotate_delete() call, and don't call them with standard allocator, as in that implementation, it's not necessary. But it depends on memory deallocator implementation.
Also, that memory will be probably poisoned by deallocator soon after, but sometimes deallocator may access memory and then it cannot be poisoned.

AdvenamTacet added a subscriber: Restricted Project.Feb 16 2023, 12:16 PM
philnik requested changes to this revision.Feb 17 2023, 3:11 AM

I think this should be put in D136765 and not as a separate commit. Regression tests are also missing.

This revision now requires changes to proceed.Feb 17 2023, 3:11 AM
AdvenamTacet edited the summary of this revision. (Show Details)Feb 17 2023, 6:30 AM

Sure, I can add it there.

I created a new revision, because I considered it a separate issue, which should be fixed regardless of the support for more allocators.

Right now, __annotate_delete() in __destroy_vector class makes no sense (it unpoisons whole memory, and just after that, clear() poisons part of it), but the purpose of __annotate_delete(); is to unpoison memory before deallocation.
Missing __annotate_delete() in __vdeallocate() and __copy_assign_alloc(const vector& __c, true_type) makes all existing calls to __annotate_delete() pointless, as with todays implementation of default deallocator, everything should work without calling __annotate_delete() at all.

But I will add it in D136765 and I will add there a simple test with allocator which cleans the memory during allocation and deallocation.

AdvenamTacet abandoned this revision.Feb 19 2023, 8:24 PM