Caution

The tis-fuzz tool is not available in this version.

tis-fuzz Manual

Introduction

This manual explains how to combine fuzzing with the use of TrustInSoft Analyzer.

Combining fuzzing and formal verification strategies

TrustInSoft Analyzer offers two modes:

  • The interpreter mode is similar to testing: the analysis is done on precise values. It is very easy to use, but the coverage is limited by the provided inputs.
  • The analyzer mode, on the contrary to the interpreter mode, works on abstract values that represent, in a condensed way, collection of values. Hence, it provides much better coverage, but it is more difficult to configure and may raise false alarms.

An intermediate solution between these two modes is to keep using the interpreter mode but on a larger set of inputs that are generated by fuzzing. This method may be used to exhibit concrete input values that lead to undefined behaviors detected by the analyzer mode, or also simply to confirm whether a potential undefined behavior may really happen or not.

Combining TrustInSoft Analyzer and the fuzzer with tis-fuzz

The tis-fuzz tool helps to run TrustInSoft Analyzer interpreter mode on input files generated by fuzzing. The fuzzer used by tis-fuzz is the well known AFL (American fuzzy lop) but tis-fuzz can be adapted to other fuzzers on request.

A library is also provided to help preparing an entry point (main function) that is suitable for both fuzzing and analyzing.

Steps to use tis-fuzz

The document below explains how to deal with each step that is needed before using tis-fuzz:

  1. Create an entry point suitable for both the fuzzer and the analyzer.
  2. Compile and link the executable for the fuzzer.
  3. Build and save the analyzer’s internal representation of the application.
  4. Prepare some input file examples.
  5. Run the fuzzer to generate more input files based on these examples.
  6. Use tis-fuzz to run TrustInSoft Analyzer on the inputs generated by the fuzzer.

Requirements

  • At least one analysis of the application must be correctly configured. Please refer to the TrustInSoft Analyzer Manual for information concerning analysis configuration.
  • Some familiarity with the AFL fuzzer may be necessary if it needs to be tuned beyond the basic usage explained below.

Prepare the entry point

The analyzer must be provided an entry point function (usually a main function) to perform the analysis. Such a function usually prepares the initial context by initializing some data using the provided inputs, and then it calls the functions to analyze in this initial context.

The fuzzer, on the other hand, must be provided a compiled executable that reads an input file which holds the generated fuzzed data.

The simplest way to combine analysis with fuzzing is to have a unique entry point usable by both the fuzzer and the analyzer. Therefore, for the entry point function to be compatible with both approaches, it should take a filename as input.

Warning

Notice that to make this work, the analysis entry-point function must be named main since it has to be the same function that is called when running the compiled executable program.

There are three main techniques to integrate the generated inputs with the analyzer:

  • The application takes inputs from a file and the analysis uses a file system model: see section Inputs from a file
  • The analysis takes inputs from the entry point arguments: see section Inputs from a buffer
  • The inputs are generated by TrustInSoft Analyzer functions such as tis_interval built-in: see section Inputs from built-in.

In some rare situations that do not fit in these cases, you may have to create a new main function that fits the fuzzer requirements (i.e. that takes a filename as an argument) and somehow adapt the analysis entry point to read inputs from a file.

Inputs from a file

This strategy supports the analysis of applications that get their inputs from a file.

Assuming that the application to analyze is contained in the user_code.c file, and contains one interesting function to analyze named analyzed_function declared as:

int analyzed_function(FILE *f);

This reasonable very simple main function can be be defined in a tis_main.c file:

File tis_main.c
#include <stdio.h>

/* Function to analyze that takes a file as input. */
int analyzed_function(FILE *f);

/* Entry point for the analysis. Here we assume that this program
   will always be passed an existing readable file as first argument,
   therefore no error checking is performed. */
int main (int argc, char *argv[]) {
  char * filename = argv[1];
  FILE * f = fopen (filename, "r");
  int ret = analyzed_function (f);
  return ret;
}

The corresponding tis_config.json file should contain:

File tis_config.json
{
  "files": [ "tis_main.c", "user_code.c" ],
  "val-profile": "interpreter",
  "val-args": " input.txt",
  "filesystem": {
    "files": [ { "name": "input.txt", "from": "work/real_input.txt" } ]
  }
}

The filesystem section tells that the analysis is done within a file system model that holds an input.txt file, built from the real_input.txt existing file. Moreover, the val-args option is used to provide this file name as input to the entry point.

Since it takes an input file as argument, this main function is directly usable by the fuzzer.

