Skip to content

Commit 4d6f59f

Browse files
authored
Implement restrictions on taking addresses of members and variables. (checkedc#490)
Checked C restricts taking the addresses of: 1. Members with member bounds declarations. 2. Members used in member bounds declarations. 3. Variables with bounds declarations. 4. Variables/variable members used in bounds declarations. This change implements restrictions 1 through 3. This addresses issue checkedc#213 and part of issue checkedc#212. - Taking the address of non-array members with or used in bounds declarations is now an error. - Taking the address of non-array members with or used in bounds-safe interfaces is allowed in unchecked scopes. It is an error in checked scopes. In unchecked scopes, we don't warn about this, even though it could go wrong. We don't want to add warnings for existing code. We could add an optional warning if we wanted to. - Taking the address of non-array variables with bounds declaration is now an error. It is OK to take the address of an array variable or member because you can't use the resulting pointer to modify the pointer that the array converts to. We recognize variations on these restrictions, such as taking the address of a parenthesized lvalue expression or taking the address of an expression where a bounds-safe interface cast has been inserted. This was more complex to implement than the specification describes because of the possible use of nested members. See the long comment describing the algorithm in CheckedCAlias.cpp. We handle a few different cases: - The simple case where a member bounds expression only uses members declared in the current structure. - A member bounds expression uses members from a nested structure of the current structure. In this case, we do not allow the address of the nested structure to be taken. Given ``` struct NestedLen { int len; }; struct S { struct NestedLen n; _Array_ptr<int> p : count(n.len); } ``` we don't allow the address of n to be taken. - A structure with bounds is embedded in another structure, and then an address of member used in member bounds is taken. We handle these cases by recognizing "paths" of member accesses and using that to recognize paths that may result in aliases to members used in bounds, and paths that won't result in aliases to members used in bounds. All of the benchmarks that we converted to Checked C compile with these restrictions in place. This is a step toward removing one of the caveats in the SecDev paper. We still need to restrict taking the address of variables and variable members used in bounds declarations to complete the aliasing restrictions. It'll be interesting to see what happens on real-world code. We could loosen some of the restrictions, for example, and allow taking the addresses involving constant-sized bounds (bounds that only involve the variable or member). I found and fixed a bug in the lexicographic comparison of declarations. We were supposed to be comparing pointers to names in declarations, and compared pointers to the declarations instead. We then tried dereferencing the pointers to names. Testing: - I've added a new file containing tests of address-of expressions in the Checked C repo. This will be subject to a separate pull request. - Passed clang tests on Linux x64. - Passed LNT tests on Linux x64. - Passed automated testing on Windows.
1 parent 76dc14d commit 4d6f59f

10 files changed

+470
-19
lines changed

include/clang/AST/ASTContext.h

+47-1
Original file line numberDiff line numberDiff line change
@@ -2479,7 +2479,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
24792479
void ResetObjCLayout(const ObjCContainerDecl *CD);
24802480

24812481
//===--------------------------------------------------------------------===//
2482-
// Predicates For Checked C checked types and bounds
2482+
// Predicates and methods for Checked C checked types and bounds
24832483
//===--------------------------------------------------------------------===//
24842484

24852485
/// \brief Determine whether a pointer, array, or function type T1 provides
@@ -2526,6 +2526,52 @@ class ASTContext : public RefCountedBase<ASTContext> {
25262526
BoundsExpr *getPrebuiltCountOne();
25272527
BoundsExpr *getPrebuiltBoundsUnknown();
25282528

2529+
// Track the set of member bounds declarations that use a given
2530+
// member path. For each member bounds declaration, we store the
2531+
// field with the declaration, not the member bound itself.
2532+
2533+
// Members are stored in reverse order. Given a.b.c, we store c.b.a
2534+
typedef SmallVector<const FieldDecl *,4> MemberPath;
2535+
struct PathCompare {
2536+
private:
2537+
Lexicographic Comparer;
2538+
public:
2539+
PathCompare(ASTContext &Context) : Comparer(Lexicographic(Context, nullptr)) {}
2540+
2541+
bool operator()(const MemberPath &P1, const MemberPath &P2) const {
2542+
if (P1.size() < P2.size())
2543+
return true;
2544+
if (P1.size() > P2.size())
2545+
return false;
2546+
for (int i = 0; i < P1.size(); i++) {
2547+
Lexicographic::Result R = Comparer.CompareDecl(P1[i],P2[i]);
2548+
if (R == Lexicographic::Result::LessThan)
2549+
return true;
2550+
if (R == Lexicographic::Result::GreaterThan)
2551+
return false;
2552+
}
2553+
return false;
2554+
}
2555+
};
2556+
2557+
typedef llvm::TinyPtrVector<const FieldDecl*> MemberDeclVector;
2558+
private:
2559+
std::map<MemberPath, MemberDeclVector, PathCompare> UsingBounds;
2560+
2561+
public:
2562+
typedef MemberDeclVector::const_iterator member_bounds_iterator;
2563+
member_bounds_iterator using_member_bounds_begin(const MemberPath &Path) const;
2564+
member_bounds_iterator using_member_bounds_end(const MemberPath &Path) const;
2565+
2566+
unsigned using_member_bounds_size(const MemberPath &Path) const;
2567+
typedef llvm::iterator_range<member_bounds_iterator> member_bounds_iterator_range;
2568+
member_bounds_iterator_range using_member_bounds(const MemberPath &Path) const;
2569+
2570+
/// \brief Note that \p MemberPath is used by the member bounds for
2571+
/// \p UsingBounds.
2572+
void addMemberBoundsUse(const MemberPath &MemberPath,
2573+
const FieldDecl *UsingBounds);
2574+
25292575
/// \brief Given an InteropTypeExpr pointer, return the interop type.
25302576
/// Adjust the type if the type is for a parameter. Return a null QualType
25312577
/// if the pointer is null.

include/clang/AST/CanonBounds.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ namespace clang {
6666
// they are not, return Current.
6767
Result CheckEquivExprs(Result Current, const Expr *E1, const Expr *E2);
6868

69-
Result CompareInteger(signed I1, signed I2);
70-
Result CompareInteger(unsigned I1, unsigned I2);
69+
Result CompareInteger(signed I1, signed I2) const;
70+
Result CompareInteger(unsigned I1, unsigned I2) const;
7171
Result CompareRelativeBoundsClause(const RelativeBoundsClause *RC1,
7272
const RelativeBoundsClause *RC2);
73-
Result CompareScope(const DeclContext *DC1, const DeclContext *DC2);
73+
Result CompareScope(const DeclContext *DC1, const DeclContext *DC2) const;
7474

7575
Result CompareImpl(const PredefinedExpr *E1, const PredefinedExpr *E2);
7676
Result CompareImpl(const DeclRefExpr *E1, const DeclRefExpr *E2);
@@ -112,9 +112,9 @@ namespace clang {
112112

113113
/// \brief Compare declarations that may be used by expressions or
114114
/// or types.
115-
Result CompareDecl(const NamedDecl *D1, const NamedDecl *D2);
116-
Result CompareType(QualType T1, QualType T2);
117-
Result CompareTypeIgnoreCheckedness(QualType QT1, QualType QT2);
115+
Result CompareDecl(const NamedDecl *D1, const NamedDecl *D2) const;
116+
Result CompareType(QualType T1, QualType T2) const;
117+
Result CompareTypeIgnoreCheckedness(QualType QT1, QualType QT2) const;
118118
};
119119
} // end namespace clang
120120

include/clang/Basic/DiagnosticSemaKinds.td

+9
Original file line numberDiff line numberDiff line change
@@ -9602,6 +9602,15 @@ def err_bounds_type_annotation_lost_checking : Error<
96029602
"|argument for parameter used in function return bounds expression"
96039603
"|argument for parameter used in function parameter bounds expression}1">;
96049604

9605+
def err_address_of_var_with_bounds : Error<"cannot take address of variable %0 with bounds">;
9606+
9607+
def err_address_of_member_in_bounds : Error<"cannot take address of member used in member bounds">;
9608+
9609+
def err_address_of_member_with_bounds : Error<"cannot take address of member with bounds">;
9610+
9611+
def note_var_bounds : Note<"bounds declared here">;
9612+
def note_member_bounds : Note<"member bounds declared here">;
9613+
96059614
def err_checked_scope_decl_type: Error<
96069615
"%select{parameter|return|local variable|global variable|member}0 %select{|used }1in a checked scope must have "
96079616
"%select{a |a pointer, array or function type that uses only |a bounds-safe interface type that uses only }2"

include/clang/Sema/Sema.h

+10
Original file line numberDiff line numberDiff line change
@@ -4649,6 +4649,11 @@ class Sema {
46494649

46504650
bool DiagnoseBoundsDeclType(QualType Ty, DeclaratorDecl *D,
46514651
BoundsAnnotations &BA, bool IsReturnAnnots);
4652+
4653+
/// \\brief Update information in ASTContext tracking for a member what
4654+
/// bounds declarations depend upon it. FD is the member whose
4655+
/// bounds are given by Bounds.
4656+
void TrackMemberBoundsDependences(FieldDecl *FD, BoundsExpr *Bounds);
46524657
void ActOnBoundsDecl(DeclaratorDecl *D, BoundsAnnotations Annots,
46534658
bool MergeDeferredBounds = false);
46544659

@@ -4770,6 +4775,10 @@ class Sema {
47704775
BDC_Initialization
47714776
};
47724777

4778+
/// \brief Check that address=of operation is not taking the
4779+
/// address of members used in bounds.
4780+
void CheckAddressTakenMembers(UnaryOperator *AddrOf);
4781+
47734782
/// CheckFunctionBodyBoundsDecls - check bounds declarations within a function
47744783
/// body.
47754784
void CheckFunctionBodyBoundsDecls(FunctionDecl *FD, Stmt *Body);
@@ -4779,6 +4788,7 @@ class Sema {
47794788
void CheckTopLevelBoundsDecls(VarDecl *VD);
47804789

47814790

4791+
47824792
// WarnDynamicCheckAlwaysFails - Adds a warning if an explicit dynamic check
47834793
// will always fail.
47844794
void WarnDynamicCheckAlwaysFails(const Expr *Condition);

lib/AST/ASTContext.cpp

+41-1
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
760760
CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),
761761
PrebuiltByteCountOne(nullptr), PrebuiltCountZero(nullptr),
762762
PrebuiltCountOne(nullptr), PrebuiltBoundsUnknown(nullptr),
763+
UsingBounds(PathCompare(*this)),
763764
LastSDM(nullptr, 0) {
764765
TUDecl = TranslationUnitDecl::Create(*this);
765766
}
@@ -8780,7 +8781,7 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) {
87808781
}
87818782

87828783
//===--------------------------------------------------------------------===//
8783-
// Predicates For Checked C checked types and bounds
8784+
// Predicates and methods for Checked C checked types and bounds
87848785
//===--------------------------------------------------------------------===//
87858786

87868787
static bool lessThan(bool Self, bool Other) {
@@ -9149,6 +9150,45 @@ BoundsExpr *ASTContext::getPrebuiltBoundsUnknown() {
91499150
return ResultType;
91509151
}
91519152

9153+
//
9154+
// Methods for tracking which member bounds declarations use a member.
9155+
//
9156+
9157+
ASTContext::member_bounds_iterator
9158+
ASTContext::using_member_bounds_begin(const MemberPath &Path) const {
9159+
auto Pos = UsingBounds.find(Path);
9160+
if (Pos == UsingBounds.end())
9161+
return nullptr;
9162+
return Pos->second.begin();
9163+
}
9164+
9165+
ASTContext::member_bounds_iterator
9166+
ASTContext::using_member_bounds_end(const MemberPath &Path) const {
9167+
auto Pos = UsingBounds.find(Path);
9168+
if (Pos == UsingBounds.end())
9169+
return nullptr;
9170+
return Pos->second.end();
9171+
}
9172+
9173+
unsigned
9174+
ASTContext::using_member_bounds_size(const MemberPath &Path) const {
9175+
auto Pos = UsingBounds.find(Path);
9176+
if (Pos == UsingBounds.end())
9177+
return 0;
9178+
return Pos->second.size();
9179+
}
9180+
9181+
ASTContext::member_bounds_iterator_range
9182+
ASTContext::using_member_bounds(const MemberPath &Path) const {
9183+
return member_bounds_iterator_range(using_member_bounds_begin(Path),
9184+
using_member_bounds_end(Path));
9185+
}
9186+
9187+
void ASTContext::addMemberBoundsUse(const MemberPath &Path,
9188+
const FieldDecl *Bounds) {
9189+
UsingBounds[Path].push_back(Bounds);
9190+
}
9191+
91529192
//===----------------------------------------------------------------------===//
91539193
// Integer Predicates
91549194
//===----------------------------------------------------------------------===//

lib/AST/CanonBounds.cpp

+13-11
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ Lexicographic::Lexicographic(ASTContext &Ctx, EquivExprSets *EquivExprs) :
140140
Context(Ctx), EquivExprs(EquivExprs), Trace(false) {
141141
}
142142

143-
Result Lexicographic::CompareInteger(signed I1, signed I2) {
143+
Result Lexicographic::CompareInteger(signed I1, signed I2) const {
144144
if (I1 < I2)
145145
return Result::LessThan;
146146
else if (I1 > I2)
@@ -149,7 +149,7 @@ Result Lexicographic::CompareInteger(signed I1, signed I2) {
149149
return Result::Equal;
150150
}
151151

152-
Result Lexicographic::CompareInteger(unsigned I1, unsigned I2) {
152+
Result Lexicographic::CompareInteger(unsigned I1, unsigned I2) const {
153153
if (I1 < I2)
154154
return Result::LessThan;
155155
else if (I1 > I2)
@@ -193,7 +193,7 @@ static Result ComparePointers(T *P1, T *P2, bool &ordered) {
193193
}
194194

195195
Result
196-
Lexicographic::CompareScope(const DeclContext *DC1, const DeclContext *DC2) {
196+
Lexicographic::CompareScope(const DeclContext *DC1, const DeclContext *DC2) const {
197197
DC1 = DC1->getPrimaryContext();
198198
DC2 = DC2->getPrimaryContext();
199199

@@ -220,7 +220,7 @@ Lexicographic::CompareScope(const DeclContext *DC1, const DeclContext *DC2) {
220220
}
221221

222222
Result
223-
Lexicographic::CompareDecl(const NamedDecl *D1Arg, const NamedDecl *D2Arg) {
223+
Lexicographic::CompareDecl(const NamedDecl *D1Arg, const NamedDecl *D2Arg) const {
224224
const NamedDecl *D1 = dyn_cast<NamedDecl>(D1Arg->getCanonicalDecl());
225225
const NamedDecl *D2 = dyn_cast<NamedDecl>(D2Arg->getCanonicalDecl());
226226
if (D1 == D2)
@@ -259,14 +259,16 @@ Lexicographic::CompareDecl(const NamedDecl *D1Arg, const NamedDecl *D2Arg) {
259259
const IdentifierInfo *Name2 = D2->getIdentifier();
260260

261261
bool ordered;
262-
Cmp = ComparePointers(D1, D2, ordered);
262+
Cmp = ComparePointers(Name1, Name2, ordered);
263263
if (ordered && Cmp != Result::Equal)
264264
return Cmp;
265-
assert(Name1 && Name2);
265+
assert((Name1 != nullptr) == (Name2 != nullptr));
266266

267-
Cmp = TranslateInt(Name1->getName().compare(Name2->getName()));
268-
if (Cmp != Result::Equal)
269-
return Cmp;
267+
if (Name1) {
268+
Cmp = TranslateInt(Name1->getName().compare(Name2->getName()));
269+
if (Cmp != Result::Equal)
270+
return Cmp;
271+
}
270272

271273
// They are canonical declarations that have the same kind and the same name, but are not
272274
// the same declaration.
@@ -494,7 +496,7 @@ Result Lexicographic::CheckEquivExprs(Result Current, const Expr *E1, const Expr
494496

495497

496498
Result
497-
Lexicographic::CompareType(QualType QT1, QualType QT2) {
499+
Lexicographic::CompareType(QualType QT1, QualType QT2) const {
498500
QT1 = QT1.getCanonicalType();
499501
QT2 = QT2.getCanonicalType();
500502
if (QT1 == QT2)
@@ -505,7 +507,7 @@ Lexicographic::CompareType(QualType QT1, QualType QT2) {
505507
}
506508

507509
Result
508-
Lexicographic::CompareTypeIgnoreCheckedness(QualType QT1, QualType QT2) {
510+
Lexicographic::CompareTypeIgnoreCheckedness(QualType QT1, QualType QT2) const {
509511
QT1 = QT1.getCanonicalType();
510512
QT2 = QT2.getCanonicalType();
511513
if (Context.isEqualIgnoringChecked(QT1, QT2))

lib/Sema/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ endif()
1111
add_clang_library(clangSema
1212
AnalysisBasedWarnings.cpp
1313
AttributeList.cpp
14+
CheckedCAlias.cpp
1415
CheckedCInterop.cpp
1516
CodeCompleteConsumer.cpp
1617
DeclSpec.cpp

0 commit comments

Comments
 (0)