Skip to content

Commit 6277778

Browse files
eupharinaresistor
authored andcommitted
[CHERI_CSA] SubObjectRepresentability: disable notes for now
1 parent fdf3f22 commit 6277778

File tree

2 files changed

+170
-83
lines changed

2 files changed

+170
-83
lines changed

clang/lib/StaticAnalyzer/Checkers/CHERI/SubObjectRepresentabilityChecker.cpp

Lines changed: 165 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,135 @@ using namespace ento;
3131

3232
namespace {
3333
class SubObjectRepresentabilityChecker
34-
: public Checker<check::ASTDecl<RecordDecl>> {
34+
: public Checker<check::ASTDecl<RecordDecl>, check::ASTCodeBody> {
3535
BugType BT_1{this, "Field with imprecise subobject bounds",
3636
"CHERI portability"};
37+
BugType BT_2{this, "Address taken of a field with imprecise subobject bounds",
38+
"CHERI portability"};
3739

3840
public:
3941
void checkASTDecl(const RecordDecl *R, AnalysisManager &mgr,
4042
BugReporter &BR) const;
43+
void checkASTCodeBody(const Decl *D, AnalysisManager &mgr,
44+
BugReporter &BR) const;
4145
};
4246

4347
} // namespace
4448

49+
namespace {
50+
51+
std::unique_ptr<BasicBugReport>
52+
reportExposedFields(const FieldDecl *D, ASTContext &ASTCtx, BugReporter &BR,
53+
uint64_t Base, uint64_t Top,
54+
std::unique_ptr<BasicBugReport> Report) {
55+
const RecordDecl *Parent = D->getParent();
56+
auto FI = Parent->field_begin();
57+
while (FI != Parent->field_end() &&
58+
ASTCtx.getFieldOffset(*FI) / 8 +
59+
ASTCtx.getTypeSize(FI->getType()) / 8 <=
60+
Base)
61+
FI++;
62+
63+
bool Before = true;
64+
while (FI != Parent->field_end() && ASTCtx.getFieldOffset(*FI) / 8 < Top) {
65+
if (*FI != D) {
66+
SmallString<1024> Note;
67+
llvm::raw_svector_ostream OS2(Note);
68+
69+
uint64_t CurFieldOffset = ASTCtx.getFieldOffset(*FI) / 8;
70+
uint64_t CurFieldSize = ASTCtx.getTypeSize(FI->getType()) / 8;
71+
uint64_t BytesExposed =
72+
Before ? std::min(CurFieldSize, CurFieldOffset + CurFieldSize - Base)
73+
: std::min(CurFieldSize, Top - CurFieldOffset);
74+
OS2 << BytesExposed << "/" << CurFieldSize << " bytes exposed";
75+
if (cheri::hasCapability(FI->getType(), ASTCtx))
76+
OS2 << " (may expose capability!)";
77+
78+
PathDiagnosticLocation LN =
79+
PathDiagnosticLocation::createBegin(*FI, BR.getSourceManager());
80+
Report->addNote(OS2.str(), LN);
81+
} else
82+
Before = false;
83+
FI++;
84+
}
85+
return Report;
86+
}
87+
88+
#if 0
89+
// FIXME: CC_MORELLO is not set in cheri_compressed_cap
90+
// FIXME: other targets
91+
using Handler = CompressedCap128;
92+
Handler::cap_t getBoundedCap(uint64_t ParentSize, uint64_t Offset,
93+
uint64_t Size) {
94+
Handler::addr_t InitLength = Handler ::representable_length(ParentSize);
95+
Handler::cap_t MockCap = Handler::make_max_perms_cap(0, 0, InitLength);
96+
bool exact = Handler::setbounds(&MockCap, Offset, Offset + Size);
97+
assert(!exact);
98+
return MockCap;
99+
}
100+
#endif
101+
102+
} // namespace
103+
104+
std::unique_ptr<BugReport> checkField(const FieldDecl *D, AnalysisManager &mgr,
105+
BugReporter &BR, const BugType &BT) {
106+
QualType T = D->getType();
107+
108+
ASTContext &ASTCtx = BR.getContext();
109+
uint64_t Offset = ASTCtx.getFieldOffset(D) / 8;
110+
if (Offset > 0) {
111+
uint64_t Size = ASTCtx.getTypeSize(T) / 8;
112+
uint64_t ReqAlign = llvm::CompressedCapability::GetRequiredAlignment(
113+
Size, llvm::CompressedCapability::Cheri128)
114+
.value();
115+
uint64_t CurAlign = 1 << llvm::countr_zero(Offset);
116+
if (CurAlign < ReqAlign) {
117+
/* Emit warning */
118+
SmallString<1024> Err;
119+
llvm::raw_svector_ostream OS(Err);
120+
const PrintingPolicy &PP = ASTCtx.getPrintingPolicy();
121+
OS << "Field '";
122+
D->getNameForDiagnostic(OS, PP, false);
123+
OS << "' of type '" << T.getAsString(PP) << "'";
124+
OS << " (size " << Size << ")";
125+
OS << " requires " << ReqAlign << " byte alignment for precise bounds;";
126+
OS << " field offset is " << Offset;
127+
OS << " (aligned to " << CurAlign << ");";
128+
129+
/*
130+
* Print current bounds
131+
* TODO: use cheri_compressed_cap correctly
132+
*
133+
const RecordDecl *Parent = D->getParent();
134+
uint64_t ParentSize = ASTCtx.getTypeSize(Parent->getTypeForDecl()) / 8;
135+
auto MockCap = getBoundedCap(ParentSize, Offset, Size);
136+
uint64_t Base = MockCap.base();
137+
uint64_t Top = MockCap.top();
138+
OS << " Current bounds: " << Base << "-" << Top;
139+
*/
140+
141+
// Note that this will fire for every translation unit that uses this
142+
// class. This is suboptimal, but at least scan-build will merge
143+
// duplicate HTML reports.
144+
PathDiagnosticLocation L =
145+
PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
146+
auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), L);
147+
Report->setDeclWithIssue(D);
148+
Report->addRange(D->getSourceRange());
149+
150+
/*
151+
* Add exposed fields as notes
152+
* TODO: use cheri_compressed_cap correctly
153+
Report = reportExposedFields(D, ASTCtx, BR, Base, Top, std::move(Report));
154+
*/
155+
156+
return Report;
157+
}
158+
}
159+
160+
return nullptr;
161+
}
162+
45163
void SubObjectRepresentabilityChecker::checkASTDecl(const RecordDecl *R,
46164
AnalysisManager &mgr,
47165
BugReporter &BR) const {
@@ -60,85 +178,54 @@ void SubObjectRepresentabilityChecker::checkASTDecl(const RecordDecl *R,
60178
*/
61179

62180
for (FieldDecl *D : R->fields()) {
63-
QualType T = D->getType();
64-
65-
ASTContext &ASTCtx = BR.getContext();
66-
uint64_t Offset = ASTCtx.getFieldOffset(D) / 8;
67-
if (Offset > 0) {
68-
uint64_t Size = ASTCtx.getTypeSize(T) / 8;
69-
uint64_t ReqAlign = llvm::CompressedCapability::GetRequiredAlignment(
70-
Size, llvm::CompressedCapability::Cheri128)
71-
.value();
72-
uint64_t CurAlign = 1 << llvm::countr_zero(Offset);
73-
if (CurAlign < ReqAlign) {
74-
/* Emit warning */
75-
SmallString<1024> Err;
76-
llvm::raw_svector_ostream OS(Err);
77-
const PrintingPolicy &PP = ASTCtx.getPrintingPolicy();
78-
OS << "Field '";
79-
D->getNameForDiagnostic(OS, PP, false);
80-
OS << "' of type '" << T.getAsString(PP) << "'";
81-
OS << " (size " << Size << ")";
82-
OS << " requires " << ReqAlign << " byte alignment for precise bounds;";
83-
OS << " field offset is " << Offset;
84-
OS << " (aligned to " << CurAlign << ");";
85-
86-
#if 0
87-
// CHERIOT FIXME: Get rid of dependency on compressed-cap-lib
88-
using Handler = CompressedCap128;
89-
uint64_t ParentSize = ASTCtx.getTypeSize(R->getTypeForDecl())/8;
90-
Handler::addr_t InitLength = Handler ::representable_length(ParentSize);
91-
Handler::cap_t MockCap = Handler::make_max_perms_cap(0, 0, InitLength);
92-
bool exact = Handler::setbounds(&MockCap, Offset, Offset + Size);
93-
assert(!exact);
94-
auto Base = MockCap.base();
95-
Handler::addr_t Top = MockCap.top();
96-
OS << " Current bounds: " << Base << "-" << Top;
97-
#endif
98-
99-
PathDiagnosticLocation L =
100-
PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
101-
auto Report = std::make_unique<BasicBugReport>(BT_1, OS.str(), L);
102-
Report->setDeclWithIssue(D);
103-
Report->addRange(D->getSourceRange());
181+
auto Report = checkField(D, mgr, BR, BT_1);
182+
if (Report)
183+
BR.emitReport(std::move(Report));
184+
}
185+
}
104186

105-
#if 0
106-
// CHERIOT FIXME: Get rid of dependency on compressed-cap-lib
107-
auto FI = R->field_begin();
108-
while (FI != R->field_end() &&
109-
ASTCtx.getFieldOffset(*FI)/8
110-
+ ASTCtx.getTypeSize(FI->getType())/8 <= Base)
111-
FI++;
112-
113-
bool Before = true;
114-
while (FI != R->field_end() && ASTCtx.getFieldOffset(*FI)/8 < Top) {
115-
if (*FI != D) {
116-
SmallString<1024> Note;
117-
llvm::raw_svector_ostream OS2(Note);
118-
119-
uint64_t CurFieldOffset = ASTCtx.getFieldOffset(*FI)/8;
120-
uint64_t CurFieldSize = ASTCtx.getTypeSize(FI->getType())/8;
121-
uint64_t BytesExposed =
122-
Before ? std::min(CurFieldSize,
123-
CurFieldOffset + CurFieldSize - Base)
124-
: std::min(CurFieldSize, Top - CurFieldOffset);
125-
OS2 << BytesExposed << "/" << CurFieldSize << " bytes exposed";
126-
if (cheri::hasCapability(FI->getType(), ASTCtx))
127-
OS2 << " (may expose capability!)";
128-
129-
PathDiagnosticLocation LN =
130-
PathDiagnosticLocation::createBegin(*FI, BR.getSourceManager());
131-
Report->addNote(OS2.str(), LN);
132-
} else
133-
Before = false;
134-
FI++;
187+
void SubObjectRepresentabilityChecker::checkASTCodeBody(const Decl *D,
188+
AnalysisManager &mgr,
189+
BugReporter &BR) const {
190+
using namespace ast_matchers;
191+
192+
auto Member = memberExpr().bind("member");
193+
auto Decay =
194+
castExpr(hasCastKind(CK_ArrayToPointerDecay), has(Member)).bind("decay");
195+
auto Addr = unaryOperator(hasOperatorName("&"), has(Member)).bind("addr");
196+
197+
auto PointerSizeCheck = traverse(TK_AsIs, stmt(anyOf(Decay, Addr)));
198+
199+
auto Matcher = decl(forEachDescendant(PointerSizeCheck));
200+
201+
auto Matches = match(Matcher, *D, BR.getContext());
202+
for (const auto &Match : Matches) {
203+
if (const CastExpr *CE = Match.getNodeAs<CastExpr>("decay")) {
204+
if (const MemberExpr *ME = Match.getNodeAs<MemberExpr>("member")) {
205+
ValueDecl *VD = ME->getMemberDecl();
206+
if (FieldDecl *FD = dyn_cast<FieldDecl>(VD)) {
207+
auto Report = checkField(FD, mgr, BR, BT_2);
208+
if (Report) {
209+
PathDiagnosticLocation LN = PathDiagnosticLocation::createBegin(
210+
CE, BR.getSourceManager(), mgr.getAnalysisDeclContext(D));
211+
Report->addNote("Array to pointer decay", LN);
212+
BR.emitReport(std::move(Report));
213+
}
214+
}
215+
}
216+
} else if (const UnaryOperator *UO =
217+
Match.getNodeAs<UnaryOperator>("addr")) {
218+
if (const MemberExpr *ME = Match.getNodeAs<MemberExpr>("member")) {
219+
ValueDecl *VD = ME->getMemberDecl();
220+
if (FieldDecl *FD = dyn_cast<FieldDecl>(VD)) {
221+
auto Report = checkField(FD, mgr, BR, BT_2);
222+
if (Report) {
223+
PathDiagnosticLocation LN = PathDiagnosticLocation::createBegin(
224+
UO, BR.getSourceManager(), mgr.getAnalysisDeclContext(D));
225+
Report->addNote("Address of a field taken", LN);
226+
BR.emitReport(std::move(Report));
227+
}
135228
}
136-
#endif
137-
138-
// Note that this will fire for every translation unit that uses this
139-
// class. This is suboptimal, but at least scan-build will merge
140-
// duplicate HTML reports.
141-
BR.emitReport(std::move(Report));
142229
}
143230
}
144231
}

clang/test/Analysis/Checkers/CHERI/subobject-representability-morello.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct R1 {
1111
char a[0x3FFF]; // no warn
1212
} f1good;
1313
struct {
14-
char c; // expected-note{{}}
14+
char c;
1515
char a[0x4000]; // expected-warning{{Field 'a' of type 'char[16384]' (size 16384) requires 8 byte alignment for precise bounds; field offset is 1}}
1616
} f2bad;
1717
struct {
@@ -27,9 +27,9 @@ struct S2 {
2727
};
2828

2929
struct R2 {
30-
char x[0x50]; // expected-note{{16/80}}
31-
struct S2 s2; // expected-note{{32/32 bytes exposed (may expose capability!)}}
32-
char c; // expected-note{{1}}
30+
char x[0x50];
31+
struct S2 s2;
32+
char c;
3333
char a[0x8000]; // expected-warning{{Field 'a' of type 'char[32768]'}}
34-
char y[32]; // expected-note{{15/32}}
34+
char y[32];
3535
};

0 commit comments

Comments
 (0)