Skip to content

Commit bb1759e

Browse files
committed
[ConstEval] Fix crash when comparing strings past the end
When `ArePotentiallyOverlappingStringLiterals`, added in llvm#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 9c6eca2 commit bb1759e

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
@@ -2234,10 +2234,15 @@ static bool ArePotentiallyOverlappingStringLiterals(const EvalInfo &Info,
22342234
// within RHS. We don't need to look at the characters of one string that
22352235
// would appear before the start of the other string if they were merged.
22362236
CharUnits Offset = RHS.Offset - LHS.Offset;
2237-
if (Offset.isNegative())
2237+
if (Offset.isNegative()) {
2238+
if (LHSString.Bytes.size() < (size_t)-Offset.getQuantity())
2239+
return false;
22382240
LHSString.Bytes = LHSString.Bytes.drop_front(-Offset.getQuantity());
2239-
else
2241+
} else {
2242+
if (RHSString.Bytes.size() < (size_t)Offset.getQuantity())
2243+
return false;
22402244
RHSString.Bytes = RHSString.Bytes.drop_front(Offset.getQuantity());
2245+
}
22412246

22422247
bool LHSIsLonger = LHSString.Bytes.size() > RHSString.Bytes.size();
22432248
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
@@ -2199,6 +2199,8 @@ namespace BuiltinStrlen {
21992199
static_assert(__builtin_strlen("foo") == 3, "");
22002200
static_assert(__builtin_strlen("foo\0quux") == 3, "");
22012201
static_assert(__builtin_strlen("foo\0quux" + 4) == 4, "");
2202+
static_assert(__builtin_strlen("foo") + 1 + "foo" == "foo", ""); // expected-error {{static assertion expression is not an integral constant expression}}
2203+
// expected-note@-1 {{comparison against pointer '&"foo"[4]' that points past the end of a complete object has unspecified value}}
22022204

22032205
constexpr bool check(const char *p) {
22042206
return __builtin_strlen(p) == 3 &&

0 commit comments

Comments
 (0)