mirror of
https://github.com/intel/llvm.git
synced 2026-01-20 10:58:11 +08:00
[analyzer] Teach CastSizeChecker about flexible array members.
...as well as fake flexible array members: structs that end in arrays with length 0 or 1. Patch by Daniel Fahlgren! llvm-svn: 201583
This commit is contained in:
@@ -29,6 +29,64 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
/// Check if we are casting to a struct with a flexible array at the end.
|
||||
/// \code
|
||||
/// struct foo {
|
||||
/// size_t len;
|
||||
/// struct bar data[];
|
||||
/// };
|
||||
/// \endcode
|
||||
/// or
|
||||
/// \code
|
||||
/// struct foo {
|
||||
/// size_t len;
|
||||
/// struct bar data[0];
|
||||
/// }
|
||||
/// \endcode
|
||||
/// In these cases it is also valid to allocate size of struct foo + a multiple
|
||||
/// of struct bar.
|
||||
static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
|
||||
CharUnits TypeSize, QualType ToPointeeTy) {
|
||||
const RecordType *RT = ToPointeeTy->getAs<RecordType>();
|
||||
if (!RT)
|
||||
return false;
|
||||
|
||||
const RecordDecl *RD = RT->getDecl();
|
||||
RecordDecl::field_iterator Iter(RD->field_begin());
|
||||
RecordDecl::field_iterator End(RD->field_end());
|
||||
const FieldDecl *Last = 0;
|
||||
for (; Iter != End; ++Iter)
|
||||
Last = *Iter;
|
||||
assert(Last && "empty structs should already be handled");
|
||||
|
||||
const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
|
||||
CharUnits FlexSize;
|
||||
if (const ConstantArrayType *ArrayTy =
|
||||
Ctx.getAsConstantArrayType(Last->getType())) {
|
||||
FlexSize = Ctx.getTypeSizeInChars(ElemType);
|
||||
if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
|
||||
TypeSize -= FlexSize;
|
||||
else if (ArrayTy->getSize() != 0)
|
||||
return false;
|
||||
} else if (RD->hasFlexibleArrayMember()) {
|
||||
FlexSize = Ctx.getTypeSizeInChars(ElemType);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FlexSize.isZero())
|
||||
return false;
|
||||
|
||||
CharUnits Left = RegionSize - TypeSize;
|
||||
if (Left.isNegative())
|
||||
return false;
|
||||
|
||||
if (Left % FlexSize == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
|
||||
const Expr *E = CE->getSubExpr();
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
@@ -66,18 +124,20 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
|
||||
if (typeSize.isZero())
|
||||
return;
|
||||
|
||||
if (regionSize % typeSize != 0) {
|
||||
if (ExplodedNode *errorNode = C.generateSink()) {
|
||||
if (!BT)
|
||||
BT.reset(
|
||||
new BuiltinBug(this, "Cast region with wrong size.",
|
||||
"Cast a region whose size is not a multiple of the"
|
||||
" destination type size."));
|
||||
BugReport *R = new BugReport(*BT, BT->getDescription(),
|
||||
errorNode);
|
||||
R->addRange(CE->getSourceRange());
|
||||
C.emitReport(R);
|
||||
}
|
||||
if (regionSize % typeSize == 0)
|
||||
return;
|
||||
|
||||
if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
|
||||
return;
|
||||
|
||||
if (ExplodedNode *errorNode = C.generateSink()) {
|
||||
if (!BT)
|
||||
BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
|
||||
"Cast a region whose size is not a multiple"
|
||||
" of the destination type size."));
|
||||
BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode);
|
||||
R->addRange(CE->getSourceRange());
|
||||
C.emitReport(R);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -270,6 +270,222 @@ void PR7217() {
|
||||
buf[1] = 'c'; // not crash
|
||||
}
|
||||
|
||||
void cast_emtpy_struct() {
|
||||
struct st {
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_1() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_2() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[0];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_3() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[1];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_4() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[2];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_5() {
|
||||
struct st {
|
||||
char i[200];
|
||||
char j[1];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) - sizeof(char)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_warn_1() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[2];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_warn_2() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[2];
|
||||
};
|
||||
|
||||
struct st *s = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_1() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_2() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[0];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_3() {
|
||||
struct st {
|
||||
int i[100];
|
||||
char j[1];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_4() {
|
||||
struct foo {
|
||||
char f[32];
|
||||
};
|
||||
struct st {
|
||||
char i[100];
|
||||
struct foo data[];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_5() {
|
||||
struct foo {
|
||||
char f[32];
|
||||
};
|
||||
struct st {
|
||||
char i[100];
|
||||
struct foo data[0];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_6() {
|
||||
struct foo {
|
||||
char f[32];
|
||||
};
|
||||
struct st {
|
||||
char i[100];
|
||||
struct foo data[1];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_warn_1() {
|
||||
struct foo {
|
||||
char f[32];
|
||||
};
|
||||
struct st {
|
||||
char i[100];
|
||||
struct foo data[];
|
||||
};
|
||||
|
||||
struct st *s = malloc(3 * sizeof(struct st) + 3 * sizeof(struct foo)); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_warn_2() {
|
||||
struct foo {
|
||||
char f[32];
|
||||
};
|
||||
struct st {
|
||||
char i[100];
|
||||
struct foo data[0];
|
||||
};
|
||||
|
||||
struct st *s = malloc(3 * sizeof(struct st) + 3 * sizeof(struct foo)); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_warn_3() {
|
||||
struct foo {
|
||||
char f[32];
|
||||
};
|
||||
struct st {
|
||||
char i[100];
|
||||
struct foo data[1];
|
||||
};
|
||||
|
||||
struct st *s = malloc(3 * sizeof(struct st) + 3 * sizeof(struct foo)); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_warn_4() {
|
||||
struct st {
|
||||
int i[100];
|
||||
int j[];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_warn_5() {
|
||||
struct st {
|
||||
int i[100];
|
||||
int j[0];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void cast_struct_flex_array_warn_6() {
|
||||
struct st {
|
||||
int i[100];
|
||||
int j[1];
|
||||
};
|
||||
|
||||
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
|
||||
free(s);
|
||||
}
|
||||
|
||||
void mallocCastToVoid() {
|
||||
void *p = malloc(2);
|
||||
const void *cp = p; // not crash
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,alpha.unix,alpha.security.ArrayBound -analyzer-store=region -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This file tests cases where we should not flag out-of-bounds warnings.
|
||||
@@ -24,8 +25,7 @@ void free(void *);
|
||||
|
||||
void field() {
|
||||
struct vec { size_t len; int data[0]; };
|
||||
// FIXME: Not warn for this.
|
||||
struct vec *a = malloc(sizeof(struct vec) + 10); // expected-warning {{Cast a region whose size is not a multiple of the destination type size}}
|
||||
struct vec *a = malloc(sizeof(struct vec) + 10*sizeof(int));
|
||||
a->len = 10;
|
||||
a->data[1] = 5; // no-warning
|
||||
free(a);
|
||||
|
||||
Reference in New Issue
Block a user