git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 10/10] add UNLEAK annotation for reducing leak false positives

From
Stefan Beller <sbeller@google.com>
Date
Sep 5, 2017, 22:05 UTC
Message-ID
<CAGZ79kYmZJ8k2=SVrx9vpEXoKhRBGwu8J=fS=cYiCLdKf+nDGA@mail.gmail.com>
In-Reply-To
<20170905130505.him3p4jhxp64r2vy@sigill.intra.peff.net>
On Tue, Sep 5, 2017 at 6:05 AM, Jeff King <peff@peff.net> wrote:
>   int main(void)
nit of the day:
  s/void/int argc, char *argv/ or in case we do not
  want to emphasize the argument list s/void//
  as that adds no uninteresting things.
Show 12 quoted lines
>
> In other words, you can do:
>
>   int main(void)
>   {
>         char *p = some_function();
>         printf("%s", p);
>         UNLEAK(p);
>         return 0;
>   }
>
> to annotate "p" and suppress the leak report.
This sounds really cool so far.

After having a sneak peak at the implementation it is O(1) in runtime for each added element, and the space complexity is O(well).

> But wait, couldn't we just say "free(p)"? In this toy
> example, yes. But using UNLEAK() has several advantages over
> actually freeing the memory:
This is indeed the big question, that I have had.
Show 11 quoted lines
>
>   1. It can be compiled conditionally. There's no need in
>      normal runs to do this free(), and it just wastes time.
>      By using a macro, we can get the benefit for leak-check
>      builds with zero cost for normal builds (this patch
>      uses a compile-time check, though we could clearly also
>      make it a run-time check at very low cost).
>
>      Of course one could also hide free() behind a macro, so
>      this is really just arguing for having UNLEAK(), not
>      for its particular implementation.

This is only a real argument in combination with (2), or in other words you seem to hint at situations like these:

  struct *foo = obtain_new_foo();
  ...
  #if FREE_ANNOTATED_LEAKS
    /* special free() */
    release_foo(foo);
  #endif

With UNLEAK this situation works out nicely as we just copy over all memory, ignoring elements allocated inside foo, but for free() we'd have issues combining the preprocessor magic with the special free implementation.

So how would we use syntactic sugar to made this more comfortable? Roughly like

    MAYBE(release_foo(foo))
  #if (FREE_ANNOTATED_LEAKS)
  /* we rely on strict text substitution */
  /* as the function signature may change */
  #define MAYBE(fn) fn;
  #else
  #define MAYBE(fn)
  #endif

Me regurgitating this first argument is just a long way of saying that it put me off even more after reading only the first argument. Maybe reorder this argument to show up after the current second argument, so the reader is guided better?

Show 21 quoted lines
>   2. It's recursive across structures. In many cases our "p"
>      is not just a pointer, but a complex struct whose
>      fields may have been allocated by a sub-function. And
>      in some cases (e.g., dir_struct) we don't even have a
>      function which knows how to free all of the struct
>      members.
>
>      By marking the struct itself as reachable, that confers
>      reachability on any pointers it contains (including those
>      found in embedded structs, or reachable by walking
>      heap blocks recursively.
>
>   3. It works on cases where we're not sure if the value is
>      allocated or not. For example:
>
>        char *p = argc > 1 ? argv[1] : some_function();
>
>      It's safe to use UNLEAK(p) here, because it's not
>      freeing any memory. In the case that we're pointing to
>      argv here, the reachability checker will just ignore
>      our bytes.

This argument demonstrates why the MAYBE above is inferior.

Show 21 quoted lines
>
>   4. Because it's not actually freeing memory, you can
>      UNLEAK() before we are finished accessing the variable.
>      This is helpful in cases like this:
>
>        char *p = some_function();
>        return another_function(p);
>
>      Writing this with free() requires:
>
>        int ret;
>        char *p = some_function();
>        ret = another_function(p);
>        free(p);
>        return ret;
>
>      But with unleak we can just write:
>
>        char *p = some_function();
>        UNLEAK(p);
>        return another_function(p);
  5. It's not just about worrying if we can call UNLEAK
      once (in 4), but we also do not have to worry about
      calling it twice, or recursively. (This argument can be bad
      for cargo cult programmers, but we don't have these ;-)
> +#ifdef SUPPRESS_ANNOTATED_LEAKS
> +extern void unleak_memory(const void *ptr, size_t len);
> +#define UNLEAK(var) unleak_memory(&(var), sizeof(var));
As always with macros we have to be careful about its arguments.
  UNLEAK(a++)
  UNLEAK(baz())
won't work as intended.
Previous: Jeff KingNext: Jeff King
Message 13 of 42 in “towards clean leak-checker output”
  1. 0/10 towards clean leak-checker outputJeff King, Sep 5, 2017
  2. 01/10 test-lib: --valgrind should not override --verbose-logJeff King, Sep 5, 2017
  3. 02/10 test-lib: set LSAN_OPTIONS to abort by defaultJeff King, Sep 5, 2017
  4. 03/10 add: free leaked pathspec after add_files_to_cache()Jeff King, Sep 5, 2017
  5. 04/10 update-index: fix cache entry leak in add_one_file()Jeff King, Sep 5, 2017
  6. 05/10 config: plug user_config leakJeff King, Sep 5, 2017
  7. 06/10 reset: make tree counting less confusingJeff King, Sep 5, 2017
  8. 07/10 reset: free allocated tree buffersJeff King, Sep 5, 2017
  9. 08/10 repository: free fields before overwriting themJeff King, Sep 5, 2017
  10. 09/10 set_git_dir: handle feeding gitdir to itselfJeff King, Sep 5, 2017
  11. Brandon WilliamsSep 7, 2017
  12. 10/10 add UNLEAK annotation for reducing leak false positivesJeff King, Sep 5, 2017
  13. Stefan BellerSep 5, 2017
  14. Jeff KingSep 7, 2017
  15. Stefan BellerSep 7, 2017
  16. Kaartic SivaraamSep 12, 2017
  17. Jeff KingSep 12, 2017
  18. Kaartic SivaraamSep 13, 2017
  19. Martin ÅgrenSep 6, 2017
  20. Jeff KingSep 7, 2017
  21. Kaartic SivaraamSep 12, 2017
  22. Jeff KingSep 12, 2017
  23. Kaartic SivaraamSep 13, 2017
  24. Martin ÅgrenSep 5, 2017
  25. Jeff KingSep 5, 2017
  26. Martin ÅgrenSep 5, 2017
  27. Jeff KingSep 6, 2017
  28. Junio C HamanoSep 6, 2017
  29. 0/2 simplifying !RUNTIME_PREFIXJeff King, Sep 6, 2017
  30. 1/2 system_path: move RUNTIME_PREFIX to a sub-functionJeff King, Sep 6, 2017
  31. Johannes SchindelinSep 6, 2017
  32. Jeff KingSep 6, 2017
  33. 2/2 git_extract_argv0_path: do nothing without RUNTIME_PREFIXJeff King, Sep 6, 2017
  34. 10/10 add UNLEAK annotation for reducing leak false positivesJeff King, Sep 8, 2017
  35. Jonathan TanSep 19, 2017
  36. Jeff KingSep 19, 2017
  37. git-compat-util: make UNLEAK less error-proneJonathan Tan, Sep 19, 2017
  38. Jeff KingSep 19, 2017
  39. git-compat-util: make UNLEAK less error-proneJonathan Tan, Sep 19, 2017
  40. Junio C HamanoSep 20, 2017
  41. Jeff KingSep 20, 2017
  42. Junio C HamanoSep 20, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.