facebook / infer

A static analyzer for Java, C, C++, and Objective-C
http://fbinfer.com/
MIT License
14.98k stars 2.02k forks source link

"Skipped call: function or method not found" #700

Open hmijail opened 7 years ago

hmijail commented 7 years ago

Infer version v0.12.0-2dcde3a8

Does this bug report mean that Infer didn't find the called function? As far as I can tell, the skipped function was compiled correctly together with the rest of the source code. What could cause Infer to not find it? Is there any way to help Infer find it?

sblackshear commented 7 years ago

Yes, that's what the report means. This can happen for a variety of reasons: the skipped method might be native code, an interface method, an abstract method, or a lambda.

Can you provide a small example, self-contained showing the skipped method? It might be possible to help Infer find it, but it's hard to know why it might have skipped the method without more details.

hmijail commented 7 years ago

This is all plain C code, so I guess that something more basic must be going on.

Some of the skipped functions are function pointers. Of course a static analyzer won't do anything there, though it'd be nice to be able to annotate, model, etc. the function call; AFAICS the Infer docs only mention modelling plain function calls, not function pointers. Can this be done? (by the way, the "adding models" docs don't say where should one place the models and how to build them)

But some of the skipped functions I'm seeing are plain C functions that were built in the same batch as the calling function. The compilation and linking finished successfully, and the resulting binary works; so the code should be accessible to Infer too.

Looking at the build process, the only unusual-ish thing I see is that object files were turned into static libraries with ar before linking into an executable; and the ar step was not captured by Infer. Could this explain anything? Though if this was the case I guess that Infer would be skipping a lot more of functions.

I will try to generate an example.

sblackshear commented 7 years ago

There's currently no way to model function pointers, but we could potentially add this feature. The doc mentions that models go under infer/models/c/src. You're right that it neglects to mention how to build them; make -C infer/models should do the trick.

Not sure why Infer is skipping plain C functions whose source is available; that probably shouldn't happen. The example would really help us debug this.

hmijail commented 7 years ago

The doc mentions that models go under infer/models/c/src

Ah, thank you. I understood that there go the infer-included models, and expected that the user-generated models would go somewhere else (probably project-specific).

Regarding the skipped functions: turns out that just compiling the CCAN JSON library by itself already causes skipped function problems. The skipped functions are present in the very same Translation Unit, so I guess this makes no sense.

In my particular case, this is the error:

...JSON/src/json.c:1038:3: Skipped call: function or method not found
1036.       sb_putc(out, '[');
1037.       json_foreach(element, array) {
1038.           emit_value(out, element);
          ^
1039.           if (element->next != NULL)
1040.               sb_putc(out, ',');

But emit_value was defined in line 978 of the same file:

static void emit_value(SB *out, const JsonNode *node)
{

FWIW, I have tried using creduce to generate a reduced test case, but infer's output is convoluted enough that it might take some effort/creativity to make a good "interestingness test" from it. Is there any preferred way for accessing infer's output programatically?

hmijail commented 7 years ago

After trying a bit more with creduce, it generated this test case:

char e, f;
void *realloc();
typedef struct { char *a; } b;
static void c();
b d;
void g() {
  b *h = &d;
  long i = &f - h->a, a = &e - h->a;
  h->a = realloc(h->a, a) + i;
  c(&d);
}
void c() {}

Which causes this bug report:

json_prep.c:10: error: MEMORY_LEAK
  memory dynamically allocated by call to `realloc()` at line 9, column 10 is not reachable after line 10, column 3.
Showing all 5 steps of the trace
[...]
json_prep.c:10:3: Skipped call: function or method not found
8.     long i = &f - h->a, a = &e - h->a;
9.     h->a = realloc(h->a, a) + i;
10.     c(&d);
        ^
11.   }
12.   void c() {}
hmijail commented 7 years ago

Note that c() is defined right there, but Infer is reporting it as "not found".

For context, this was generated by using creduce on the json.c file from the mentioned library CCAN JSON. I haven't tried simplifying it further.

jvillard commented 7 years ago

I wonder if #604 is hitting the same issue. Thanks for the repro @hmijail!

JacobBarthelmeh commented 6 years ago

Just giving a bump to this issue and am curious if there was a work around / fix in place for it? Am encountering a similar issue. My case is also in pure C code. Am building a C library using make.

I've taken hmijail's example and simplified it a little more.

#include <stdio.h>
#include <stdlib.h>

typedef struct { char *a; } b;
static void foo(b* in);
void main() {
    b d;
    b *h = &d;
    h->a = (char*)malloc(2);
    foo(&d);
    free(h->a);
}
void foo(b* in) {}

and with infer version

Infer version v0.13.1
Copyright 2009 - present Facebook. All Rights Reserved.

Am seeing

$ ~/Documents/infer-osx-v0.13.1/infer/bin/infer run -- clang infer-testcase.c 
Capturing in make/cc mode...
Found 1 source file to analyze in ~/Documents/testing/infer-test/infer-out
Starting analysis...

legend:
  "F" analyzing a file
  "." analyzing a procedure

F..

Found 1 issue

infer-testcase.c:10: error: MEMORY_LEAK
  memory dynamically allocated to `d.a` by call to `malloc()` at line 9, column 19 is not reachable after line 10, column 5.
  8.       b *h = &d;
  9.       h->a = (char*)malloc(2);
  10. >     foo(&d);
  11.       free(h->a);
  12.   }

Summary of the reports

  MEMORY_LEAK: 1

When veiwing the report with "inferTraceBugs --html" it lists the function foo as skipped.

infer-testcase.c:10:5: Skipping foo(): function or method not found
8.       b *h = &d;
9.       h->a = (char*)malloc(2);
10.       foo(&d);
          ^
11.       free(h->a);
12.   }