Skip to content

Commit 97d2c9c

Browse files
committedFeb 18, 2014
[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
1 parent 5cc5cd9 commit 97d2c9c

File tree

3 files changed

+290
-14
lines changed

3 files changed

+290
-14
lines changed
 

‎clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp

+72-12
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,64 @@ class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > {
2929
};
3030
}
3131

32+
/// Check if we are casting to a struct with a flexible array at the end.
33+
/// \code
34+
/// struct foo {
35+
/// size_t len;
36+
/// struct bar data[];
37+
/// };
38+
/// \endcode
39+
/// or
40+
/// \code
41+
/// struct foo {
42+
/// size_t len;
43+
/// struct bar data[0];
44+
/// }
45+
/// \endcode
46+
/// In these cases it is also valid to allocate size of struct foo + a multiple
47+
/// of struct bar.
48+
static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
49+
CharUnits TypeSize, QualType ToPointeeTy) {
50+
const RecordType *RT = ToPointeeTy->getAs<RecordType>();
51+
if (!RT)
52+
return false;
53+
54+
const RecordDecl *RD = RT->getDecl();
55+
RecordDecl::field_iterator Iter(RD->field_begin());
56+
RecordDecl::field_iterator End(RD->field_end());
57+
const FieldDecl *Last = 0;
58+
for (; Iter != End; ++Iter)
59+
Last = *Iter;
60+
assert(Last && "empty structs should already be handled");
61+
62+
const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
63+
CharUnits FlexSize;
64+
if (const ConstantArrayType *ArrayTy =
65+
Ctx.getAsConstantArrayType(Last->getType())) {
66+
FlexSize = Ctx.getTypeSizeInChars(ElemType);
67+
if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
68+
TypeSize -= FlexSize;
69+
else if (ArrayTy->getSize() != 0)
70+
return false;
71+
} else if (RD->hasFlexibleArrayMember()) {
72+
FlexSize = Ctx.getTypeSizeInChars(ElemType);
73+
} else {
74+
return false;
75+
}
76+
77+
if (FlexSize.isZero())
78+
return false;
79+
80+
CharUnits Left = RegionSize - TypeSize;
81+
if (Left.isNegative())
82+
return false;
83+
84+
if (Left % FlexSize == 0)
85+
return true;
86+
87+
return false;
88+
}
89+
3290
void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
3391
const Expr *E = CE->getSubExpr();
3492
ASTContext &Ctx = C.getASTContext();
@@ -66,18 +124,20 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
66124
if (typeSize.isZero())
67125
return;
68126

69-
if (regionSize % typeSize != 0) {
70-
if (ExplodedNode *errorNode = C.generateSink()) {
71-
if (!BT)
72-
BT.reset(
73-
new BuiltinBug(this, "Cast region with wrong size.",
74-
"Cast a region whose size is not a multiple of the"
75-
" destination type size."));
76-
BugReport *R = new BugReport(*BT, BT->getDescription(),
77-
errorNode);
78-
R->addRange(CE->getSourceRange());
79-
C.emitReport(R);
80-
}
127+
if (regionSize % typeSize == 0)
128+
return;
129+
130+
if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
131+
return;
132+
133+
if (ExplodedNode *errorNode = C.generateSink()) {
134+
if (!BT)
135+
BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
136+
"Cast a region whose size is not a multiple"
137+
" of the destination type size."));
138+
BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode);
139+
R->addRange(CE->getSourceRange());
140+
C.emitReport(R);
81141
}
82142
}
83143

‎clang/test/Analysis/malloc.c

+216
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,222 @@ void PR7217() {
270270
buf[1] = 'c'; // not crash
271271
}
272272

