Search code examples
frama-c

Frama-C aborted Invalid user input


I am very new to Frama-c and I got an issue when I am trying to open a C source file.

The error shows as 
"fatal error: event.h: No such file or directory. Compilation terminated".

[kernel] Parsing FRAMAC_SHARE/libc/__fc_builtin_for_normalization.i (no preprocessing)

[kernel] Parsing WorkSpace/bipbuffer.c (with preprocessing)

[kernel] user error: failed to run: gcc -E -C -I.  -dD -D__FRAMAC__  -nostdinc -D__FC_MACHDEP_X86_32 -I/usr/share/frama-c/libc -o '/tmp/bipbuffer.ce6d077.i' '/home/xxx/WorkSpace/bipbuffer.c'    you may set the CPP environment variable to select the proper preprocessor command or use the option "-cpp-command".

[kernel] user error: stopping on file "/home/xxx/WorkSpace/bipbuffer.c" that has errors. Add'-kernel-msg-key pp' for preprocessing command.

So bascially I am trying to open a C source file but it returns an error like this. I aslo tried other very simple C files like hello world and other slicing functions, it works well.

I thought it was because I didn't have the dependencies of 'event.h' but it still return these errors after I installed the libevent dependencies. I am not sure if I need to manually set some path of the dependencies for frama-c

Here is part of the C file (Source link: https://memcached.org/) that I would like to open:

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

/* for memcpy */
#include <string.h>

#include "bipbuffer.h"

static size_t bipbuf_sizeof(const unsigned int size)
{
    return sizeof(bipbuf_t) + size;
}

int bipbuf_unused(const bipbuf_t* me)
{
    if (1 == me->b_inuse)
        /* distance between region B and region A */
        return me->a_start - me->b_end;
    else
        return me->size - me->a_end;
}
......

Thanks,


Solution

  • Here's a detailed summary of how you could proceed, using the new -json-compilation-database option from Frama-C 17 Chlorine, plus an extra script list_files.py (which is not in the beta, but will be available in the final 17 release, and can be downloaded here):

    1. Get the source files you want to analyze with Frama-C, run ./configure, and if possible try to disable optional dependencies from external libraries; for instance, some code bases include optional dependencies based on availability of libraries/system features, but have fallback options (resorting to standard C library or POSIX functions). The more you give Frama-C, the better the chances of analyzing it well, so if such external libraries are not essential, excluding them might help get a more "POSIXy" code, which should help. This is typically visible in config.h files, in macros commonly named HAVE_*.

    2. Compile and install Build EAR or some equivalent tool to obtain a compile_commands.json file.

    3. Run bear make (or cmake with flag CMAKE_EXPORT_COMPILE_COMMANDS) to get the compile_commands.json file.

    4. Run the aforementioned list_files.py in the directory containing compile_commands.json to obtain the list of C sources used during compilation.

    5. Run Frama-C (17 Chlorine or newer), giving it the list of sources found in the previous step, plus option -json-compilation-database . to parse the compile_commands.json and, hopefully, get the appropriate preprocessing flags.

    Ideally, this should suffice, but in practice, this is rarely enough. In particular due to the presence of external libraries and non-C99, non-POSIX functions, the following steps are always needed.

    6. Inclusion of external libraries

    At this step, Frama-C will complain about the lack of event.h. You'll have to include the headers of this library yourself. Note: copying headers directly from your /usr/include is not likely to work, due to several architecture-specific definitions, especially files such as bits/*.h..

    Instead, consider downloading the external libraries and preparing them (e.g. running ./configure at least). Then manually add the extra include directory via -cpp-extra-args="-I <path/to/your/sources/for/libevent.h>/include".

    7. Inclusion of missing non-POSIX headers

    Some other headers may be missing, in particular GNU- or BSD-specific sources (e.g. sysexits.h). Get these headers and add them when necessary. The error message in this case comes from the preprocessor (gcc) and is similar to this:

    memcached.c:51:10: fatal error: sysexits.h: No such file or directory
     #include <sysexits.h>
              ^~~~~~~~~~~~
    compilation terminated.
    

    8. Definition of missing non-POSIX types and constants

    At this point, all necessary headers should be available, but parsing with Frama-C may still fail. This is due to usage of non-POSIX type definitions (e.g. caddr_t, struct ling), non-POSIX constants (e.g. MAXPATHLEN, SOCK_NONBLOCK, NI_MAXSERV). Error messages typically resemble the following:

    [kernel] memcached.c:3261: Failure: Cannot resolve variable MAXPATHLEN
    

    Constants are often easy to provide manually, by grepping what's available in your /usr/include.

    Type definitions, on the other hand, may require some copy-pasting at the right places, especially if they depend on other types which are also missing. This step is hardly automatizable, but relatively straightforward once you get used to some specific error messages.

    For instance, the following error message is related to a missing type definition (caddr_t):

    [kernel] Parsing memcached.c (with preprocessing)
    [kernel] memcached.c:1074: 
      syntax error:
      Location: line 1074, between columns 38 and 47, before or at token: c
      1072          *hdr++ = 0;
      1073          *hdr++ = 0;
      1074          assert((void *) hdr == (caddr_t)c->msglist[i].msg_iov[0].iov_base + UDP_HEADER_SIZE);
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      1075      }
      1076
    

    Note that the token just before c is (caddr_t), which has never been defined (it is often defined as either void * or char *).

    The following error message is related to an incomplete type, i.e., a struct used somewhere but never defined:

    [kernel] memcached.c:5811: User Error: 
      variable `ling' has initializer but incomplete type
    

    It means that variable ling's type, which is struct linger (non-POSIX), has never been defined. In this case, we can copy it from our /usr/include/bits/socket.h:

    struct linger
      {
        int l_onoff;            /* Nonzero to linger on close.  */
        int l_linger;           /* Time to linger.  */
      };
    

    Note: if there are POSIX constants/definitions missing from Frama-C's libc, consider notifying its developers, or proposing pull requests in Frama-C's Github.

    9. Fixing incompatible and missing function prototypes

    Parsing is likely to succeed after the previous step, but it may still fail due to incompatible function prototypes. For instance, you may get:

    [kernel] User Error: Incompatible declaration for usleep:
      different integer types int and unsigned int
      First declaration was at assoc.c:238
      Current declaration is at items.c:1573
    

    This is the consequence of a warning emitted earlier:

    [kernel:typing:implicit-function-declaration] slabs.c:1150: Warning: 
      Calling undeclared function usleep. Old style K&R code?
    

    It means that function usleep is called, but it does not have a prototype, therefore Frama-C uses the pre-C99 convention of "implicit int": it generates such a prototype, but later in the code, an actual declaration of usleep is found, and its type is not int. Hence the error.

    To prevent this, you need to ensure usleep's prototype is properly included. Since it is not POSIX.1-2008, you need to either define/undefine the appropriate macros (see unistd.h), or add your own prototype.

    At the end, this should allow Frama-C to parse the files and build an AST.

    However, there are several missing prototypes yet; we were just lucky that none conflicted with actual declarations. Ideally, you'll consider the parsing stage done when there are no more messages such as implicit-function-declaration and similar warnings.

    Some of the missing prototypes in memcached, such as getsubopt, are POSIX and should be integrated into Frama-C's standard library. Others might make part of a small library of non-standard stubs, to be reused for other software.

    Contributing with results for future reuse

    Successful conclusion of the parsing stage for such open source libraries is enough to consider them for integration into this repository of open source case studies, so that future users can start their analyses without having to redo all of these steps. (The repository is oriented towards Eva, but not exclusively: parsing is useful for all of Frama-C plug-ins.)