[PATCH][Review request] unix.Malloc checker improvement: +handling new/delete, +memory.MismatchedFree, +memory.MismatchedDelete, +improved display names for allocators/deallocators

Anton Yartsev anton.yartsev at gmail.com
Fri Feb 22 07:48:45 PST 2013


Modified the patch to avoid bugs in case when a default zero index is 
returned by getAllocDeallocDataIdx()
>
> On Feb 21, 2013, at 6:26 PM, Anna Zaks <ganna at apple.com 
> <mailto:ganna at apple.com>> wrote:
>
>>
>> On Feb 21, 2013, at 6:00 PM, Anton Yartsev <anton.yartsev at gmail.com 
>> <mailto:anton.yartsev at gmail.com>> wrote:
>>
>>>>
>>>> On Feb 19, 2013, at 10:18 PM, Anton Yartsev 
>>>> <anton.yartsev at gmail.com <mailto:anton.yartsev at gmail.com>> wrote:
>>>>
>>>>> Hi, Jordan. Thanx for the review!
>>>>>
>>>>> Attached is the new version of the patch with all the comments 
>>>>> addressed. Also added support for directly called operator 
>>>>> new()/new[]() and operator delete()
>>>>>
>>>>> There is currently one problem with handling of operator delete(). 
>>>>> The following test
>>>>>
>>>>> void testDeleteOp1() {
>>>>>  int *p = (int *)malloc(sizeof(int));
>>>>>  operator delete(p); // FIXME: should complain "Argument to 
>>>>> operator delete() was allocated by malloc(), not operator new"
>>>>> }
>>>>>
>>>>> produce no warnings as attached RefState seem to be missing at the 
>>>>> point when checkPostStmt(const CallExpr *CE, CheckerContext &C) 
>>>>> callback is called for operator delete(p).
>>>>> I haven't investigated the problem deeply yet, intend to address 
>>>>> it parallel with the review.
>>>>>
>>>>>> +  if (NE->getNumPlacementArgs())
>>>>>> +    return;
>>>>>> +  // skip placement new operators as they may not allocate memory
>>>>>>
>>>>>> Two comments here:
>>>>>> - Please make sure all comments are complete, capitalized, and 
>>>>>> punctuated sentences. (This has the important one, 
>>>>>> "complete"....just missing capitalization and punctuation.)
>>>>> I'll try. Unfortunately I am not as good in English as I want to 
>>>>> be, so sorry for my grammar, syntax, and punctuation.
>>>>>
>>>>> -- 
>>>>> Anton
>>>>>
>>>>> <MallocChecker_v2.patch>
>>>>
>>>
>>> Hi Anna. Thanks for your comments! Attached is the new patch.
>>>
>>>> Just adding another kind as extra enumeration values does not seem 
>>>> right. Another option is to make Kind be a pointer to a static 
>>>> array, which contains objects recording all necessary info about 
>>>> each kind (MacOSKeychainAPIChecker uses this approach). This is 
>>>> probably an overkill for now, but is another option.
>>> Not sure that I have got an idea.
>>> The memory and deallocator kind are both set for a RefState. Do you 
>>> mean creating a static array with 'memory kinds' * 'deallocator 
>>> kind' elements for all possible combinations? Also there is no 
>>> necessary info other then the kind itself.
>>> Left for now.
>>
>> I think of ToBeReleasedWith* as being different types of Allocate; I 
>> don't think they should be separate values in the same enum. It's 
>> also unfortunate to have to copy the constant values in both places - 
>> DeallocatorKind and RefState::Kind. Maybe you could restructure this 
>> similarly to how this is done in SVals.h?
>>
>>>
>>>> +  const FunctionDecl *getCalleeDecl() const { return CalleeDecl; }
>>>> Do we only store the name of the allocator declaration here?
>>
>> If the Decl is always an allocator Decl, we should change the name of 
>> the method to be more descriptive.
>>>> Do we need to store this in the state? (Growing states implies 
>>>> memory overhead.) Can't this be easily implied from the kind?
>>> Kind can't give us information about the name of an allocator that 
>>> can be malloc(), realloc(), a user function with ownership_takes 
>>> attribute, etc.
>>> One solution to avoid keeping a CalleeDecl in RefState is to 
>>> rollback to CallExpr::getDirectCallee() from 
>>> CheckerContext::getCalleeDec() and to print "malloc()" in case of 
>>> indirect calls.
>>
>> Ok, I see.
>
> After thinking about it some more, I do not think we should add an 
> extra pointer to the state to differentiate between few allocator 
> functions. In the case when we do not have ownership attributes, we 
> only have few possible allocators, whose names we know ahead of time. 
> In case we support ownership attributes, we are likely to have few 
> allocator functions whose names we could just store in the checker 
> state on the first encounter (like we store the IdentifierInfo).
>
> In addition, we could change the ownership attributes in such a way 
> that each allocator would have a corresponding deallocator; for 
> example, if we wanted to check matching allocators and deallocators. 
> Annotated deallocators won't necessarily be one of the functions you 
> know at compile time, so the DeallocatorKind enum would not cover it. 
> I think, it's best if we kept a table on a side that would store this 
> info (allocation function name, deallocator) and refer to the entries 
> in the table from the state. (Take a look at MacOSKeychainAPIChecker - 
> it's very similar to malloc, but it handles different 
> allocator/deallocator pairs. I think a similar solution could work in 
> this case as well. Other solutions that address these issues are 
> welcome as well!)
>
>>> Jordan, what do you think about this?
>>>
>>>> +void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
>>>> +  CheckerContext &C) const {NE
>>>> +
>>>> +  FunctionDecl *OperatorNew = NE->getOperatorNew();
>>>> +  if (!OperatorNew)
>>>> +    return;
>>>> +
>>>> +  // Skip custom new operators
>>>> +  if (!OperatorNew->isImplicit() &&
>>>> + 
>>>>  !C.getSourceManager().isInSystemHeader(OperatorNew->getLocStart()) &&
>>>> +      !NE->isGlobalNew())
>>>> +    return;
>>>> +
>>>> +  // Skip standard global placement operator 
>>>> new/new[](std::size_t, void * p);
>>>> +  // process all other standard new/new[] operators including 
>>>> placement
>>>> +  // operators new/new[](std::size_t, const std::nothrow_t&)
>>>> +  if (OperatorNew->isReservedGlobalPlacementOperator())
>>>> +    return;
>>>>
>>>> Is there a reason why we first process each operator new in 
>>>> "checkPostStmt(const callExpr" and finish processing in 
>>>> "checkPostStmt(const CXXNewExpr" ? I think the code would be 
>>>> simpler if everything could be done in a single callback.
>>> Code added to "checkPostStmt(const callExpr" is for processing 
>>> direct calls to operator new/delete functions, "checkPostStmt(const 
>>> CXXNewExpr" is for handling new expressions. Either first or second 
>>> callback is called in each particular case but not both of them. Am 
>>> I right?
>>>
>>
>> I see; makes sense. Please, add a comment in "checkPostStmt(const 
>> callExpr*".
>>
>>>>
>>>> +void MallocChecker::PrintExpectedAllocName(raw_ostream &os, 
>>>> CheckerContext &C,
>>>> + const Expr *E) const {
>>>> +  DeallocatorKind dKind = GetDeallocKind(C, E);
>>>> +
>>>> +  switch(dKind) {
>>>> +    case D_free: os << "malloc()"; return;
>>>> +    case D_delete: os << "operator new"; return;
>>>> +    case D_deleteArray: os << "operator new[]"; return;
>>>> +    case D_unknown: os << "unknown means"; return;
>>>>
>>>> This function is used to form user visible warnings. Do we ever 
>>>> expect it to print "unknown means"? Can this be based on the Kind 
>>>> stored inside of RefState, where there is no D_unknown?
>>> Right, changed the case to assert. There is actually implicit 
>>> D_unknown in RefState - case when 2nd and 3rd bits are set to zero.
>>>
>>> -- 
>>> Anton
>>> <MallocChecker_v3.patch>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at cs.uiuc.edu <mailto:cfe-commits at cs.uiuc.edu>
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>


-- 
Anton

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130222/8aeb0151/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: MallocChecker_v4_1.patch
Type: text/x-diff
Size: 33125 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130222/8aeb0151/attachment.patch>


More information about the cfe-commits mailing list