Skip to content

Commit 410dfd1

Browse files
committed
* Emit diagnostics when validation fails: null pointer, one-past-the-end,
out-of-bounds access. * Enhance test suite to cover more scenarios * Return true when Size is zero.
1 parent 9d2faf6 commit 410dfd1

File tree

3 files changed

+78
-29
lines changed

3 files changed

+78
-29
lines changed

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,12 +2222,22 @@ static bool interp__builtin_assume_dereferenceable(InterpState &S, CodePtr OpPC,
22222222
assert(Call->getNumArgs() == 2);
22232223

22242224
APSInt ReqSize = popToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1)));
2225-
if (ReqSize.getZExtValue() < 1)
2226-
return false;
2227-
22282225
const Pointer &Ptr = S.Stk.pop<Pointer>();
2229-
if (Ptr.isZero() || !Ptr.isLive() || !Ptr.isBlockPointer() || Ptr.isPastEnd())
2226+
2227+
if (ReqSize.isZero())
2228+
return true;
2229+
if (Ptr.isZero()) {
2230+
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null)
2231+
<< AK_Read << S.Current->getRange(OpPC);
22302232
return false;
2233+
}
2234+
if (!Ptr.isLive() || !Ptr.isBlockPointer())
2235+
return false;
2236+
if (Ptr.isPastEnd()) {
2237+
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end)
2238+
<< AK_Read << S.Current->getRange(OpPC);
2239+
return false;
2240+
}
22312241

22322242
const ASTContext &ASTCtx = S.getASTContext();
22332243
const Descriptor *DeclDesc = Ptr.getDeclDesc();
@@ -2239,9 +2249,12 @@ static bool interp__builtin_assume_dereferenceable(InterpState &S, CodePtr OpPC,
22392249
if (ByteOffset > *FullSize)
22402250
return false;
22412251

2242-
unsigned RemainingSpace = *FullSize - ByteOffset;
2243-
if (RemainingSpace < ReqSize.getZExtValue())
2252+
unsigned AvailSize = *FullSize - ByteOffset;
2253+
if (AvailSize < ReqSize.getZExtValue()) {
2254+
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end)
2255+
<< AK_Read << S.Current->getRange(OpPC);
22442256
return false;
2257+
}
22452258

22462259
return true;
22472260
}

clang/lib/AST/ExprConstant.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19697,24 +19697,32 @@ class VoidExprEvaluator
1969719697
APSInt ReqSizeVal;
1969819698
if (!::EvaluateInteger(E->getArg(1), ReqSizeVal, Info))
1969919699
return false;
19700+
if (ReqSizeVal.isZero())
19701+
return true;
19702+
1970019703
LValue Pointer;
1970119704
if (!EvaluatePointer(E->getArg(0), Pointer, Info))
1970219705
return false;
1970319706
if (Pointer.Designator.Invalid)
1970419707
return false;
19705-
if (Pointer.isNullPointer())
19708+
if (Pointer.isNullPointer()) {
19709+
Info.FFDiag(E, diag::note_constexpr_access_null) << AK_Read;
19710+
return false;
19711+
}
19712+
if (Pointer.Designator.isOnePastTheEnd()) {
19713+
Info.FFDiag(E, diag::note_constexpr_access_past_end) << AK_Read;
1970619714
return false;
19715+
}
1970719716

1970819717
uint64_t ReqSize = ReqSizeVal.getZExtValue();
19709-
if (ReqSize < 1)
19710-
return false;
1971119718
CharUnits EndOffset;
1971219719
if (!determineEndOffset(Info, E->getExprLoc(), 0, Pointer, EndOffset))
1971319720
return false;
1971419721

