Skip to content

Fix self-move-assignment use-after-destroy in RE2::Set and FilteredRE2 - #654

Open
Alb3e3 wants to merge 1 commit into
google:mainfrom
Alb3e3:fix-self-move-assignment-615
Open

Fix self-move-assignment use-after-destroy in RE2::Set and FilteredRE2#654
Alb3e3 wants to merge 1 commit into
google:mainfrom
Alb3e3:fix-self-move-assignment-615

Conversation

@Alb3e3

@Alb3e3 Alb3e3 commented Aug 27, 2026

Copy link
Copy Markdown

Fixes #615.

RE2::Set::operator=(Set&&) and FilteredRE2::operator=(FilteredRE2&&) implement move-assignment with the destroy-then-placement-new idiom:

this->~Set();
(void) new (this) Set(std::move(other));

This is undefined behavior when this == &other (self-move-assignment): the destructor runs first and releases the object's owned regexes (Regexp::Decref() for Set, delete for FilteredRE2), and the move constructor then reads from the just-destroyed object. Self-move-assignment can arise through aliased references or library operations that are permitted to self-assign.

Observed effect: under libstdc++ the object is left destroyed and empty, so its compiled state is gone and Match()/AllMatches() silently returns wrong results; on other implementations the same UB can manifest as a double-free or crash.

Fix: add the standard self-assignment guard to both operators:

if (this == &other)
  return *this;

Tests: extend the existing Set.MoveSemantics and FilteredRE2Test.MoveSemantics with a self-move-assignment case, written through an aliased reference to avoid a -Wself-move diagnostic, asserting the object is unchanged and still usable. Without the guard these fail (the object is emptied); with it they pass.

RE2::Set::operator=(Set&&) and FilteredRE2::operator=(FilteredRE2&&)
implement move-assignment as `this->~T(); (void) new (this) T(std::move(other));`.
This is undefined behavior on self-move-assignment (this == &other): the
destructor releases the object's owned regexes (Regexp::Decref() for Set,
delete for FilteredRE2) and the move constructor then reads from the
just-destroyed object. Under libstdc++ the object is left empty, so
Match()/AllMatches() silently returns wrong results; on other
implementations the same UB can manifest as a double-free or crash.

Add the standard self-assignment guard to both operators, and extend the
existing Set.MoveSemantics and FilteredRE2Test.MoveSemantics tests with a
self-move-assignment case (via an aliased reference, to avoid a
-Wself-move diagnostic) that asserts the object is unchanged and usable.

Fixes google#615.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Self-move-assignment in RE2::Set and FilteredRE2 causes use-after-destroy

1 participant