Skip to content

Commit d7b9b2d

Browse files
Ensure bounds are not inserted into unwritable files (#689)
Adds checks to ensure that 3C will not attempt to rewrite in unwritable files to insert array bounds. The test file in this PR contains an example using a function body in an unwritable file which fails on the current main. After changes made to support rewriting in functions declarations without definitions (#691), this will be able to happen even when the functions are not defined, and so it will becomes a more prevalent issue.
1 parent 16f7573 commit d7b9b2d

File tree

5 files changed

+173
-48
lines changed

5 files changed

+173
-48
lines changed

clang/include/clang/3C/CtxSensAVarBounds.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class CtxSensitiveBoundsKeyHandler {
9292
std::map<BoundsKey, BoundsKey> &BKMap,
9393
bool IsGlobal);
9494

95+
void contextualizeCVar(RecordDecl *RD, std::string AccessKey, bool IsGlobal,
96+
ASTContext *C, ProgramInfo &I);
97+
9598
// Get string that represents a context sensitive key for the struct
9699
// member access ME.
97100
std::string getCtxStructKey(MemberExpr *ME, ASTContext *C);

clang/lib/3C/AVarBoundsInfo.cpp

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -592,18 +592,37 @@ void PotentialBoundsInfo::addPotentialBoundsPOne(
592592
}
593593

594594
bool AVarBoundsInfo::isValidBoundVariable(clang::Decl *D) {
595+
if (D == nullptr)
596+
return false;
597+
598+
// Any pointer declaration in an unwritable file without existing bounds
599+
// annotations is not valid. This ensures we do not add bounds onto pointers
600+
// where attempting to rewrite the variable to insert the bound would be an
601+
// error. If there are existing bounds, no new bound will be inferred, so no
602+
// rewriting will be attempted. By leaving existing bounds as valid, these
603+
// bounds can be used infer bounds on other (writeable) declarations.
604+
// Non-pointer types are also still valid because these will never need bounds
605+
// expression, and they need to remain valid so that they can be used by
606+
// existing array pointer bounds.
607+
auto PSL = PersistentSourceLoc::mkPSL(D, D->getASTContext());
608+
if (!canWrite(PSL.getFileName())) {
609+
if (auto *DD = dyn_cast<DeclaratorDecl>(D))
610+
return !DD->getType()->isPointerType() || DD->hasBoundsExpr();
611+
return false;
612+
}
613+
595614
// All return and field values are valid bound variables.
596-
if (D && (isa<FunctionDecl>(D) || isa<FieldDecl>(D)))
615+
if (isa<FunctionDecl>(D) || isa<FieldDecl>(D))
597616
return true;
598617

599618
// For Parameters, check if they belong to a valid function.
600619
// Function pointer types are not considered valid functions, so function
601-
// pointer parameters are are disqualified as valid bound variables here.
602-
if (auto *PD = dyn_cast_or_null<ParmVarDecl>(D))
620+
// pointer parameters are disqualified as valid bound variables here.
621+
if (auto *PD = dyn_cast<ParmVarDecl>(D))
603622
return PD->getParentFunctionOrMethod() != nullptr;
604623

605-
// For VarDecls, check if these are are not dummy and have a name.
606-
if (auto *VD = dyn_cast_or_null<VarDecl>(D))
624+
// For VarDecls, check if these are not dummy and have a name.
625+
if (auto *VD = dyn_cast<VarDecl>(D))
607626
return !VD->getNameAsString().empty();
608627

609628
return false;
@@ -651,27 +670,25 @@ bool AVarBoundsInfo::tryGetVariable(clang::Decl *D, BoundsKey &R) {
651670

652671
bool AVarBoundsInfo::tryGetVariable(clang::Expr *E, const ASTContext &C,
653672
BoundsKey &Res) {
654-
Optional<llvm::APSInt> OptConsVal;
655-
bool Ret = false;
656673
if (E != nullptr) {
657674
E = E->IgnoreParenCasts();
658-
if (E->getType()->isArithmeticType() &&
659-
(OptConsVal = E->getIntegerConstantExpr(C))) {
675+
676+
// Get the BoundsKey for the constant value if the expression is a constant
677+
// integer expression.
678+
Optional<llvm::APSInt> OptConsVal = E->getIntegerConstantExpr(C);
679+
if (E->getType()->isArithmeticType() && OptConsVal.hasValue()) {
660680
Res = getVarKey(*OptConsVal);
661-
Ret = true;
662-
} else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
663-
auto *D = DRE->getDecl();
664-
Ret = tryGetVariable(D, Res);
665-
if (!Ret) {
666-
assert(false && "Invalid declaration found inside bounds expression");
667-
}
668-
} else if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
669-
return tryGetVariable(ME->getMemberDecl(), Res);
670-
} else {
671-
// assert(false && "Variable inside bounds declaration is an expression");
681+
return true;
672682
}
683+
684+
// For declarations or struct member references, get the bounds key for the
685+
// references variables or field.
686+
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
687+
return tryGetVariable(DRE->getDecl(), Res);
688+
if (auto *ME = dyn_cast<MemberExpr>(E))
689+
return tryGetVariable(ME->getMemberDecl(), Res);
673690
}
674-
return Ret;
691+
return false;
675692
}
676693

677694
// Merging bounds B with the present bounds of key L at the same priority P

clang/lib/3C/CtxSensAVarBounds.cpp

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ bool CtxSensitiveBoundsKeyHandler::tryGetFieldCSKey(
170170
FieldDecl *FD, CtxStKeyMap *CSK, const std::string &AK, ASTContext *C,
171171
ProgramInfo &I, BoundsKey &CSKey) {
172172
bool RetVal = false;
173-
if (CSK->find(AK) != CSK->end()) {
173+
if (ABI->isValidBoundVariable(FD) && CSK->find(AK) != CSK->end()) {
174174
CVarOption CV = I.getVariable(FD, C);
175175
BoundsKey OrigK;
176176
if (CV.hasValue() && CV.getValue().hasBoundsKey()) {
@@ -225,44 +225,41 @@ void CtxSensitiveBoundsKeyHandler::contextualizeStructRecord(
225225
}
226226
}
227227

228+
void CtxSensitiveBoundsKeyHandler::contextualizeCVar(RecordDecl *RD,
229+
std::string AccessKey,
230+
bool IsGlobal,
231+
ASTContext *C,
232+
ProgramInfo &I) {
233+
std::string FileName = PersistentSourceLoc::mkPSL(RD, *C).getFileName();
234+
if (canWrite(FileName)) {
235+
// Context sensitive struct key map.
236+
CtxStKeyMap *MECSMap = getCtxStKeyMap(IsGlobal);
237+
auto &BKeyMap = (*MECSMap)[AccessKey];
238+
contextualizeStructRecord(I, C, RD, AccessKey, BKeyMap, IsGlobal);
239+
}
240+
}
241+
228242
void CtxSensitiveBoundsKeyHandler::contextualizeCVar(MemberExpr *ME,
229243
ASTContext *C,
230244
ProgramInfo &I) {
231245
FieldDecl *FD = dyn_cast_or_null<FieldDecl>(ME->getMemberDecl());
232-
if (FD != nullptr) {
233-
RecordDecl *RD = FD->getParent();
234-
// If the base decl is not null.
235-
if (RD != nullptr) {
236-
// Get structure access key.
237-
StructAccessVisitor SKV(C);
238-
SKV.TraverseStmt(ME->getBase()->getExprStmt());
239-
std::string AK = SKV.getStructAccessKey();
240-
// Context sensitive struct key map.
241-
CtxStKeyMap *MECSMap = getCtxStKeyMap(SKV.IsGlobal);
242-
auto &BKeyMap = (*MECSMap)[AK];
243-
contextualizeStructRecord(I, C, RD, AK, BKeyMap, SKV.IsGlobal);
244-
}
246+
if (RecordDecl *RD = FD != nullptr ? FD->getParent() : nullptr) {
247+
// Get structure access key.
248+
StructAccessVisitor SKV(C);
249+
SKV.TraverseStmt(ME->getBase()->getExprStmt());
250+
contextualizeCVar(RD, SKV.getStructAccessKey(), SKV.IsGlobal, C, I);
245251
}
246252
}
247253

248254
void CtxSensitiveBoundsKeyHandler::contextualizeCVar(VarDecl *VD, ASTContext *C,
249255
ProgramInfo &I) {
250-
const RecordType *RT = dyn_cast_or_null<RecordType>(
251-
VD->getType()->getUnqualifiedDesugaredType());
252-
const RecordDecl *RD = nullptr;
253-
if (RT != nullptr) {
254-
RD = RT->getDecl();
255-
}
256-
// If the base decl is not null.
257-
if (RT != nullptr) {
256+
const auto *RT = dyn_cast_or_null<RecordType>(
257+
VD->getType()->getUnqualifiedDesugaredType());
258+
if (RecordDecl *RD = RT != nullptr ? RT->getDecl() : nullptr) {
258259
// Get structure access key.
259260
StructAccessVisitor SKV(C);
260261
SKV.processVarDecl(VD);
261-
// Context sensitive struct key map.
262-
CtxStKeyMap *MECSMap = getCtxStKeyMap(SKV.IsGlobal);
263-
std::string AK = SKV.getStructAccessKey();
264-
auto &BKeyMap = (*MECSMap)[AK];
265-
contextualizeStructRecord(I, C, RD, AK, BKeyMap, SKV.IsGlobal);
262+
contextualizeCVar(RD, SKV.getStructAccessKey(), SKV.IsGlobal, C, I);
266263
}
267264
}
268265

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: rm -rf %t*
2+
// RUN: cd %S
3+
// RUN: 3c -alltypes -addcr -output-dir=%t.checked/base_subdir %s --
4+
5+
// The functions called from this file would normally have bounds inferred if
6+
// they were declared in a writable file. The file is not writable, so the new
7+
// bounds must not be inferred. This possibility of this happening existed
8+
// before 3C started inferring itypes on undefined functions, but it became a
9+
// significant issue and was noticed with the introduction of this feature.
10+
11+
#include "../unwritable_bounds.h"
12+
void test_functions() {
13+
{
14+
char s[0];
15+
unwrite0(s);
16+
}
17+
{
18+
char s[0];
19+
unwrite1(s);
20+
}
21+
{
22+
char s[0];
23+
unwrite2(s);
24+
}
25+
{
26+
char s[0];
27+
unwrite3(s);
28+
}
29+
{
30+
char s[0];
31+
unwrite4(s);
32+
}
33+
{
34+
char s[0];
35+
unwrite5(s);
36+
}
37+
{
38+
char s[0];
39+
unwrite6(s);
40+
}
41+
}
42+
43+
void test_struct() {
44+
// There is a code path for variable declarations and a different path for
45+
// uses of a variable. The initializer is required to test the declaration
46+
// path.
47+
struct arr_struct a = {};
48+
for (int i = 0; i < a.n; i++)
49+
a.arr[i];
50+
51+
// I don't quite understand why this was a problem, but it caused an
52+
// assertion to fail after apply the fix for the first struct test
53+
// case.
54+
union e {
55+
float d
56+
};
57+
int i = struct_ret()->c;
58+
union e f;
59+
f.d;
60+
}
61+
62+
void test_glob() {
63+
for (int i = 0; i < 10; i++)
64+
glob0[i];
65+
66+
for (int i = 0; i < 10; i++)
67+
glob1[i];
68+
69+
for (int i = 0; i < 10; i++)
70+
glob2[i];
71+
72+
for (int i = 0; i < 10; i++)
73+
glob3[i];
74+
}

clang/test/3C/unwritable_bounds.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Used by base_subdir/unwritable_bounds.c .
2+
3+
void unwrite0(void *p : itype(_Array_ptr<void>));
4+
void unwrite1(char *p);
5+
void unwrite2(char *p : itype(_Array_ptr<char>));
6+
void unwrite3(_Array_ptr<char> p);
7+
8+
void unwrite4(char *p) {
9+
for (int i = 0; i < 10; i++)
10+
p[i];
11+
}
12+
void unwrite5(char *p : itype(_Array_ptr<char>)) {
13+
for (int i = 0; i < 10; i++)
14+
p[i];
15+
}
16+
void unwrite6(_Array_ptr<char> p) {
17+
for (int i = 0; i < 10; i++)
18+
p[i];
19+
}
20+
21+
struct arr_struct {
22+
int *arr;
23+
int n;
24+
};
25+
26+
struct other_struct {
27+
char *c;
28+
};
29+
struct other_struct *struct_ret();
30+
31+
int *glob0;
32+
int *glob1 : itype(_Array_ptr<int>);
33+
int *glob2 : count(10);
34+
int *glob3 : itype(_Ptr<int>);

0 commit comments

Comments
 (0)