As it is explained later, this real_input.txt file name will have to be provided to tis-fuzz so that, before running an analysis, it can set it to a generated input file.

Tip

Notice that in order to be able to use the filesystem model, the analyses must be performed with the C library provided with TrustInSoft Analyzer. For applications that need to use their own libc, see other solutions below.

One can adapt this method to support more complex applications that provide API functions that accept filename as inputs: the only thing to change is the files section of the tis_config.json and to add the correct preprocessing option (see section Prepare the entry point).

Inputs from a buffer

This strategy supports the analysis of applications that get their inputs from their arguments or their source code.

Transfer file content to a buffer

The analysis inputs may come from the entry point arguments, using the -val-args option, or from some hard-coded constants that are manually changed between the analyses. In that case, one needs to provide a main function that instructs the analyzer to read data from a file on the host that runs the analyzer itself. Either the existing entry point has to be adapted, or a new one has to be added.

The tis_inject_file built-in can be used to transfer the content of a file on the host to a buffer in the analyzed application:

char *tis_inject_file (const char *__filename, unsigned long *__size);

Using this builtin is equivalent to using fopen/fread calls but does not make any file system model visible to the application under analysis: this prevents any risk of influencing the semantics of the analyzed application.

The returned buffer can be used to set the actual input values.

A typical main function implementing this approach is:

File tis_main.c using tis_inject_file
#include <tis_fuzz_runtime.h>

/* Function to analyze that takes a buffer as input. */
int analyzed_function(char * buf, unsigned long len);

int main (int argc, char *argv[]) {
  char * filename = argv[1];
  unsigned long len;
  char * buf = tis_inject_file (filename, &len);
  int ret = analyzed_function(buf, len);
  return ret;
}

Notice that, since the tis_inject_file function is a TrustInSoft Analyzer built-in, the fuzzer cannot use it without having an actual C implementation for this function. Such an implementation is provided by tis-fuzz in the tis_fuzz_runtime.c file. This file has to be compiled and linked when building the instrumented executable for the fuzzer (see Prepare the program for fuzzing).

If the analyzed application does not need to use a virtual file system at all, one may disable the file system support of the analyzer with:

File tis_config.json with no filesystem model
{
  "files": [ "tis_main.c", "user_code.c" ],
  "val-profile": "interpreter",
  "val-args": " input.txt",
  "filesystem": { "enabled": false }
}

Parse inputs from a file

Some work may be needed to parse the inputs from the buffer when the functions to analyze do not read from a buffer. However, some simple cases can be easily handled with these functions provided by the tis_fuzz_runtime.c file:

/* Allocates an [argv] array and fill it by a copy of the strings
   found on each line of the given filename.
   If the file has [n] lines, then [argv] will have [n+1] elements,
   the last one being set to a NULL pointer.
   Notice that the first line of the file holds the program name,
   and the other lines, the arguments.
   The returned value is [n] or 0 if something wrong happened.
   In the later case, [p_argv] is not set, otherwise, it is set to [argv].
*/
int tis_fuzz_inject_argv_split_by_line(char* filename, char**p_argv[]);

/* Same as [tis_inject_file_split_by_line]
   but by using the given [sep] instead of a newline. */
int tis_fuzz_inject_argv_split_by_char(char* filename, char**p_argv[], char sep);

So for instance, if another analyzed_function now takes two strings as arguments, and has been analyzed with the following tis_main entry point:

File tis_main.c with tis_fuzz_inject_argv_split_by_line
/* Function to analyze that takes two strings as input. */
int analyzed_function(char * str1, char * str2);

int tis_main (int argc, char *argv[]) {
  char * arg1 =  argv[1];
  char * arg2 =  argv[2];
  int ret = analyzed_function(arg1, arg2);
  return ret;
}

And analyzed with the following command line:

$ tis-analyzer tis_main.c user_code.c -main tis_main \
               --interpreter -val-args " hello world"

This new main function, in a new_tis_main.c file, can be used to call the previous tis_main function:

File new_tis_main.c
#include <tis_fuzz_runtime.h>

/* old entry point that expect two input strings. */
int tis_main (int argc, char *argv[]);

int main (int argc, char *argv[]) {
  unsigned long len;
  char * filename = argv[1];
  char ** new_argv;
  int new_argc = tis_fuzz_inject_argv_split_by_line (filename, &new_argv);
  int ret = 1;
  if (new_argc >= 3)
    ret = tis_main (3, new_argv);
  return ret;
}

The input strings now come from a file:

