[cfe-dev] Ownership attribute for malloc checking, revised patch
Andrew McGregor
andrewmcgr at gmail.com
Tue Jul 27 15:11:41 PDT 2010
On Tue, Jul 27, 2010 at 5:02 PM, Ted Kremenek <kremenek at apple.com> wrote:
> Looking good. Here are a few comments related to your recent changes:
>
> + // If ptr is NULL, no operation is performed.
> + if (nullState) {
> + if (!notNullState) {
> + return;
> + }
> + }
>
> There are some tabs in here. Also, this can be written more succinctly:
>
> if (nullState && !notNullState)
> return;
>
Yes, of course. As for the tabs... I don't get that, I think I have to
spend some time with editor documentation to figure that one out.
>
> +
> + SymbolRef Sym = val.getLocSymbolInBase();
> + if (Sym) {
> + const RefState *RS = state->get<RegionState>(Sym);
>
> I think you want to use 'notNullState' here, since your assumption is that
> this transition occurs only when the value is non-null. When 'nullState' is
> not null, you'll also probably want a transition so that path has consistent
> checking of the nullity of the symbol.
>
Not sure I understand... the RegionState is used because I'm getting at and
assuming forward the semantics of the memory region being assigned away, and
not touching the null or otherwise assumptions. Should I be using
notNullState to assert that the assignee is not null?
>
> Minor style nit:
>
> state->set<RegionState> (S...)
> isa<HeapSpaceRegion> (MS)
>
> Please don't write your function calls that way (with the extra space).
> It's inconsistent with the rest of the codebase, and it's not even
> consistent within the file.
>
Ok. Not sure how that happened... too much fiddling around with editors
again (this has been touched by Eclipse, Emacs and TextMate... things have
been a bit chaotic... I think this one is Eclipse's fault).
>
> On Jul 27, 2010, at 8:23 AM, Andrew McGregor wrote:
>
> So, here it is again. This version addresses all your comments, I think.
> I went a bit further in deleting unnecessary methods etc.
>
> Passes all its tests on OS X (previous patch was tested on Linux).
>
> Andrew
>
> On Fri, Jul 23, 2010 at 8:25 PM, Ted Kremenek <kremenek at apple.com> wrote:
>
>>
>> On Jul 20, 2010, at 8:55 PM, Andrew McGregor wrote:
>>
>> > Regenerated patch against an SVN pull today.
>> >
>> > This seems complete.
>> >
>> > No new tests fail with this patch, and it passes all its own tests.
>> >
>> > I have compiled large portions of an embedded Linux distribution with
>> checking, and given annotations on commonly used container functions, this
>> reduces the false positive rate for malloc and null pointer dereference
>> checks enormously. Without ownership attributes, approx 10k warnings, with
>> attributes in only a few headers approx 2k warnings (mostly glib headers
>> plus a few in the proprietary parts of the code), and a sampling of those
>> shows that they are mostly real problems (if not likely to occur in actual
>> execution, or of much significance). We have a checked build in our nightly
>> runs, and it has already found the root cause of a number of real issues.
>> >
>> > The patch itself is a git diff since I am working with git-svn, I have
>> options for regenerating it if that's not suitable.
>> >
>> > Please review.
>> >
>> > Thanks,
>> >
>> > Andrew McGregor
>> > Allied Telesis Labs
>> > <clang-ownership-withtests-r3.patch>
>>
>> Hi Andrew,
>>
>> This is looking good, but there are a few things that are worth
>> addressing. A few specific comments:
>>
>> +void OwnershipAttr::Destroy(ASTContext &C) {
>> + if (ArgNums)
>> + C.Deallocate(ArgNums);
>> + AttrWithString::Destroy(C);
>> +}
>> +
>> +void OwnershipTakesAttr::Destroy(ASTContext &C) {
>> + OwnershipAttr::Destroy(C);
>> +}
>> +
>> +void OwnershipHoldsAttr::Destroy(ASTContext &C) {
>> + OwnershipAttr::Destroy(C);
>> +}
>> +
>> +
>> +void OwnershipReturnsAttr::Destroy(ASTContext &C) {
>> + OwnershipAttr::Destroy(C);
>> +}
>> +
>>
>> It looks like all the Destroy methods just call OwnershipAttr::Destroy.
>> Why implement them?
>>
>> Concerning the constructors:
>>
>> +
>> +OwnershipTakesAttr::OwnershipTakesAttr(ASTContext &C, unsigned* arg_nums,
>> + unsigned size, llvm::StringRef
>> module) :
>> + OwnershipAttr(attr::OwnershipTakes, C, arg_nums, size, module, Takes) {
>> + setKind( Takes);
>> +}
>> +
>>
>> Why call setKind() at all? 'Takes' is already passed up to OwnershipAttr.
>> Actually, I think setKind() isn't needed at all; the kind should be
>> immutable for the lifetime of the attribute object.
>>
>> Next, for the definition of OwnershipAttr:
>>
>> +class OwnershipAttr: public AttrWithString {
>> + protected:
>> + unsigned* ArgNums;
>> + unsigned Size;
>> +public:
>> + enum OwnershipKind { Holds, Takes, Returns, None };
>> + OwnershipKind OKind;
>> + attr::Kind AKind;
>> +public:
>> + OwnershipAttr(attr::Kind AK, ASTContext &C, unsigned* arg_nums,
>> unsigned size,
>> + llvm::StringRef module, OwnershipKind kind);
>> +
>> +
>> + virtual void Destroy(ASTContext &C);
>> +
>> + OwnershipKind getKind() const {
>> + return OKind;
>> + }
>> + void setKind(const OwnershipKind k) {
>> + OKind = k;
>> + }
>> + bool isKind(const OwnershipKind k) const {
>> + return OKind == k;
>> + }
>> +
>> + llvm::StringRef getModule() const {
>> + return getString();
>> + }
>> + void setModule(ASTContext &C, llvm::StringRef module) {
>> + ReplaceString(C, module);
>> + }
>> + bool isModule(const char *m) const {
>> + return getModule().equals(m);
>> + }
>>
>>
>> Please add a doxygen comment above OwnershipAttr that describes its
>> purpose. Also, the 'getModule' and 'isAttrArg' aren't obvious in their
>> purpose just from inspecting this class declaration. Please add doxygen
>> comments for them as well.
>>
>> Now on to DiagnosticSemaKinds.td:
>>
>> --- a/include/clang/Basic/DiagnosticSemaKinds.td
>> +++ b/include/clang/Basic/DiagnosticSemaKinds.td
>> @@ -836,6 +836,12 @@ def err_attribute_requires_objc_interface : Error<
>> "attribute may only be applied to an Objective-C interface">;
>> def err_nonnull_pointers_only : Error<
>> "nonnull attribute only applies to pointer arguments">;
>> +def err_ownership_returns_integers_only : Error<
>> + "ownership_returns attribute only applies to integer arguments">;
>> +def err_ownership_takes_pointers_only : Error<
>> + "ownership_takes attribute only applies to pointer arguments">;
>> +def err_ownership_holds_pointers_only : Error<
>> + "ownership_holds attribute only applies to pointer arguments">;
>>
>> Instead of having 3 diagnostics, you only need one. You can pass the name
>> of the attribute when issuing the diagnostic, and use a %0 to represent a
>> string substitution. It makes it more general and reduces clutter.
>>
>> On to the checker itself....
>>
>> +void MallocChecker::PreVisitBind(CheckerContext &C,
>> + const Stmt *AssignE,
>> + const Stmt *StoreE,
>> + SVal location,
>> + SVal val) {
>>
>> Please add a comment above/within this method that says what it is doing.
>> In a previous email, you said:
>>
>> "The PreVisitBind implements the same algorithm as already used by the
>> Objective C ownership checker: if the pointer escaped from this scope by
>> assignment, let it go. However, assigning to fields of a stack-storage
>> structure does not transfer ownership."
>>
>> Please include this in a comment. I have found such documentation to be
>> invaluable, even for code that I wrote myself.
>>
>> + // If ptr is NULL, no operation is preformed.
>>
>> Spelling: performed
>>
>> + if (!val.isZeroConstant()) {
>> +
>>
>> This isn't a generic way to detect of 'val' is null. Val may be a symbol
>> whose value is constrained to be null along a given path. Take a look at
>> the DereferenceChecker on how we detect if a value is null by using 'Assume'
>> on the GRState. That way is completely generic, irrespective of how the
>> argument is written.
>>
>> Now on to the test case:
>>
>> +++ b/test/Analysis/malloc.c
>> @@ -4,22 +4,134 @@ void *malloc(size_t);
>> void free(void *);
>> void *realloc(void *ptr, size_t size);
>> void *calloc(size_t nmemb, size_t size);
>> +void __attribute((ownership_returns(malloc))) *my_malloc(size_t);
>> +void __attribute((ownership_takes(malloc, 1))) my_free(void *);
>> +void __attribute((ownership_returns(malloc, 1))) *my_malloc2(size_t);
>> +void __attribute((ownership_holds(malloc, 1))) my_hold(void *);
>> +// Silly, but not an error. Duplicate has no extra effect.
>> +// If two are of different kinds, that is an error, but the test case
>> doesn't work.
>>
>> This comment doesn't mean anything to me. It feels like a mental dump of
>> something you had in your head, but doesn't really explain anything to
>> someone else. I'm not certain what you are actually referring to when you
>> say "Silly, but not an error." Please clarify this comment.
>>
>> This is looking really good!
>>
>>
>>
>>
> <clang-ownership-withtests-r4.patch>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20100727/c1701f35/attachment.html>
More information about the cfe-dev
mailing list