On Mar 21, 2013, at 8:01 PM, Anton Yartsev <[email protected]> wrote:

> On 14.03.2013 21:42, Anna Zaks wrote:
>> One more concern. I do not see any tests with path notes. It's very 
>> important to test not only the existence of the warning, but also the notes 
>> along the path. 
>>  - malloc-path.c currently tests the path notes produced by the warnings 
>> from the MallocChecker
>>  - test/Analysis/diagnostics/deref-track-symbolic-region.c is a good example 
>> of how to test for the notes in text and plist formats(used to draw the path 
>> in Xcode).
>> 
>> I'd add a new test file and test the diagnostics in both text and plist 
>> format. I am OK with this test and any related work going in as a separate 
>> commit.
> New test added, MallocChecker::MallocBugVisitor::isAllocated() and 
> MallocChecker::MallocBugVisitor::isReleased() changed a little.
> Nothing else changed since the last patch.
> OK to commit?

Anton, 

Jordan has noticed that you have Windows line endings ('\r') in the new file. 
Please, make sure you do not commit these.

Otherwise, we are OK with post commit review.

I am curious if you were able to find any real bugs with the new checker.

Thanks!
Anna.
> 
>> 
>> Thanks,
>> Anna.
>> 
>> 
>> On Mar 14, 2013, at 8:38 AM, Anton Yartsev <[email protected]> wrote:
>> 
>>> On 12.03.2013 22:06, Anna Zaks wrote:
>>>> Thanks Anton! The patch looks good overall. Have you evaluated it on some 
>>>> real C++ codebases?
>>> Not yet. Failed to launch scan-build with my Perl 5.16.2.
>>> 
>> 
>> Let's do this ASAP. It might point out issues we have not thought of yet.
>> 
>>>> 
>>>> Below are some comments.
>>>> 
>>>> ---
>>>> +  // The return value from operator new is already bound and we don't 
>>>> want to 
>>>> +  // break this binding so we call MallocUpdateRefState instead of 
>>>> MallocMemAux.
>>>> +  State = MallocUpdateRefState(C, NE, State, NE->isArray() ? 
>>>> AF_CXXNewArray 
>>>> +                                                           : AF_CXXNew);
>>>> Why is this different from handling malloc? MallocMemAux does break the 
>>>> binding formed by default handling of malloc and forms a new binding with 
>>>> more semantic information. (I am fine with addressing this after the 
>>>> initial commit/commits.)
>>> In case of 'new' the memory can be initialized to a non-default value (e.g. 
>>> int *p = new int(3); ). The existing logic of MallocMemAux() breaks the 
>>> binding and information about the initialization value is lost. This causes 
>>> several tests to fail.
>>> Changed the comment to be more informative.
>> 
>> I see. I am concerned about the inconsistency of processing malloc and new. 
>> On the other hand, it probably does not matter right now.
>> 
>>> 
>>>> 
>>>> ---
>>>>  def MallocOptimistic : Checker<"MallocWithAnnotations">,
>>>> -  HelpText<"Check for memory leaks, double free, and use-after-free 
>>>> problems. Assumes that all user-defined functions which might free a 
>>>> pointer are annotated.">,
>>>> +  HelpText<"Check for memory leaks, double free, and use-after-free 
>>>> problems. Traces memory managed by malloc()/free(). Assumes that all 
>>>> user-defined functions which might free a pointer are annotated.">,
>>>> 
>>>> Shouldn't the MallocWithAnnotations only check the functions which are 
>>>> explicitly annotated? I think it might be better to change the code rather 
>>>> than the comment.
>>> Currently MallocWithAnnotations is designed as extended unix.Malloc and it 
>>> is not a single line change to make it distinct. Can we address this after 
>>> primary commits?
>>> 
>> 
>> Addressing after the initial patch is fine.
>> 
>>>> 
>>>> ---
>>>> +  unsigned K : 2; // Kind enum, but stored as a bitfield.
>>>> +  unsigned Family : 30; // Rest of 32-bit word, currently just an 
>>>> allocation 
>>>> +                        // family.
>>>> 
>>>> We usually add the comment on a line preceding the declaration, like this:
>>>> +  // Kind enum, but stored as a bitfield.
>>>> +  unsigned K : 2; 
>>>> +  // Rest of 32-bit word, currently just an allocation family.
>>>> +  unsigned Family : 30; 
>>>> 
>>>> ---
>>>> +  // Check if an expected deallocation function matches the real one.
>>>> +  if (RsBase && 
>>>> +      RsBase->getAllocationFamily() != AF_None &&
>>>> +      RsBase->getAllocationFamily() != getAllocationFamily(C, ParentExpr) 
>>>> ) {
>>>> Is it possible to have AF_None family here? Shouldn't " 
>>>> RsBase->getAllocationFamily() != AF_None" be inside an assert?
>>> It is possible. In the example below 
>>> initWithCharactersNoCopy:length:freeWhenDone takes ownership of memory 
>>> allocated by unknown means, RefState with AF_None is bound to the call and 
>>> after, when free() is processed, we have RsBase->getAllocationFamily() == 
>>> AF_None.
>> 
>> My understanding is that this API assumes that the memory belongs to the 
>> malloc family. So, for example, we would warn on freeing with a regular 
>> "free" after freeing with "initWithCharactersNoCopy.. freeWhenDone".
>> 
>> If the family is unknown, we should not be tracking the memory at all.
> Great idea, I'll include corresponding changes in the next patch devoted to 
> unix.MismatchedDeallocator
> 
>> 
>>> 
>>> void test(unichar *characters) {
>>>   NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters 
>>> length:12 freeWhenDone:1];
>>>   if (!string) {free(characters);}
>>> }
>>> 
>>>> 
>>>> ---
>>>> +// Used to check correspondence between allocators and deallocators.
>>>> +enum AllocationFamily {
>>>> The comment should describe what family is. It is a central notion for the 
>>>> checker and I do not think we explain it anywhere.
>>>> 
>>>> ---
>>>> The patch is very long. Is it possible to split it up into smaller chunks 
>>>> (http://llvm.org/docs/DeveloperPolicy.html#incremental-development)?
>>> Committed the initial refactoring as r176949.
>>> 
>>> Let's start! Attached is the cplusplus.NewDelete checker separated from the 
>>> patch.
>>> 
>>>> 
>>>> Thanks,
>>>> Anna.
>>>> On Mar 12, 2013, at 8:56 AM, Anton Yartsev <[email protected]> wrote:
>>>> 
>>>>> On 12.03.2013 2:24, Jordan Rose wrote:
>>>>>> Looking over this one last time...
>>>>>> 
>>>>>>> -  os << "Argument to free() is offset by "
>>>>>>> +  os << "Argument to ";
>>>>>>> +  if (!printAllocDeallocName(os, C, DeallocExpr))
>>>>>>> +    os << "deallocator";
>>>>>>> +  os << " is offset by "
>>>>>>>       << offsetBytes
>>>>>>>       << " "
>>>>>>>       << ((abs(offsetBytes) > 1) ? "bytes" : "byte")
>>>>>>> -     << " from the start of memory allocated by malloc()";
>>>>>>> +     << " from the start of ";
>>>>>>> +  if (AllocExpr) {
>>>>>>> +    SmallString<100> TempBuf;
>>>>>>> +    llvm::raw_svector_ostream TempOs(TempBuf);
>>>>>>>  
>>>>>>> +    if (printAllocDeallocName(TempOs, C, AllocExpr))
>>>>>>> +        os << "memory allocated by " << TempOs.str();
>>>>>>> +      else
>>>>>>> +        os << "allocated memory";
>>>>>>> +  } else
>>>>>>> +    printExpectedAllocName(os, C, DeallocExpr);
>>>>>>> +
>>>>>> 
>>>>>> The way you've set it up, AllocExpr will never be NULL (which is good, 
>>>>>> because otherwise you'd get "...from the start of malloc()" rather than 
>>>>>> "from the start of memory allocated by malloc()").
>>>>> Strange logic. Fixed.
>>>>> 
>>>>>> 
>>>>>> 
>>>>>>> +@interface Wrapper : NSData
>>>>>>> +- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len;
>>>>>>> +@end
>>>>>> 
>>>>>> As I discovered with the rest of the ObjC patch, this isn't a great test 
>>>>>> case because the analyzer doesn't consider it a system method. However, 
>>>>>> I don't see you use it anywhere in the file anyway, so you can probably 
>>>>>> just remove it.
>>>>>> 
>>>>>> 
>>>>>>> +void testNew11(NSUInteger dataLength) {
>>>>>>> +  int *data = new int;
>>>>>>> +  NSData *nsdata = [NSData dataWithBytesNoCopy:data length:sizeof(int) 
>>>>>>> freeWhenDone:1]; // expected-warning{{Memory allocated by 'new' should 
>>>>>>> be deallocated by 'delete', not 
>>>>>>> +dataWithBytesNoCopy:length:freeWhenDone:}}
>>>>>>> +}
>>>>>> 
>>>>>> 
>>>>>> Hm, that is rather unwieldy, but what bothers me more is that 
>>>>>> +dataWithBytesNoCopy:length:freeWhenDone: doesn't free the memory; it 
>>>>>> just takes ownership of it. I guess it's okay to leave that as a FIXME 
>>>>>> for now, but in the long run we should say something like 
>>>>>> "+dataWithBytesNoCopy:length:freeWhenDone: cannot take ownership of 
>>>>>> memory allocated by 'new'." (In the "hold" cases, most likely the user 
>>>>>> wasn't intending to free 
>>>>>> 
>>>>>> But, this doesn't have to block the patch; you/we can fix it post-commit.
>>>>>> 
>>>>>> 
>>>>>>> +  delete x; // FIXME: Shoud detect pointer escape and keep silent. 
>>>>>>> checkPointerEscape() is not currently invoked for delete.
>>>>>> 
>>>>>> 
>>>>>> Pedantic note: the real issue here is that we don't model delete at all 
>>>>>> (see ExprEngine::VisitCXXDeleteExpr). The correct model won't explicitly 
>>>>>> invoke checkPointerEscape, but it will construct a CallEvent for the 
>>>>>> deletion operator and then try to evaluate that call—or at least 
>>>>>> invalidate the arguments like VisitCXXNewExpr does for placement 
>>>>>> args—which will cause the argument region to get invalidated and 
>>>>>> checkPointerEscape to be triggered.
>>>>>> 
>>>>>> Jordan
>>>>> Updated patch attached.
>>>>> -- 
>>>>> Anton
>>>>> <MallocChecker_v11.patch>
>>>> 
>>> -- 
>>> Anton
>>> <cplusplus.NewDelete_v1.patch>
>> 
> 
> 
> -- 
> Anton
> <cplusplus.NewDelete_v2.patch>

_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to