19715-
uint64_t TotalSize =
19722+
uint64_t AvailSize =
1971619723
(EndOffset - Pointer.getLValueOffset()).getQuantity();
19717-
if (TotalSize < ReqSize) {
19724+
if (AvailSize < ReqSize) {
19725+
Info.FFDiag(E, diag::note_constexpr_access_past_end) << AK_Read;
1971819726
return false;
1971919727
}
1972019728
return true;
Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,92 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -triple x86_64-unknown-unknown %s
22
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -triple x86_64-unknown-unknown %s -fexperimental-new-constant-interpreter
33

4-
constexpr int arr[10] = {};
5-
64
constexpr bool test_constexpr_valid() {
5+
constexpr int arr[10] = {};
76
__builtin_assume_dereferenceable(arr, 40);
87
return true;
98
}
109
static_assert(test_constexpr_valid(), "");
1110

1211
constexpr bool test_constexpr_partial() {
12+
constexpr int arr[10] = {};
1313
__builtin_assume_dereferenceable(&arr[5], 20);
1414
return true;
1515
}
1616
static_assert(test_constexpr_partial(), "");
1717

18-
constexpr bool test_constexpr_nullptr() {
19-
__builtin_assume_dereferenceable(nullptr, 4);
18+
constexpr bool test_constexpr_nullptr() { // expected-error {{constexpr function never produces a constant expression}}
19+
__builtin_assume_dereferenceable(nullptr, 4); // expected-note 2{{read of dereferenced null pointer is not allowed in a constant expression}}
2020
return true;
2121
}
22-
static_assert(test_constexpr_nullptr(), ""); // expected-error {{not an integral constant expression}}
22+
static_assert(test_constexpr_nullptr(), ""); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
2323

24-
constexpr bool test_constexpr_too_large() {
25-
__builtin_assume_dereferenceable(arr, 100);
24+
constexpr bool test_constexpr_too_large() { // expected-error {{constexpr function never produces a constant expression}}
25+
constexpr int arr[10] = {};
26+
__builtin_assume_dereferenceable(arr, 100); // expected-note 2{{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
2627
return true;
2728
}
28-
static_assert(test_constexpr_too_large(), ""); // expected-error {{not an integral constant expression}}
29+
static_assert(test_constexpr_too_large(), ""); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
2930

30-
constexpr int single_var = 42;
3131
constexpr bool test_single_var() {
32+
constexpr int single_var = 42;
3233
__builtin_assume_dereferenceable(&single_var, 4);
3334
return true;
3435
}
3536
static_assert(test_single_var(), "");
3637

3738
constexpr bool test_exact_boundary() {
39+
constexpr int arr[10] = {};
3840
__builtin_assume_dereferenceable(&arr[9], 4);
3941
return true;
4042
}
4143
static_assert(test_exact_boundary(), "");
4244

43-
constexpr bool test_one_over() {
44-
__builtin_assume_dereferenceable(&arr[9], 5);
45+
constexpr bool test_one_over() { // expected-error {{constexpr function never produces a constant expression}}
46+
constexpr int arr[10] = {};
47+
__builtin_assume_dereferenceable(&arr[9], 5); // expected-note 2{{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
4548
return true;
4649
}
47-
static_assert(test_one_over(), ""); // expected-error {{not an integral constant expression}}
50+
static_assert(test_one_over(), ""); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
4851

4952
constexpr bool test_zero_size() {
53+
constexpr int arr[10] = {};
5054
__builtin_assume_dereferenceable(arr, 0);
5155
return true;
5256
}
53-
static_assert(test_zero_size(), ""); // expected-error {{not an integral constant expression}}
57+
static_assert(test_zero_size(), "");
5458

55-
struct S {
56-
int x;
57-
int y;
58-
};
59-
constexpr S s = {1, 2};
6059
constexpr bool test_struct_member() {
60+
struct S {
61+
int x;
62+
int y;
63+
};
64+
constexpr S s = {1, 2};
6165
__builtin_assume_dereferenceable(&s.x, 4);
6266
return true;
6367
}
6468
static_assert(test_struct_member(), "");
69+
70+
constexpr bool test_range_valid() {
71+
constexpr int range_data[5] = {1, 2, 3, 4, 5};
72+
__builtin_assume_dereferenceable(range_data, 5 * sizeof(int));
73+
return range_data[0] == 1;
74+
}
75+
static_assert(test_range_valid(), "");
76+
77+
constexpr bool test_range_invalid() { // expected-error {{constexpr function never produces a constant expression}}
78+
constexpr int range_data[5] = {1, 2, 3, 4, 5};
79+
__builtin_assume_dereferenceable(range_data, 6 * sizeof(int)); // expected-note 2{{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
80+
return true;
81+
}
82+
static_assert(test_range_invalid(), ""); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
83+
84+
constexpr int arr1[10] = {};
85+
constexpr int valid = (__builtin_assume_dereferenceable(arr1, 40), 12);
86+
87+
constexpr int invalid = (__builtin_assume_dereferenceable((int*)123, 4), 12); // expected-error {{constexpr variable 'invalid' must be initialized by a constant expression}} expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
88+
89+
constexpr int arr2[5] = {1, 2, 3, 4, 5};
90+
constexpr int too_large = (__builtin_assume_dereferenceable(arr2, 6 * sizeof(int)), 12); // expected-error {{constexpr variable 'too_large' must be initialized by a constant expression}} expected-note {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
91+
92+
constexpr int null = (__builtin_assume_dereferenceable(nullptr, 4), 12); // expected-error {{constexpr variable 'null' must be initialized by a constant expression}} expected-note {{read of dereferenced null pointer is not allowed in a constant expression}}

0 commit comments

Comments
 (0)