On Fri, 2017-08-04 at 21:00 -0400, Eric Gallager wrote:
> On 8/4/17, David Malcolm <dmalc...@redhat.com> wrote:
> > This patch kit clearly isn't ready yet as-is (see e.g. the
> > "known unknowns" below), but I'm posting it now in the hope of
> > getting early feedback.
> > 
> > Summary
> > =======
> > 
> > This patch kit provides an easy way to make integrate 3rd-party
> > static
> > analysis tools into gcc, and have them:
> > (a) report through gcc's diagnostic subsystem, and
> > (b) "watermark" the generated binaries with queryable data on what
> > checkers
> >     were run, and what the results were.
> > 
> > Here's an example showing gcc running a bank of 3rd-party checkers
> > on this
> > source file:
> > 
> >   #include <stdlib.h>
> > 
> >   void test ()
> >   {
> >     void *ptr_1;
> >     void *ptr_2;
> > 
> >     ptr_1 = malloc (64);
> >     if (!ptr_1)
> >       return;
> >     ptr_2 = malloc (64);
> >     if (!ptr_2)
> >       return;
> > 
> >     free (ptr_2);
> >     free (ptr_1);
> >   }
> > 
> > via a simple command-line:
> > 
> >   $ ./xgcc -B. -c conditional-leak.c -Wrun-analyzers=policy.json
> >   conditional-leak.c:13:5: error: Potential leak of memory pointed
> > to by
> > 'ptr_1' [clang-analyzer:Memory leak]
> >        return;
> >        ^
> >   conditional-leak.c:8:11: note: state 1 of 4: Memory is allocated
> >      ptr_1 = malloc (64);
> >              ^
> >   conditional-leak.c:9:7: note: state 2 of 4: Assuming 'ptr_1' is
> > non-null
> >      if (!ptr_1)
> >          ^
> >   conditional-leak.c:12:7: note: state 3 of 4: Assuming 'ptr_2' is
> > null
> >      if (!ptr_2)
> >          ^
> >   conditional-leak.c:13:5: note: state 4 of 4: Potential leak of
> > memory
> > pointed to by 'ptr_1'
> >        return;
> >        ^
> >   conditional-leak.c:13:0: error: Memory leak: ptr_1
> > [cppcheck:memleak]
> >        return;
> > 
> > Of the checkers, clang's static analyzer and cppcheck both identify
> > the
> > memory leak; the former also identifies the control flow (the other
> > checkers didn't report anything).
> > 
> > The idea is to provide a mechanism to make it easy for developers
> > and
> > projects to impose policy on what checkers should be run, and to
> > gate
> > the build if certain tests fail.
> > 
> > In this case, the results are treated as hard errors and block the
> > build,
> > but policy could allow them to be warnings.
> > 
> > Extensive metadata is captured about what checkers were run, and
> > what
> > they emitted, using the "Firehose" interchange format:
> > 
> >   http://firehose.readthedocs.io/en/latest/index.html
> > 
> > In the case where this doesn't block the build, this can be queried
> > via a
> >   contrib/get-static-analysis.py
> > script, so e.g. you can verify that a setuid binary was indeed
> > compiled
> > using all the checkers that you expect it to be.
> > 
> > This can also be used to embed data about the code into the
> > watermark.
> > For example, checkers/ianal.py embeds information about "Copyright"
> > lines in the source code into the generated binaries, from where it
> > can be queried (this example is intended as a proof-of-concept
> > rather
> > than as a real license-tracking solution...)
> > 
> > 
> > Statement of the problem
> > ========================
> > 
> > Static analysis is IMHO done too late, if at all: static analysis
> > tools are
> > run
> > as an optional extra, "on the side", rather than in developers'
> > normal
> > workflow, with some kind of "override the compiler and do extra
> > work" hook,
> > which may preclude running more than one analyzer at
> > once.  Analysis
> > results
> > are reviewed (if at all) in some kind of on-the-side tool, rather
> > than when
> > the
> > code is being edited, or patches being prepared.
> > 
> > It would be better to have an easy way for developers to run
> > analyzer(s)
> > as they're doing development, as part of their edit-compile-test
> > cycle
> > - analysis problems are reported immediately, and can be acted on
> > immediately (e.g. by treating some checker tests as being hard
> > errors).
> > 
> > It would also be good to have a way to run analyzer(s) when
> > packages are
> > built, with a variety of precanned policies for analyzers.  For
> > example,
> > setuid binaries and network-facing daemons could each be built with
> > a
> > higher strictness of checking.
> > 
> > It would also be good to tag binaries with information on what
> > analyzers
> > were run, what options they were invoked with, etc.
> > Potentially have "dump_file" information from optimization passes
> > stored
> > in the metadata also.   Have a tool to query all of this.
> > 
> > This way a distribution can perform a query like:
> > 
> >   "show me all setuid binaries that contain code that wasn't
> > checked
> >    with $CHECKER with $TEST set to be a hard error"
> > 
> > Can/should we break the build if there are issues?
> > Yes: but have a way to opt-in easily: if the tool is well-
> > integrated with
> > the
> >     compiler: e.g.
> > 
> > -Wrun-analyzers=/usr/share/analyzers/userspace/network-facing-
> > service
> > then upstream developers and packagers can turn on the setting, and
> > see
> > what
> > breaks, and fix it naturally within an compile-edit-test cycle
> > 
> > This gives a relatively painless way to opt-in to increasing levels
> > of
> > strictness (e.g. by an upstream project, or by an individual
> > developer).
> > 
> > Does this slow the build down?
> > Yes: but you can choose which analyzers run, and can choose to turn
> > them
> > off.
> > It ought to parallelize well.  I believe users will prefer to turn
> > them on,
> > and have builders burn up the extra CPU cycles.
> > This may make much more sense for binary distributions (e.g.
> > Fedora,
> > Debian)
> > that it does for things like Gentoo.
> > 
> > Example policy files/options might be:
> >   -Wrun-analyzers=/usr/share/analyzers/userspace/network-facing-
> > service
> >   -Wrun-analyzers=/usr/share/analyzers/userspace/application
> >   -Wrun-analyzers=/usr/share/analyzers/userspace/setuid-binary
> >   -Wrun-analyzers=/usr/share/analyzers/userspace/default
> >   -Wrun-analyzers=/usr/share/analyzers/kernel
> > 
> > or whatnot.
> > 
> > Idea is to provide mechanism, and for the distribution to decide on
> > some
> > standard policies.
> > 
> > This may also allow us to sandbox a gcc plugin by running the
> > plugin inside
> > another cc1, for plugins that add warnings - if the plugin ICEs,
> > then the
> > main
> > cc1 isn't affected (useful for doing mass rebuilds of code using an
> > experimental plugin).
> > 
> > 
> > Known unknowns
> > ==============
> > 
> > How does one suppress a specific false-positive site?
> > Do we need a pragma for it?  (though pragmas ought to already
> > affect some
> > of
> > the underlying checkers...)
> > 
> > Do we really want .json for the policy format?
> > If we're expecting users to edit this, we need great error
> > messages,
> > and probably support for comments.  Would YAML or somesuch be
> > better?
> > Or have them as individual command-line flags, and the policy files
> > are
> > "@" files for gcc.
> > 
> > How to mark which checkers are appropriate for which languages?
> > 
> > (etc; see also all the FIXMEs in the code...)
> > 
> > 
> > Dependencies
> > ============
> > 
> > The "checkers" subdirectory uses Python 2 or 3, and has a few
> > Python
> > dependencies, including "firehose" and "gccinvocation".
> > 
> > 
> > How it works
> > ============
> > 
> > If enabled, toplev.c starts each of the various checkers from
> > separate
> > threads from near the start of toplev.c, so that the checkers run
> > in
> > parallel with each other, and with the bulk of cc1.  Near the end
> > of
> > toplev.c it waits for each thread to finish, and reads the stdout,
> > which is expected to be in Firehose JSON format.  This is then sent
> > through the diagnostic subsystem.
> > 
> > Each "checker" is a harness script, which "knows" how to invoke
> > the particular 3rd-party tool, and coerce the output from the tool
> > into the common JSON format.
> > 
> > Some notes on the data model can be seen here:
> >   http://firehose.readthedocs.io/en/latest/data-model.html
> > (though that's expressed as Python objects and XML, rather than
> > the JSON format).
> > 
> > 
> > Successfully bootstrapped&regrtested the combination of the patches
> > on x86_64-pc-linux-gnu (though the only testcases are selftest
> > based
> > unit-tests, rather than DejaGnu tests).
> > 
> > 
> 
> General questions:
> 
> 1. When bootstrapping, did you try adding the new -Wrun-analyzers to
> the build system to see what it reports for GCC's own source code?
> It'd be worthwhile to do some dogfooding to determine what kind of
> results it produces

