[vm, compiler] Avoid undefined behavior when tracking induction variables wraps around.
TEST=ubsan
Bug: https://github.com/dart-lang/sdk/issues/45511
Change-Id: Iaa5733dc048a811c87f479fa54fdb89bf64a0373
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193443
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index 788dd2a..a381505 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -292,6 +292,13 @@
return static_cast<T>(static_cast<Unsigned>(a) * static_cast<Unsigned>(b));
}
+ template <typename T = int64_t>
+ static inline T NegWithWrapAround(T a) {
+ // Avoid undefined behavior by doing arithmetic in the unsigned type.
+ using Unsigned = typename std::make_unsigned<T>::type;
+ return static_cast<T>(-static_cast<Unsigned>(a));
+ }
+
// Shifts int64_t value left. Supports any non-negative number of bits and
// silently discards shifted out bits.
static inline int64_t ShiftLeftWithTruncation(int64_t a, int64_t b) {
diff --git a/runtime/vm/compiler/backend/loops.cc b/runtime/vm/compiler/backend/loops.cc
index 4ff2dc8..f28ae39 100644
--- a/runtime/vm/compiler/backend/loops.cc
+++ b/runtime/vm/compiler/backend/loops.cc
@@ -723,13 +723,16 @@
// Invariant + Invariant : only for same or just one instruction.
if (x->def_ == y->def_) {
return new (zone_)
- InductionVar(x->offset_ + y->offset_, x->mult_ + y->mult_, x->def_);
+ InductionVar(Utils::AddWithWrapAround(x->offset_, y->offset_),
+ Utils::AddWithWrapAround(x->mult_, y->mult_), x->def_);
} else if (y->mult_ == 0) {
return new (zone_)
- InductionVar(x->offset_ + y->offset_, x->mult_, x->def_);
+ InductionVar(Utils::AddWithWrapAround(x->offset_, y->offset_),
+ x->mult_, x->def_);
} else if (x->mult_ == 0) {
return new (zone_)
- InductionVar(x->offset_ + y->offset_, y->mult_, y->def_);
+ InductionVar(Utils::AddWithWrapAround(x->offset_, y->offset_),
+ y->mult_, y->def_);
}
} else if (y != nullptr) {
// Invariant + Induction.
@@ -768,13 +771,16 @@
// Invariant + Invariant : only for same or just one instruction.
if (x->def_ == y->def_) {
return new (zone_)
- InductionVar(x->offset_ - y->offset_, x->mult_ - y->mult_, x->def_);
+ InductionVar(Utils::SubWithWrapAround(x->offset_, y->offset_),
+ Utils::SubWithWrapAround(x->mult_, y->mult_), x->def_);
} else if (y->mult_ == 0) {
return new (zone_)
- InductionVar(x->offset_ - y->offset_, x->mult_, x->def_);
+ InductionVar(Utils::SubWithWrapAround(x->offset_, y->offset_),
+ x->mult_, x->def_);
} else if (x->mult_ == 0) {
return new (zone_)
- InductionVar(x->offset_ - y->offset_, -y->mult_, y->def_);
+ InductionVar(Utils::SubWithWrapAround(x->offset_, y->offset_),
+ Utils::NegWithWrapAround(y->mult_), y->def_);
}
} else if (y != nullptr) {
// Invariant - Induction.
@@ -823,7 +829,8 @@
if (InductionVar::IsConstant(x) && y != nullptr) {
if (y->kind_ == InductionVar::kInvariant) {
return new (zone_)
- InductionVar(x->offset_ * y->offset_, x->offset_ * y->mult_, y->def_);
+ InductionVar(Utils::MulWithWrapAround(x->offset_, y->offset_),
+ Utils::MulWithWrapAround(x->offset_, y->mult_), y->def_);
}
return new (zone_)
InductionVar(y->kind_, Mul(x, y->initial_), Mul(x, y->next_));