$ cat input.txt
my_prog_name
hello
world

And the command becomes:

$ tis-analyzer tis_main.c user_code.c new_tis_main.c \
               --interpreter -val-args " input.txt" -tis-fuzz-runtime

Note that the -tis-fuzz-runtime option is necessary to be able to use the tis_fuzz_inject_argv_split_by_line function.

In this example, the input file only contains strings. However, the format of the input file very much depends on the particular application’s inputs and may need some more advanced parsing to retrieve the arguments.

Inputs from built-in

This strategy supports the analysis of applications that have already been analyzed with TrustInSoft Analyzer. It helps the analysts to confirm potential undefined behaviors or write deterministic bug reports to the application developers.

When using the analyzer mode of TrustInSoft Analyzer, some built-in functions, such as tis_interval, tis_nondet or tis_make_unknown may be used to create abstract input values. One feature of tis-fuzz is to provide C implementations for these functions so that:

  • when used during fuzzing, they read from a file and return concrete values generated by the fuzzer,
  • when used in the interpreter mode of TrustInSoft Analyzer, they use the same file, and the exact same concrete values are returned.

These implementations are provided by tis-fuzz in the tis_fuzz_runtime.c file. So, this source file must be both:

  • included in the analyzed source files, which is done by using the -tis-fuzz-runtime option,
  • compiled and linked when building the executable for the fuzzer.

The implementations of these functions rely on the initialization of the TIS_FUZZED_INPUT global variable with the input filename before calling any of them. Here is a typical main function to configure the analysis:

File tis_main.c with built-ins
/* Function to analyze that takes two numbers as input. */
int analyzed_function(int a, int b);

#include <tis_fuzz_runtime.h>

/* Analysis driver for analyzed_function called all integers between 0 and 1000. */
int main (int argc, char *argv[]) {
  /* This is this only line to add support for fuzzing. */
  TIS_FUZZED_INPUT = argv[1];
  int x = tis_interval (0, 1000);
  int y = tis_interval (0, 1000);
  int ret = analyzed_function(x, y);
  return ret;
}

It can be analyzed with:

$ tis-analyzer tis_main.c user_code.c --interpreter -val-args " input.txt" -tis-fuzz-runtime

Prepare the program for fuzzing

To run the fuzzer, the program must be instrumented and linked with the main function, and the tis_fuzz_runtime.c file if needed, as explained before.

Tip

The tis_fuzz_runtime.c file can be found within the TrustInSoft Analyzer directory at:

"$(tis-kernel -print-share-path)"/tis-fuzz/tis_fuzz_runtime.c

Instrumenting the application depends on your compilation tool chain. For instance, if the program is usually compiled with gcc, it has to be compiled with afl-gcc instead to build a new instrumented binary for ALF.

Tip

If your application is not complete or relies on linker script that have no sense on the host platform, you may want to use -Wl,--unresolved-symbols=ignore-in-object-files. If the missing symbols are not used during the execution this will be enough. Otherwise, your application will crash and you will need to use gdb to understand which additional symbol needs to be provided.

At this point, the instrumented executable should be ready to be used. In the rest of this document, we assume that the instrumented binary is in work/app_for_afl.exe.

Pre-parse the source files

Tip

This step is not mandatory, but highly recommended.

To speed up the future analyses with tis-fuzz, it is recommended to pre-parse the source files with TrustInSoft Analyzer before using tis-fuzz, and to save the result in a project.state file. This file will be used back by TrustInSoft Analyzer for each analysis, saving a lot of time.

To generate this project.state file, use the tis_config.json configuration file written in the previous step (see section Prepare the entry point). If it holds a filesystem section, a local file (named real_input.txt in the example above) is needed to build the file system model. This file is not really used at this step since no analysis is run, but it has to exist, so start with:

$ touch real_input.txt # create a temporary dummy input file

Then, to generate project.state, run:

$ tis-analyzer -tis-config-load tis_config.json -save project.state

Warning

The configuration file must contain "val-profile": "interpreter" at this point. If this field is not given here, the source files will be parsed again during the usage of tis-fuzz, making this optimization to reduce the time of analyses meaningless.

Tip

If, for some reason, one does not want to save the pre-parse source files, the command lines used for the following examples must be the ones that load the tis_config.json configuration file for each analysis instead of loading the project.state file that holds the computed internal representation of the source files.

Prepare input file examples

The fuzzer needs some examples of the expected input files to initialize. It uses them as seeds to generate some new files.