I haven't done that yet; the bootstrapping purely exercised the
selftests (and ensured a lack of warnings on the new code).

> 2. Since -Wrun-analyzers is a warning option, can I turn messages
> from
> it into errors with -Werror=run-analyzers? Will it work with #pragma
> GCC diagnostic push/pop?

Neither of those will work as-is.

The option was actually -frun-analyzers until the day before I posted
the kit; changed it to be a "-W" option was a relatively last-minute
thing, and I think I now prefer it to be -f.

I don't like the patch kit's "monolithic" nature of loading a .json
policy file.  I think my preferred approach now is to pass in a -f
option locating a checker script, and to have extra options controlling
it, and for these to be in @ files.  So something like:

$ gcc -c foo.c @some-policy.opt

[etc]

# Policy file can be built up from policies for individual checkers:

$ cat some-policy.opt

@clang-analyzer.opt
@cppcheck.opt

# An "@"-file for one checker:

$ cat clang-analyzer.opt

-frun-checker=/path/to/checker/clang-analyzer.py
  -fchecker-clang-analyzer=error
  -fchecker-clang-analyzer:"something else"=log

or somesuch.  I'd prefer things to default to errors.

I agree that we'd need some kind of interaction with pragmas, for more
fine-grained control over test ids.

Maybe have e.g.:

