[cfe-dev] Ownership attribute for malloc checking, revised patch

Andrew McGregor andrewmcgr at gmail.com
Tue Jul 27 08:23:24 PDT 2010


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!
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20100727/644180b8/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clang-ownership-withtests-r4.patch
Type: application/octet-stream
Size: 34231 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20100727/644180b8/attachment.obj>


More information about the cfe-dev mailing list