These examples usually come from the existing tests when the application is expected to read some files. If not, their format very much depend on the application inputs as we have seen in the Inputs from a buffer section, and in that cases, at least one file that meets the expected format defined by our main function, has to be created.

Let’s say the examples are in an input-files directory.

Before going further, one needs to confirm that the setup is complete and the fuzzer is ready to run.

  • execute the binary prepared for the fuzzer:

    $ work/app_for_afl.exe input-files/test1.txt
    

    Warning

    This execution is expected to terminate with a 0 status since it is supposed to represent a representative behavior of the program.

  • run the matching analysis:

    • in case the file system model is used, the input file must match the name given in the filesystem specification:
    $ ln -s input-files/test1.txt real_input.txt
    
    • then to run the analysis:
    $ tis-analyzer -tis-config-load tis_config.json -val
    
    • or, using the pre-parsed source files:
    $ tis-analyzer -tis-config-load tis_config.json -save project.state
    $ tis-analyzer -load project.state -val
    

    Both commands should show a behavior similar to the one obtained with the previous execution of work/app_for_afl.exe.

If both commands behave as expected, the fuzzer is now ready to generate new input files and to use tis-fuzz to analyze them.

Run the fuzzer

To generate files in the afl-results directory starting from examples in the input-files, use the following command:

$ afl-fuzz -i input-files -o afl-results -- work/app_for_afl.exe @@

Warning

When running afl-fuzz for the first time, some errors may show up about the system configuration. and may require to tune some system parameters. Please follow the given messages or refer to AFL documentation to get rid of these errors.

The fuzzer may take a lot of time to generate interesting inputs. It can be stopped at any time with Ctrl-C. Another way to limit the number of generated files is either to use afl-fuzz options (such as -V or -E) or to run afl-fuzz through the timeout command (see man timeout).

For other options to fine tune AFL behavior, please refer to its documentation

Use tis-fuzz

Now that everything is ready, tis-fuzz can be used to run the analyses on the generated files.

Specify the pre-parsed source files