-frun-checker=/path/to/checker/some-checker.py
  -Wchecker-clang-analyzer=error
  -Wchecker-clang-analyzer:"some specific test"=log
  -Wchecker-clang-analyzer:"some specific test"=ignore

so the option to add a checker is "-f", but then the tests within the
checker are controlled by "-W".

(I'm thinking aloud here).

> 3. Do we care about duplicated warnings between the analyzers and GCC
> at all? I'm just thinking, if I put in a bug report requesting a new
> warning for GCC after -Wrun-analyzers is added, will people just
> close
> it saying "Oh you can already get that with -Wrun-analyzers" or
> something? That would be disappointing.

There's plenty of duplication between e.g. clang-analyzer and cppcheck;
the more tests the better IMHO.

> 4. Along those lines, how responsible exactly will GCC be for issues
> with the analyzers it runs? For example, I know splint (from [20/22])
> at least is pretty buggy, it crashed 5 times on me when running it
> manually over my fork of gdb. Does GCC really want to encourage the
> use of potentially buggy external tools that might lead to issues?

Although the patch kit has 7 checkers, I found that the only Free ones
that seemed useful in my (limited) testing were clang-analyzer and
cppcheck.

My thinking was to provide the mechanism to allow people to run 3rd-
party analysis tools; whether those tools are any good is for the users
to decide.  But yeah, I agree that there's a potential issue with us
shipping "support" for tools that might not be up-to-scratch.

> ...I thought I had a 5th one but I forget... Anyways, I like the idea
> overall! Keep up the good work!

Thanks
Dave

Reply via email to