Skip to content

Commit 016cf8f

Browse files
authored
Bounds checking bounds-safe interfaces in unchecked scopes (#1169)
* Add LValuesAssignedChecked member to CheckedState LValuesAssignedChecked contains AbstractSets representing lvalues expressions that have unchecked pointer type that were assigned a checked pointer during the current top-level statement (if the statement is in an unchecked scope). AbstractSets in LValuesAssignedChecked should have their bounds validated after checking the current statement. * Add SkipBoundsValidation method * Remove expected error for lvalue with a bounds-safe interface in an unchecked scope * Put declaration of short int with declared bounds in a checked scope so it results in a warning * Update unchecked pointer inverse test so there is a checked pointer with declared bounds * Add tests for validating the bounds of an unchecked pointer with a bounds-safe interface * Add test for assigning a checked array to p * Add tests for an integer-typed variable with declared bounds * Fix expected warning to work on both Windows and Linux * Fix typo in comment
1 parent 2532482 commit 016cf8f

File tree

4 files changed

+137
-8
lines changed

4 files changed

+137
-8
lines changed

clang/lib/Sema/SemaBounds.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,13 +579,25 @@ namespace {
579579
// then be used to validate the bounds context.
580580
llvm::DenseMap<Expr *, Expr *> TargetSrcEquality;
581581

582+
// LValuesAssignedChecked is a set of AbstractSets containing lvalue
583+
// expressions with unchecked pointer type that have been assigned an
584+
// expression with checked pointer type at some point during the current
585+
// top-level statement (if the statement occurs in an unchecked scope).
586+
// These AbstractSets should have their bounds validated during
587+
// ValidateBoundsContext. In an unchecked scope, AbstractSets containing
588+
// lvalue expressions with unchecked pointer type that have not been
589+
// assigned an expression with checked pointer type during the current
590+
// statement should not have their bounds validated.
591+
AbstractSetSetTy LValuesAssignedChecked;
592+
582593
// Resets the checking state after checking a top-level CFG statement.
583594
void Reset() {
584595
SameValue.clear();
585596
LostLValues.clear();
586597
UnknownSrcBounds.clear();
587598
BlameAssignments.clear();
588599
TargetSrcEquality.clear();
600+
LValuesAssignedChecked.clear();
589601
}
590602
};
591603
}
@@ -4429,6 +4441,8 @@ namespace {
44294441
this->S.GetLValueDeclaredBounds(A->GetRepresentative(), CSS);
44304442
if (!DeclaredBounds || DeclaredBounds->isUnknown())
44314443
continue;
4444+
if (SkipBoundsValidation(A, CSS, State))
4445+
continue;
44324446
if (ObservedBounds->isUnknown())
44334447
DiagnoseUnknownObservedBounds(S, A, DeclaredBounds, State);
44344448
else {
@@ -4447,6 +4461,33 @@ namespace {
44474461
}
44484462
}
44494463

4464+
// SkipBoundsValidation returns true if the observed bounds of A should
4465+
// not be validated after the current top-level statement.
4466+
//
4467+
// The bounds of A should not be validated if the current statement is
4468+
// in an unchecked scope, and A represents lvalue expressions that:
4469+
// 1. Have unchecked type (i.e. their declared bounds were specified using
4470+
// a bounds-safe interface), and:
4471+
// 2. Were not assigned a checked pointer at any point in the current
4472+
// top-level statement.
4473+
bool SkipBoundsValidation(const AbstractSet *A, CheckedScopeSpecifier CSS,
4474+
CheckingState State) {
4475+
if (CSS != CheckedScopeSpecifier::CSS_Unchecked)
4476+
return false;
4477+
4478+
if (A->GetRepresentative()->getType()->isCheckedPointerType() ||
4479+
A->GetRepresentative()->getType()->isCheckedArrayType())
4480+
return false;
4481+
4482+
// State.LValuesAssignedChecked contains AbstractSets with unchecked type
4483+
// that were assigned a checked pointer at some point in the current
4484+
// statement. If A belongs to this set, we must validate the bounds of A.
4485+
if (State.LValuesAssignedChecked.contains(A))
4486+
return false;
4487+
4488+
return true;
4489+
}
4490+
44504491
// DiagnoseUnknownObservedBounds emits an error message for an AbstractSet
44514492
// A whose observed bounds are unknown after checking the top-level CFG
44524493
// statement St.
@@ -4822,6 +4863,21 @@ namespace {
48224863
if (HasTargetBounds) {
48234864
LValueAbstractSet = AbstractSetMgr.GetOrCreateAbstractSet(LValue);
48244865
State.ObservedBounds[LValueAbstractSet] = SrcBounds;
4866+
4867+
// In an unchecked scope, if an expression with checked pointer type
4868+
// is assigned to an unchecked pointer LValue (whose bounds are
4869+
// declared via a bounds-safe interface), bounds validation should
4870+
// validate the bounds of LValue after the current top-level statement.
4871+
// For unchecked pointer LValues that were not assigned a checked
4872+
// pointer expression during the current top-level statement, bounds
4873+
// validation should skip validating the bounds of LValue.
4874+
if (CSS == CheckedScopeSpecifier::CSS_Unchecked) {
4875+
if (!LValue->getType()->isCheckedPointerType() &&
4876+
!LValue->getType()->isCheckedArrayType()) {
4877+
if (IsBoundsSafeInterfaceAssignment(LValue->getType(), Src))
4878+
State.LValuesAssignedChecked.insert(LValueAbstractSet);
4879+
}
4880+
}
48254881
}
48264882

48274883
// If Src initially has unknown bounds (before making any lvalue

clang/test/CheckedC/inferred-bounds/member-reference.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ int global_arr2 _Checked[5];
348348
void f12(struct Interop_S1 a1) {
349349
// TODO: need bundled block.
350350
a1.p = global_arr2;
351-
a1.len = 5; // expected-error {{inferred bounds for 'a1.p' are unknown after assignment}}
351+
a1.len = 5;
352352
}
353353

354354
// CHECK: BinaryOperator {{0x[0-9a-f]+}} {{.*}} 'int *' '='

clang/test/CheckedC/static-checking/bounds-decl-checking.c

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -760,9 +760,9 @@ void f91(_Array_ptr<int> p : count(1)) _Unchecked { // expected-note {{(expanded
760760
// expected-note {{(expanded) inferred bounds are 'bounds(p - 1, p - 1 + 1)'}}
761761
}
762762

763-
void f92(int *p : count(1)) _Unchecked { // expected-note {{(expanded) declared bounds are 'bounds(p, p + 1)'}}
764-
++p; // expected-warning {{cannot prove declared bounds for 'p' are valid after increment}} \
765-
// expected-note {{(expanded) inferred bounds are 'bounds(p - 1, p - 1 + 1)'}}
763+
_Unchecked void f92(_Array_ptr<int> p : bounds(q, q + 1), int *q) { // expected-note {{(expanded) declared bounds are 'bounds(q, q + 1)'}}
764+
++q; // expected-warning {{cannot prove declared bounds for 'p' are valid after increment}} \
765+
// expected-note {{(expanded) inferred bounds are 'bounds(q - 1, q - 1 + 1)'}}
766766
}
767767

768768
//
@@ -816,3 +816,73 @@ void f96(_Nt_array_ptr<char> p : bounds(p, (p + (len + 4)) + 4), int len) {
816816
// expected-note {{(expanded) declared bounds are 'bounds(v, (v + (1 + len)) + 2)'}} \
817817
// expected-note {{(expanded) inferred bounds are 'bounds(p, (p + (len + 4)) + 4)'}}
818818
}
819+
820+
//
821+
// Test validating bounds for unchecked pointer with bounds-safe interfaces
822+
// in unchecked and checked scopes.
823+
//
824+
825+
_Unchecked extern int *get_unchecked(_Array_ptr<int> a);
826+
extern _Array_ptr<int> get_checked(unsigned int i) : count(i);
827+
828+
_Unchecked void f97(int *p : count(i), // expected-note 8 {{(expanded) declared bounds are 'bounds(p, p + i)'}}
829+
int *q : bounds(unknown),
830+
_Array_ptr<int> r,
831+
unsigned int i) {
832+
// For statements where a checked pointer is not assigned to p, do not
833+
// validate the bounds of p.
834+
i = 0;
835+
p++;
836+
p = q;
837+
q = r, p = q;
838+
p = get_unchecked(r);
839+
p += i;
840+
841+
// The type of the RHS expression p - (_Array_ptr<int>)q is int *, so a
842+
// checked pointer is not assigned to p here.
843+
p -= (_Array_ptr<int>)q; // expected-warning {{incompatible integer to pointer conversion assigning to 'int *'}}
844+
845+
// For statements where a checked pointer is assigned to p, validate the
846+
// bounds of p.
847+
p = (_Array_ptr<int>)(p - q); // expected-error {{inferred bounds for 'p' are unknown after assignment}} \
848+
// expected-note {{assigned expression '(_Array_ptr<int>)(p - q)' with unknown bounds to 'p'}}
849+
850+
p = r; // expected-error {{inferred bounds for 'p' are unknown after assignment}} \
851+
// expected-note {{assigned expression 'r' with unknown bounds to 'p'}}
852+
853+
p = (_Array_ptr<int>)q; // expected-error {{inferred bounds for 'p' are unknown after assignment}} \
854+
// expected-note {{assigned expression '(_Array_ptr<int>)q' with unknown bounds to 'p'}}
855+
856+
p = get_checked(i), i++; // expected-warning {{cannot prove declared bounds for 'p' are valid after increment}} \
857+
// expected-note {{(expanded) inferred bounds are 'bounds(value of get_checked(i), value of get_checked(i) + i - 1U)'}}
858+
859+
p = (_Array_ptr<int>)p, --p; // expected-warning {{cannot prove declared bounds for 'p' are valid after decrement}} \
860+
// expected-note {{'bounds((int *)p + 1, (int *)p + 1 + i)'}}
861+
862+
int arr _Checked[3] : count(3) = {0, 1, 2};
863+
p = arr; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after assignment}} \
864+
// expected-note {{the declared upper bounds use the variable 'i' and there is no relational information involving 'i' and any of the expressions used by the inferred upper bounds}} \
865+
// expected-note {{(expanded) inferred bounds are 'bounds(arr, arr + 3)'}}
866+
867+
// In a checked scope, always validate the bounds of p.
868+
_Checked {
869+
i = 1; // expected-error {{inferred bounds for 'p' are unknown after assignment}} \
870+
// expected-note {{lost the value of the expression 'i' which is used in the (expanded) inferred bounds 'bounds(p, p + i)' of 'p'}}
871+
872+
++p; // expected-warning {{cannot prove declared bounds for 'p' are valid after increment}} \
873+
// expected-note {{(expanded) inferred bounds are 'bounds(p - 1, p - 1 + i)'}}
874+
}
875+
876+
// Non-pointer-typed values with declared bounds do not have their bounds
877+
// validated in unchecked scopes.
878+
short int t1 : byte_count(2) = (short int)arr; // expected-warning {{cast to smaller integer type 'short' from '_Array_ptr<int>'}}
879+
t1 = (short int)r; // expected-warning {{cast to smaller integer type 'short' from '_Array_ptr<int>'}}
880+
881+
// Non-pointer-typed values with declared bounds have their bounds
882+
// validated in checked scopes.
883+
_Checked {
884+
short int t2 : byte_count(2) = (short int)r; // expected-error {{inferred bounds for 't2' are unknown after initialization}} \
885+
// expected-note {{(expanded) declared bounds are 'bounds((_Array_ptr<char>)t2, (_Array_ptr<char>)t2 + 2)'}} \
886+
// expected-warning {{cast to smaller integer type 'short' from '_Array_ptr<int>'}}
887+
}
888+
}

clang/test/CheckedC/static-checking/free-variables.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,13 @@ void f3(struct S1 a3) {
133133
void f4(void) {
134134
int a checked[5];
135135
// Check that parentheses are correctly ignored.
136-
short int t1 : byte_count(5 * sizeof(int)) = ((((short int)(a)))); // expected-warning {{cast to smaller integer type 'short' from '_Array_ptr<int>'}} \
137-
// expected-warning {{cannot prove declared bounds for 't1' are valid after initialization}} \
138-
// expected-note {{(expanded) declared bounds are 'bounds((_Array_ptr<char>)t1, (_Array_ptr<char>)t1 + 5 * sizeof(int))'}} \
139-
// expected-note {{(expanded) inferred bounds are 'bounds(a, a + 5)'}}
136+
_Checked {
137+
short int t1 : byte_count(5 * sizeof(int)) = ((((short int)(a)))); // expected-warning {{cast to smaller integer type 'short' from '_Array_ptr<int>'}} \
138+
// expected-warning {{cannot prove declared bounds for 't1' are valid after initialization}} \
139+
// expected-note {{(expanded) declared bounds are 'bounds((_Array_ptr<char>)t1, (_Array_ptr<char>)t1 + 5 * sizeof(int))'}} \
140+
// expected-note {{(expanded) inferred bounds are 'bounds(a, a + 5)'}}
141+
}
142+
140143
array_ptr<int> t2 : byte_count(5 * sizeof(int)) = (((a)));
141144
array_ptr<int> t3 : byte_count(5 * sizeof(int)) = (((array_ptr<int>)(((a)))));
142145

0 commit comments

Comments
 (0)