Skip to content

Commit 55160e6

Browse files
authored
[ConstEval] Fix crash when comparing strings past the end (#137078)
When `ArePotentiallyOverlappingStringLiterals`, added in #109208, compares string literals it drops the front of the string with the greatest offset from its base pointer. The number of characters dropped is equal to the difference between the two strings' offsets from their base pointers. This would trigger an assert when the resulting offset is past the end of the object. Not only are one-past-the-end pointers legal constructs, the compiler should not crash even when faced with illegal constructs. rdar://149865910
1 parent 2397180 commit 55160e6

File tree

3 files changed

+18
-2
lines changed

3 files changed

+18
-2
lines changed

clang/lib/AST/ExprConstant.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,10 +2232,15 @@ static bool ArePotentiallyOverlappingStringLiterals(const EvalInfo &Info,
22322232
// within RHS. We don't need to look at the characters of one string that
22332233
// would appear before the start of the other string if they were merged.
22342234
CharUnits Offset = RHS.Offset - LHS.Offset;
2235-
if (Offset.isNegative())
2235+
if (Offset.isNegative()) {
2236+
if (LHSString.Bytes.size() < (size_t)-Offset.getQuantity())
2237+
return false;
22362238
LHSString.Bytes = LHSString.Bytes.drop_front(-Offset.getQuantity());
2237-
else
2239+
} else {
2240+
if (RHSString.Bytes.size() < (size_t)Offset.getQuantity())
2241+
return false;
22382242
RHSString.Bytes = RHSString.Bytes.drop_front(Offset.getQuantity());
2243+
}
22392244

22402245
bool LHSIsLonger = LHSString.Bytes.size() > RHSString.Bytes.size();
22412246
StringRef Longer = LHSIsLonger ? LHSString.Bytes : RHSString.Bytes;

clang/test/AST/ByteCode/cxx20.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ constexpr auto b3 = name1() == name1(); // ref-error {{must be initialized by a
119119
constexpr auto b4 = name1() == name2();
120120
static_assert(!b4);
121121

122+
constexpr auto bar(const char *p) { return p + __builtin_strlen(p); }
123+
constexpr auto b5 = bar(p1) == p1;
124+
static_assert(!b5);
125+
constexpr auto b6 = bar(p1) == ""; // ref-error {{must be initialized by a constant expression}} \
126+
// ref-note {{comparison of addresses of potentially overlapping literals}}
127+
constexpr auto b7 = bar(p1) + 1 == ""; // both-error {{must be initialized by a constant expression}} \
128+
// ref-note {{comparison against pointer '&"test1"[6]' that points past the end of a complete object has unspecified value}} \
129+
// expected-note {{comparison against pointer '&"test1"[6] + 1' that points past the end of a complete object has unspecified value}}
130+
122131
namespace UninitializedFields {
123132
class A {
124133
public:

clang/test/SemaCXX/constant-expression-cxx11.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,6 +2203,8 @@ namespace BuiltinStrlen {
22032203
static_assert(__builtin_strlen("foo") == 3, "");
22042204
static_assert(__builtin_strlen("foo\0quux") == 3, "");
22052205
static_assert(__builtin_strlen("foo\0quux" + 4) == 4, "");
2206+
static_assert(__builtin_strlen("foo") + 1 + "foo" == "foo", ""); // expected-error {{static assertion expression is not an integral constant expression}}
2207+
// expected-note@-1 {{comparison against pointer '&"foo"[4]' that points past the end of a complete object has unspecified value}}
22062208

22072209
constexpr bool check(const char *p) {
22082210
return __builtin_strlen(p) == 3 &&

0 commit comments

Comments
 (0)