Skip to content

Commit d936f59

Browse files
ezbrcopybara-github
authored andcommitted
Move dereferences that crash when the iterator is invalid into the new CrashIfIteratorIsInvalid function in order to give more helpful stack traces.
PiperOrigin-RevId: 915656842 Change-Id: I8542ce08c37d44f35a85e666eaf7eccd46132aef
1 parent c20be0a commit d936f59

2 files changed

Lines changed: 34 additions & 19 deletions

File tree

absl/container/internal/raw_hash_set.h

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,26 @@ constexpr bool SwisstableDebugEnabled() {
13791379
#endif
13801380
}
13811381

1382+
// Dereferences `ptr`. The function is named in order to provide a helpful error
1383+
// message when users see crashing stack traces. Note that this function is not
1384+
// guaranteed to crash when `ptr` is invalid if sanitizer mode is not enabled.
1385+
template <typename T>
1386+
T CrashIfIteratorIsInvalid(const T* ptr) {
1387+
// If the following line(s) crash, then it's likely that `ptr` is from a
1388+
// backing array that has been deallocated. If you see a crash here, it likely
1389+
// means that you are comparing an invalid iterator from a table that has
1390+
// rehashed, moved, or been destroyed. In such cases, it is often helpful to
1391+
// reproduce the issue with --config=asan and (assuming there's a crash here)
1392+
// examine the corresponding deallocation stack trace.
1393+
T ret = *ptr;
1394+
// Force a read with inline asm to make sure that a crash happens here, rather
1395+
// than later when the value is used.
1396+
#ifdef __clang__
1397+
asm("" : "+r"(ret));
1398+
#endif
1399+
return ret;
1400+
}
1401+
13821402
inline void AssertIsFull(const ctrl_t* ctrl, GenerationType generation,
13831403
const GenerationType* generation_ptr,
13841404
const char* operation) {
@@ -1396,20 +1416,21 @@ inline void AssertIsFull(const ctrl_t* ctrl, GenerationType generation,
13961416
operation);
13971417
}
13981418
if (SwisstableGenerationsEnabled()) {
1399-
if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) {
1419+
if (ABSL_PREDICT_FALSE(generation !=
1420+
CrashIfIteratorIsInvalid(generation_ptr))) {
14001421
ABSL_RAW_LOG(FATAL,
14011422
"%s called on invalid iterator. The table could have "
14021423
"rehashed or moved since this iterator was initialized.",
14031424
operation);
14041425
}
1405-
if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) {
1426+
if (ABSL_PREDICT_FALSE(!IsFull(CrashIfIteratorIsInvalid(ctrl)))) {
14061427
ABSL_RAW_LOG(
14071428
FATAL,
14081429
"%s called on invalid iterator. The element was likely erased.",
14091430
operation);
14101431
}
14111432
} else {
1412-
if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) {
1433+
if (ABSL_PREDICT_FALSE(!IsFull(CrashIfIteratorIsInvalid(ctrl)))) {
14131434
ABSL_RAW_LOG(
14141435
FATAL,
14151436
"%s called on invalid iterator. The element might have been erased "
@@ -1425,20 +1446,15 @@ inline void AssertIsValidForComparison(const ctrl_t* ctrl,
14251446
GenerationType generation,
14261447
const GenerationType* generation_ptr) {
14271448
if (!SwisstableDebugEnabled()) return;
1428-
const bool ctrl_is_valid_for_comparison = [ctrl]() {
1429-
if (ctrl == nullptr) return true;
1430-
if (ctrl == DefaultIterControl()) return true;
1431-
// Note: if the following line crashes, then it's likely that `ctrl` is from
1432-
// a backing array that has been deallocated. If you see a crash here, it
1433-
// likely means that you are comparing an invalid iterator from a table that
1434-
// has rehashed, moved, or been destroyed.
1435-
return IsFull(*ctrl);
1436-
}();
1449+
const bool ctrl_is_valid_for_comparison =
1450+
ctrl == nullptr || ctrl == DefaultIterControl() ||
1451+
IsFull(CrashIfIteratorIsInvalid(ctrl));
14371452
if (SwisstableGenerationsEnabled()) {
1438-
if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) {
1453+
if (ABSL_PREDICT_FALSE(generation !=
1454+
CrashIfIteratorIsInvalid(generation_ptr))) {
14391455
// Note: in the case of a rehash, we would expect to see a sanitizer crash
1440-
// above when `ctrl` is dereferenced so this assertion will only catch
1441-
// moved table cases, unless we're using a custom allocator that does not
1456+
// in CrashIfIteratorIsInvalid so this assertion will only catch moved
1457+
// table cases, unless we're using a custom allocator that does not
14421458
// deallocate the old backing array (e.g. an arena allocator).
14431459
ABSL_RAW_LOG(
14441460
FATAL,

absl/container/internal/raw_hash_set_test.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2922,12 +2922,11 @@ TEST(TableDeathTest, InvalidIteratorAssertsSoo) {
29222922
// the control is static constant.
29232923
}
29242924

2925-
// Invalid iterator use can trigger use-after-free in asan/hwasan,
2926-
// use-of-uninitialized-value in msan, or invalidated iterator assertions.
2925+
// Invalid iterator use can trigger crashes or invalidated iterator assertions.
29272926
testing::Matcher<const std::string&> InvalidIteratorMatcher() {
29282927
return AnyOf(HasSubstr("invalidated iterator"), HasSubstr("Invalid iterator"),
2929-
HasSubstr("invalid iterator"), HasSubstr("use-after-free"),
2930-
HasSubstr("use-of-uninitialized-value"));
2928+
HasSubstr("invalid iterator"),
2929+
HasSubstr("CrashIfIteratorIsInvalid"));
29312930
}
29322931

29332932
TYPED_TEST(SooTest, IteratorInvalidAssertsEqualityOperator) {

0 commit comments

Comments
 (0)