Skip to content

Commit f2c1fb3

Browse files
malfetpytorchmergebot
authored andcommitted
Fix crash in SymInt unary minus (#116160)
Before this change `-SymInt(std::numeric_limits<int64_t>::min()) == 0` would reliably crash with null pointer dereference, as `data_` of the SymInt returned by `operator-` would be `0x8000000000000000`, because of the carry/overflow flags set by `negq`. Before the change x86_64 assembly generated for https://github.com/pytorch/pytorch/blob/4f02cc06706648799a33218711e62eae859d5927/c10/core/SymInt.cpp#L137 looked as follows: ``` 0x7ffff7f2f490 <+115>: movq %rax, %rdx 0x7ffff7f2f493 <+118>: negq %rdx 0x7ffff7f2f496 <+121>: movq %rdx, (%rbp) 0x7ffff7f2f49a <+125>: movabsq $0x4000000000000000, %rdx ; imm = 0x4000000000000000 0x7ffff7f2f4a4 <+135>: cmpq %rdx, %rax 0x7ffff7f2f4a7 <+138>: jle 0x7ffff7f2f520 ; <+259> at SymInt.cpp:141:1 ``` `negq %rfx` correspond to unary minus and `cmpq %rdx, 0x4000000000000000` are inverted `check_range` https://github.com/pytorch/pytorch/blob/b6d0d0819ae6099b2d659d03892e1e19c17e912b/c10/core/SymInt.h#L247-L249 Flags raised by `negq` will affect the results of `cmpq`, and as result value would not be allocated on heap, but rather preserved as `nullptr`. Not sure if it's worth benchmarking, but perhaps using `__builtin_sub_overflow` would be faster as it does not require an extra comparison, just guarantees that overflow flags is cleared after the op. Pull Request resolved: #116160 Approved by: https://github.com/Skylion007, https://github.com/colesbury
1 parent f8ad664 commit f2c1fb3

2 files changed

Lines changed: 16 additions & 1 deletion

File tree

c10/core/SymInt.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,13 @@ bool SymInt::expect_size(const char* file, int64_t line) const {
134134

135135
SymInt operator-(const SymInt& s) {
136136
if (auto ma = s.maybe_as_int()) {
137-
return SymInt(-*ma);
137+
const auto val = *ma;
138+
// Note: Result of `-std::numeric_limits<decltype(val)>::min()` is undefined
139+
// But on many platforms it equals to self + setting Carry/Overflow flags
140+
// Which in opimized code affects results of `check_range` condition
141+
// Workaround by using ternary that avoids alterning the flags
142+
constexpr auto val_min = std::numeric_limits<decltype(val)>::min();
143+
return SymInt(val != val_min ? -val : val_min);
138144
} else {
139145
return SymInt(s.toSymNodeImplUnowned()->neg());
140146
}

c10/test/core/SymInt_test.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,13 @@ TEST(SymIntTest, CheckRange) {
2222
EXPECT_FALSE(SymInt::check_range(INT64_MIN));
2323
}
2424

25+
TEST(SymIntTest, Overflows) {
26+
const auto x = SymInt(INT64_MAX);
27+
EXPECT_NE(-(x + 1), 0);
28+
29+
const auto y = SymInt(INT64_MIN);
30+
EXPECT_NE(-y, 0);
31+
EXPECT_NE(0 - y, 0);
32+
}
33+
2534
#endif

0 commit comments

Comments
 (0)