-
Couldn't load subscription status.
- Fork 15k
[clang] Implement -Walloc-size diagnostic option #150028
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
1f03a09
c483fd3
c790ac1
aa57034
4be5615
83892e3
06eee5e
a3697e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3542,6 +3542,59 @@ bool CallExpr::isBuiltinAssumeFalse(const ASTContext &Ctx) const { | |
| Arg->EvaluateAsBooleanCondition(ArgVal, Ctx) && !ArgVal; | ||
| } | ||
|
|
||
| const AllocSizeAttr *CallExpr::getAllocSizeAttr() const { | ||
| if (const FunctionDecl *DirectCallee = getDirectCallee()) | ||
| return DirectCallee->getAttr<AllocSizeAttr>(); | ||
| if (const Decl *IndirectCallee = getCalleeDecl()) | ||
| return IndirectCallee->getAttr<AllocSizeAttr>(); | ||
| return nullptr; | ||
erichkeane marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| bool CallExpr::getBytesReturnedByAllocSizeCall(const ASTContext &Ctx, | ||
| llvm::APInt &Result) const { | ||
| const AllocSizeAttr *AllocSize = getAllocSizeAttr(); | ||
|
|
||
| assert(AllocSize && AllocSize->getElemSizeParam().isValid()); | ||
| unsigned SizeArgNo = AllocSize->getElemSizeParam().getASTIndex(); | ||
| unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType()); | ||
| if (getNumArgs() <= SizeArgNo) | ||
| return false; | ||
|
|
||
| auto EvaluateAsSizeT = [&](const Expr *E, llvm::APSInt &Into) { | ||
| Expr::EvalResult ExprResult; | ||
| if (E->isValueDependent() || | ||
| !E->EvaluateAsInt(ExprResult, Ctx, Expr::SE_AllowSideEffects)) | ||
| return false; | ||
| Into = ExprResult.Val.getInt(); | ||
| if (Into.isNegative() || !Into.isIntN(BitsInSizeT)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be BitsInSizeT-1? In the case where the That would make the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand, isn't Also, this code isn't new, it was just moved here. |
||
| return false; | ||
| Into = Into.zext(BitsInSizeT); | ||
| return true; | ||
| }; | ||
|
|
||
| llvm::APSInt SizeOfElem; | ||
| if (!EvaluateAsSizeT(getArg(SizeArgNo), SizeOfElem)) | ||
| return false; | ||
|
|
||
| if (!AllocSize->getNumElemsParam().isValid()) { | ||
| Result = std::move(SizeOfElem); | ||
| return true; | ||
| } | ||
|
|
||
| llvm::APSInt NumberOfElems; | ||
| unsigned NumArgNo = AllocSize->getNumElemsParam().getASTIndex(); | ||
| if (!EvaluateAsSizeT(getArg(NumArgNo), NumberOfElems)) | ||
| return false; | ||
|
|
||
| bool Overflow; | ||
| llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow); | ||
| if (Overflow) | ||
| return false; | ||
|
|
||
| Result = std::move(BytesAvailable); | ||
| return true; | ||
| } | ||
|
|
||
| bool CallExpr::isCallToStdMove() const { | ||
| return getBuiltinCallee() == Builtin::BImove; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // RUN: %clang_cc1 -fsyntax-only -verify -Walloc-size %s | ||
| struct Foo { int x[10]; }; | ||
|
|
||
| void *malloc(unsigned long) __attribute__((alloc_size(1))); | ||
| void *alloca(unsigned long) __attribute__((alloc_size(1))); | ||
| void *calloc(unsigned long, unsigned long) __attribute__((alloc_size(2, 1))); | ||
|
|
||
| void foo_consumer(struct Foo* p); | ||
|
|
||
| void alloc_foo(void) { | ||
| struct Foo *ptr1 = malloc(sizeof(struct Foo)); | ||
| struct Foo *ptr2 = malloc(sizeof(*ptr2)); | ||
| struct Foo *ptr3 = calloc(1, sizeof(*ptr3)); | ||
| struct Foo *ptr4 = calloc(sizeof(*ptr4), 1); | ||
| struct Foo (*ptr5)[5] = malloc(sizeof(*ptr5)); | ||
| void *ptr6 = malloc(4); | ||
|
|
||
| // Test insufficient size with different allocation functions. | ||
| struct Foo *ptr7 = malloc(sizeof(ptr7)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} | ||
| struct Foo *ptr8 = alloca(sizeof(ptr8)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} | ||
| struct Foo *ptr9 = calloc(1, sizeof(ptr9)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} | ||
| struct Foo *ptr10 = calloc(sizeof(ptr10), 1); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} | ||
|
|
||
| // Test function arguments. | ||
| foo_consumer(malloc(4)); // expected-warning {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} | ||
|
|
||
| // Test explicit cast. | ||
| struct Foo *ptr11 = (struct Foo *)malloc(sizeof(*ptr11)); | ||
| struct Foo *ptr12 = (struct Foo *)malloc(sizeof(ptr12)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} | ||
| struct Foo *ptr13 = (struct Foo *)alloca(sizeof(ptr13)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} | ||
| struct Foo *ptr14 = (struct Foo *)calloc(1, sizeof(ptr14)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} | ||
| struct Foo *ptr15 = (struct Foo *)malloc(4); // expected-warning {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} | ||
| void *ptr16 = (struct Foo *)malloc(4); // expected-warning {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} | ||
|
|
||
| struct Foo *ptr17 = (void *)(struct Foo *)malloc(4); // expected-warning 2 {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} | ||
| int *ptr18 = (unsigned *)(void *)(int *)malloc(1); // expected-warning {{initializing 'int *' with an expression of type 'unsigned int *' converts between pointers to integer types with different sign}} | ||
| // expected-warning@-1 {{allocation of insufficient size '1' for type 'int' with size '4'}} | ||
| // expected-warning@-2 {{allocation of insufficient size '1' for type 'unsigned int' with size '4'}} | ||
| int *ptr19 = (void *)(int *)malloc(1); // expected-warning {{allocation of insufficient size '1' for type 'int' with size '4'}} | ||
| // expected-warning@-1 {{allocation of insufficient size '1' for type 'int' with size '4'}} | ||
| (void)(int *)malloc(1); // expected-warning {{allocation of insufficient size '1' for type 'int' with size '4'}} | ||
| } | ||
vvuksanovic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Uh oh!
There was an error while loading. Please reload this page.