273+
void cast_emtpy_struct() {
274+
struct st {
275+
};
276+
277+
struct st *s = malloc(sizeof(struct st)); // no-warning
278+
free(s);
279+
}
280+
281+
void cast_struct_1() {
282+
struct st {
283+
int i[100];
284+
char j[];
285+
};
286+
287+
struct st *s = malloc(sizeof(struct st)); // no-warning
288+
free(s);
289+
}
290+
291+
void cast_struct_2() {
292+
struct st {
293+
int i[100];
294+
char j[0];
295+
};
296+
297+
struct st *s = malloc(sizeof(struct st)); // no-warning
298+
free(s);
299+
}
300+
301+
void cast_struct_3() {
302+
struct st {
303+
int i[100];
304+
char j[1];
305+
};
306+
307+
struct st *s = malloc(sizeof(struct st)); // no-warning
308+
free(s);
309+
}
310+
311+
void cast_struct_4() {
312+
struct st {
313+
int i[100];
314+
char j[2];
315+
};
316+
317+
struct st *s = malloc(sizeof(struct st)); // no-warning
318+
free(s);
319+
}
320+
321+
void cast_struct_5() {
322+
struct st {
323+
char i[200];
324+
char j[1];
325+
};
326+
327+
struct st *s = malloc(sizeof(struct st) - sizeof(char)); // no-warning
328+
free(s);
329+
}
330+
331+
void cast_struct_warn_1() {
332+
struct st {
333+
int i[100];
334+
char j[2];
335+
};
336+
337+
struct st *s = malloc(sizeof(struct st) + 2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
338+
free(s);
339+
}
340+
341+
void cast_struct_warn_2() {
342+
struct st {
343+
int i[100];
344+
char j[2];
345+
};
346+
347+
struct st *s = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
348+
free(s);
349+
}
350+
351+
void cast_struct_flex_array_1() {
352+
struct st {
353+
int i[100];
354+
char j[];
355+
};
356+
357+
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
358+
free(s);
359+
}
360+
361+
void cast_struct_flex_array_2() {
362+
struct st {
363+
int i[100];
364+
char j[0];
365+
};
366+
367+
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
368+
free(s);
369+
}
370+
371+
void cast_struct_flex_array_3() {
372+
struct st {
373+
int i[100];
374+
char j[1];
375+
};
376+
377+
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
378+
free(s);
379+
}
380+
381+
void cast_struct_flex_array_4() {
382+
struct foo {
383+
char f[32];
384+
};
385+
struct st {
386+
char i[100];
387+
struct foo data[];
388+
};
389+
390+
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
391+
free(s);
392+
}
393+
394+
void cast_struct_flex_array_5() {
395+
struct foo {
396+
char f[32];
397+
};
398+
struct st {
399+
char i[100];
400+
struct foo data[0];
401+
};
402+
403+
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
404+
free(s);
405+
}
406+
407+
void cast_struct_flex_array_6() {
408+
struct foo {
409+
char f[32];
410+
};
411+
struct st {
412+
char i[100];
413+
struct foo data[1];
414+
};
415+
416+
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
417+
free(s);
418+
}
419+
420+
void cast_struct_flex_array_warn_1() {
421+
struct foo {
422+
char f[32];
423+
};
424+
struct st {
425+
char i[100];
426+
struct foo data[];
427+
};
428+
429+
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}}
430+
free(s);
431+
}
432+
433+
void cast_struct_flex_array_warn_2() {
434+
struct foo {
435+
char f[32];
436+
};
437+
struct st {
438+
char i[100];
439+
struct foo data[0];
440+
};
441+
442+
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}}
443+
free(s);
444+
}
445+
446+
void cast_struct_flex_array_warn_3() {
447+
struct foo {
448+
char f[32];
449+
};
450+
struct st {
451+
char i[100];
452+
struct foo data[1];
453+
};
454+
455+
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}}
456+
free(s);
457+
}
458+
459+
void cast_struct_flex_array_warn_4() {
460+
struct st {
461+
int i[100];
462+
int j[];
463+
};
464+
465+
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
466+
free(s);
467+
}
468+
469+
void cast_struct_flex_array_warn_5() {
470+
struct st {
471+
int i[100];
472+
int j[0];
473+
};
474+
475+
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
476+
free(s);
477+
}
478+
479+
void cast_struct_flex_array_warn_6() {
480+
struct st {
481+
int i[100];
482+
int j[1];
483+
};
484+
485+
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
486+
free(s);
487+
}
488+
273489
void mallocCastToVoid() {
274490
void *p = malloc(2);
275491
const void *cp = p; // not crash

‎clang/test/Analysis/no-outofbounds.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,alpha.unix,alpha.security.ArrayBound -analyzer-store=region -verify %s
2+
// expected-no-diagnostics
23

34
//===----------------------------------------------------------------------===//
45
// This file tests cases where we should not flag out-of-bounds warnings.
@@ -24,8 +25,7 @@ void free(void *);
2425

2526
void field() {
2627
struct vec { size_t len; int data[0]; };
27-
// FIXME: Not warn for this.
28-
struct vec *a = malloc(sizeof(struct vec) + 10); // expected-warning {{Cast a region whose size is not a multiple of the destination type size}}
28+
struct vec *a = malloc(sizeof(struct vec) + 10*sizeof(int));
2929
a->len = 10;
3030
a->data[1] = 5; // no-warning
3131
free(a);

0 commit comments

Comments
 (0)
Please sign in to comment.