https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125981
Bug ID: 125981
Summary: std::find_if/std::mismatch dereference the
past-the-end iterator when the predicate result type
has an ADL-reachable operator&&
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: yanchurkin at gmail dot com
Target Milestone: ---
std::__find_if (and similarly std::__mismatch and std::__push_heap)
drives its loop with a condition of the form
while (first != last && !pred(*first))
++first;
When the predicate/comparator returns a class type and the program
declares a namespace-scope operator&&(bool, T) reachable by ADL,
overload resolution selects that user operator&& for the loop
condition. An overloaded operator&& is not short-circuiting, so the
right-hand operand -- which dereferences *first -- is evaluated even
when first == last. For an empty range this dereferences the
past-the-end iterator.
Reproducer (last.cpp):
#include <algorithm>
#include <cstdint>
#include <list>
uint8_t value = 0;
struct logic_t {
logic_t operator!() { return logic_t(); }
operator bool() { return value; }
};
struct SVInt {
logic_t t;
logic_t operator>(SVInt) { return t; }
uint32_t bitWidth;
bool unknownFlag;
};
bool operator&&(bool, logic_t) { return 0; }
int main() {
std::list<SVInt> ranges;
(void) std::find_if(ranges.begin(), ranges.end(),
[](SVInt cur) { return cur > cur; });
}
Build and run:
g++ -fsanitize=address,undefined last.cpp && ./a.out
AddressSanitizer reports a stack-buffer-overflow READ inside
std::__find_if on the empty list (the past-the-end iterator of the
empty list is dereferenced).
Expected: find_if on an empty range must not dereference any iterator.
Older releases are unaffected because find_if used to wrap the
predicate in a helper whose operator() returned bool; the refactored
code passes the raw predicate through. The ranges:: versions are
unaffected because their comparator/predicate wrappers return bool.
A patch (force the predicate result to bool in these loop conditions)
Tested on x86_64-pc-linux-gnu, no regressions.