To run analyses, tis-fuzz needs:

  • either a previously saved project.state file (see Pre-parsed the source files section):

    $ tis-fuzz --ast tis-results/project.state ...`
    
  • or a tis.config file to generate it:

    $ tis-fuzz --tis-config tis_config.json ...
    
  • or, if the tis_config.json file holds several configurations, the name of the configuration to use has to be specified:

    $ tis-fuzz --tis-config tis_config.json --tis-config-name prepare ...
    

Using an abstract file system

When the inputs are retrieved from a file in an abstract file system visible to the application, the name of the input file has been chosen to be always the same. This name has to be provided to tis-fuzz:

$ tis-fuzz --mkfs real_input.txt ...

For each run of the analyzer, tis-fuzz will make a symbolic link from this name to the actual input file that has been generated by the fuzzer. So one must ensure that:

  • the file does not already exist (otherwise, it would be erased)
  • the link with the specified name can be created.

Moreover, because of this link, it is not possible to run several analyses at the same time, so all the parallelization features of tis-fuzz are disabled.

Analyze existing input files

The tis-fuzz tool takes input files from a directory (specified with the --input option)

$ tis-fuzz --input afl-results ...

This directory must have the crashes and queue subdirectories generated by AFL. It may also have files in the hangs subdirectories, but they are ignored by tis-fuzz since they may take too much time to analyze.

The analysis results are generated in a directory (specified with the --output option)

$ tis-fuzz --output tis-results ...

The default is to analyze all the files that have not been analyzed before, but a maximum number of analyses can be specified using the --max option.

When all the files have been processed, the results are analyzed with tis-report to generate an HTML summary of the results in an HTML file (its name may be specified with the --report option).

Analyze new input files

With the --dynamic option, the tis-fuzz tool can also be run in parallel to the fuzzer. In that case, it first analyses the existing files as before, and then it watches for the new generated files, and analyzes them as soon as when they are generated. This behavior is disabled if the inotifywait command is not available in the PATH, (or when the --no-dynamic option is used, which is the default).

The number of analysis can be controlled with the --max option as before, otherwise, the tool will keep watching the input directory. To stop watching for new file, look for this message in the output:

To stop waiting for new input file, use: kill 12345

Using the specified kill command stops the watching loop, and tis-report is called at the end.

Running analyses in parallel

tis-fuzz relies on GNU parallel to run several analyses simultaneously. This can be disabled with the --no-parallel option.

The parallel command can be passed a lot of options: they must be given to the --parallel-extra-args option of tis-fuzz.

Here is a short list of some of the most commonly used:

  • --jobs: several ways to specify the number of analysis to run in parallel,
  • --timeout: maximum time of each analysis (example: --timeout 3600)
  • --memfree: minimum memory free when starting another job (example: --memfree 20G)

Please refer to the GNU parallel program documentation for more advanced customization.

When the parallel program is not available, or if the --no-parallel option is used, xargs is used instead. The tis-fuzz --jobs option can still be used to run several analyses in parallel, however, in constrast to parallel that accept a very rich desscription, it only accepts a positive integer in that case (see -P or --max-procs in man xargs).

Full examples

Three full examples are presented below. They use different features that has been presented that can be mixed up in use cases, but some of them are incompatible, such as using a file system model and using the parallel mode for instance.

With a file system model

The first example is the one introduced in the Inputs from a file section. The directory holds:

.
├── input-files
│   └── test1.txt
├── tis_config.json
├── tis_main.c
└── user_code.c

The tis_main.c and tis_config.json files have already been shown above, and, for the demonstration, the analyzed_function is implemented in this user_code.c file:

File user_code.c
#include <stdio.h>
int analyzed_function(FILE *f) {
  char read_buf[10];
  size_t r = fread (read_buf, sizeof (read_buf[0]), sizeof (read_buf), f);
  if (r == 0) {
    return -1;
  }
  int s = 0;
  for (int i = 0; i < r; i++) {
    char c = read_buf[i];
    if ('0' <= c && c <= '9') {
      c -= '0';
      s = 10 * s + c;
    }
    else
      break;
  }
  printf ("Result=%d\n", s);
  return s;
}

It reads numbers from the input file, computes a single number from them, and prints the result.

The test file input-files/test1.txt holds:

File input-files/test1.txt
0123456xyz

First of all, a work directory is created to hold the generated files, and then, since the file system model specified in the tis_config.json file expects to find the input file in work/real_input.txt, let’s create a symbolic link to the test file:

$ mkdir work
$ ln --symbolic --relative input-files/test1.txt work/real_input.txt

Now, the project.state file can be computed and saved, and, even if this step is optional, we also check that the analysis of the test file behaves as expected:

$ tis-analyzer -tis-config-load  tis_config.json -save work/project.state
$ tis-analyzer -load work/project.state -val

As expected, it prints:

Result=123456

Now, let’s compile the instrumented executable with the AFL compiler, and check if it behaves as expected:

$ afl-clang tis_main.c user_code.c -o work/app_for_afl.exe
$ work/app_for_afl.exe work/real_input.txt

Indeed, it also prints:

Result=123456

Now, the link to the input file has to be removed, since tis-fuzz needs to create a link to the generated files:

$ rm work/real_input.txt

Here, we first run AFL to generate files, before running tis-fuzz:

$ afl-fuzz -i input-files -o work/afl-results \
           -- work/app_for_afl.exe @@

The execution has to be stopped with Ctrl-C (of the -E option of afl-fuzz to specify the number of executions to run before stopping).

Then, tis-fuzz can be run:

$ tis-fuzz --ast work/project.state \
           --mkfs work/real_input.txt \
           --input-dir work/afl-results \
           --output-dir work/tis-results

The generated self-contained HTML report is available in work/tis-results/tis_report.html and may be opened with any browser.

Using built-ins

This second example is the one introduced in the Inputs from a built-in. The directory holds:

.
├── input-files
│   └── test1.txt
├── tis_main.c
└── user_code.c

The tis_main.c file has already been shown above, and, the analyzed_function is implemented in this user_code.c file:

File user_code.c using built-ins
#include <stdio.h>

int analyzed_function(int a, int b) {
  int r = 0;
  if (a > 100) r++;
  if (b > 100) r++;
  printf ("Inputs='%d' and '%d' -> Result=%d\n", a, b, r);
  return r;
}

This times, it takes two numbers, just prints them, and returns 0.

The generated files will be put in a work directory that has to be created first:

$ mkdir work

Because the main function must share the implementation of the tis_interval function with AFL, the -tis-fuzz-runtime option must be used. This time, to show another way of building the project.state file, no tis_config.json is used, and thus, the command is:

$ tis-analyzer tis_main.c user_code.c -tis-fuzz-runtime \
               -val-profile interpreter:no-mkfs -save work/project.state

To produce the instrumented executable with the AFL compiler, we also need to link with the tis_fuzz_runtime.c file that provides the implementation of the tis_interval function for AFL. Moreover, it requires to specify the path to the included files. So the command is:

$ afl-clang tis_main.c user_code.c \
            -I "$(tis-kernel -print-share-path)"/tis-lib \
            -I "$(tis-kernel -print-share-path)"/tis-fuzz \
            "$(tis-kernel -print-share-path)"/tis-fuzz/tis_fuzz_runtime.c \
            -o work/app_for_afl.exe

An input file is needed as a seed for the generation. For this example, this one is used:

File input-files/test1.txt (when using built-ins)
123456

When using the built-ins functions, the relation between the input file and the real inputs is not straightforward, but whatever the content of the file, the built-ins ensures that the inputs satisfy the constraints, i.e. that they are numbers between 0 and 1000 as expected in this example. Moreover, we can check that both the analysis and the execution produce the same result:

$ tis-analyzer -load work/project.state -val -val-args ' input-files/test1.txt'
Inputs='582' and '95' -> Result=1

And:

$ work/app_for_afl.exe input-files/test1.txt
Inputs='582' and '95' -> Result=1

This time, tis-fuzz is used in parallel to afl-fuzz. First, launch afl-fuzz:

$ afl-fuzz -i input-files -o work/afl-results \
           -- work/app_for_afl.exe @@

Then, in another terminal, launch tis-fuzz with the --dynamic option:

$ tis-fuzz --ast work/project.state \
           --input-dir work/afl-results \
           --output-dir work/tis-results \
           --dynamic

Since the program is so simple, there are not so many generated files. After some time, afl-fuzz can be stopped with Ctrl-C, and the kill command printed by tis-fuzz can be run in order to break the waiting loop.

The generated self-contained HTML report is available in work/tis-results/tis_report.html and may be opened with any browser.

Using tis_fuzz_inject_argv_split_by_line

This third example is the one introduced in the Parse inputs from a file section. It demonstrates how to reuse a tis_main function that takes its inputs from the entry point arguments (using the -val-args option).

The directory holds:

.
├── input-files
│   └── test1.txt
├── new_tis_main.c
├── tis_main.c
└── user_code.c

The tis_main.c and new_tis_main.c files have already been shown above, and, the analyzed_function is implemented in this user_code.c file:

File user_code.c using tis_fuzz_inject_argv_split_by_line
#include <stdio.h>
#include <string.h>
int analyzed_function(char * str1, char * str2) {
  int r = 0;
  if (strlen (str1) == 5) r++;
  if (strlen (str2) == 5) r++;
  printf ("Inputs='%s' and '%s' -> Result=%d\n", str1, str2, r);
  return r;
}

The program takes two strings, prints them, and returns 0.

The generated files will be put in a work directory that has to be created first:

$ mkdir work

To be able to use the tis_fuzz_inject_argv_split_by_line function, the -tis-fuzz-runtime option must be used. So, to build project.state, the command is:

$ tis-analyzer tis_main.c user_code.c new_tis_main.c -tis-fuzz-runtime \
               -val-profile interpreter:no-mkfs -save work/project.state

The program must also be linked with the tis_fuzz_runtime.c file to produce the instrumented executable:

$ afl-clang tis_main.c user_code.c new_tis_main.c \
            -I "$(tis-kernel -print-share-path)"/tis-lib \
            -I "$(tis-kernel -print-share-path)"/tis-fuzz \
            "$(tis-kernel -print-share-path)"/tis-fuzz/tis_fuzz_runtime.c \
            -o work/app_for_afl.exe

The test file is:

File input-files/test1.txt (when using tis_fuzz_inject_argv_split_by_line)
my_prog_name
hello
world

And we can check that both the analysis and the execution provide the same result:

$ tis-analyzer -load work/project.state -val -val-args ' input-files/test1.txt'
Inputs='hello' and 'world' -> Result=2

And:

$ work/app_for_afl.exe input-files/test1.txt
Inputs='hello' and 'world' -> Result=2

As before, the following commands can be used to run:

  • AFL to generate files:

    $ afl-fuzz -i input-files -o work/afl-results \
               -E 100 \
               -- work/app_for_afl.exe @@
    
  • then tis-fuzz to analyse them (--max 12 to run 12 analyses before stopping, and --jobs 6 to run 6 analyses in parallel):

    $ tis-fuzz --ast work/project.state \
               --input-dir work/afl-results \
               --output-dir work/tis-results \
               --max 12 --jobs 6
    
  • and finally, the generated self-contained HTML report is available in work/tis-results/tis_report.html and may be opened with any browser.