1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-2-Clause
3import os
4import argparse
5import itertools
6
7# Makefile-fuzz-generated.am is created from this template.
8MAKEFILE_FUZZ = '''# SPDX-License-Identifier: BSD-2-Clause
9# Copyright (c) 2018 Intel Corporation
10# All rights reserved.
11
12if ENABLE_TCTI_FUZZING
13TESTS_FUZZ = %s
14%s
15endif # ENABLE_TCTI_FUZZING
16'''
17# Each fuzz target in Makefile-fuzz-generated.am is created from this template.
18MAKEFILE_FUZZ_TARGET = '''
19noinst_PROGRAMS += test/fuzz/%s.fuzz
20test_fuzz_%s_fuzz_CPPFLAGS = $(FUZZ_CPPFLAGS)
21test_fuzz_%s_fuzz_LDADD    = $(FUZZLDADD)
22nodist_test_fuzz_%s_fuzz_SOURCES  = test/fuzz/main-sapi.cpp \\
23        test/fuzz/%s.fuzz.cpp
24
25DISTCLEANFILES += test/fuzz/%s.fuzz.cpp'''
26# Common include definitions needed for fuzzing an SAPI call
27SAPI_TEMPLATE_HEADER = '''/* SPDX-License-Identifier: BSD-2-Clause */
28/***********************************************************************
29 * Copyright (c) 2018, Intel Corporation
30 *
31 * All rights reserved.
32 ***********************************************************************/
33#ifdef HAVE_CONFIG_H
34#include <config.h>
35#endif
36
37#include <inttypes.h>
38#include <stdbool.h>
39#include <stdio.h>
40#include <string.h>
41#include <poll.h>
42#include <stdarg.h>
43
44#include <setjmp.h>
45
46extern "C" {
47#include "tss2_mu.h"
48#include "tss2_sys.h"
49#include "tss2_tcti_device.h"
50
51#include "tss2-tcti/tcti-common.h"
52#include "tss2-tcti/tcti-device.h"
53
54#define LOGMODULE fuzz
55#include "tss2_tcti.h"
56#include "util/log.h"
57#include "test.h"
58#include "test-options.h"
59#include "context-util.h"
60#include "tss2-sys/sysapi_util.h"
61#include "tcti/tcti-fuzzing.h"
62}
63
64extern "C"
65int
66test_invoke (
67        TSS2_SYS_CONTEXT *sysContext)'''
68# Template to call a SAPI _Complete function which takes no arguments
69SAPI_COMPLETE_TEMPLATE_NO_ARGS = SAPI_TEMPLATE_HEADER + '''
70{
71    %s (sysContext);
72
73    return EXIT_SUCCESS;
74}
75'''
76# Template to call a SAPI _Complete function which takes arguments
77SAPI_COMPLETE_TEMPLATE_HAS_ARGS = SAPI_TEMPLATE_HEADER + '''
78{
79    %s
80
81    %s (
82        sysContext,
83        %s
84    );
85
86    return EXIT_SUCCESS;
87}
88'''
89# Template to call a SAPI _Prepare function
90SAPI_PREPARE_TEMPLATE_HAS_ARGS = SAPI_TEMPLATE_HEADER + '''
91{
92    int ret;
93    %s
94
95    ret = fuzz_fill (
96        sysContext,
97        %d,
98        %s
99    );
100    if (ret) {
101        return ret;
102    }
103
104    %s (
105        sysContext,
106        %s
107    );
108
109    return EXIT_SUCCESS;
110}
111'''
112
113def gen_file(function):
114    '''
115    Generate a cpp file used as the fuzz target given the function definition
116    from a header file.
117    '''
118    # Parse the function name from the function definition
119    function_name = function.split('\n')[0]\
120                            .replace('TSS2_RC', '')\
121                            .replace('(', '')\
122                            .strip()
123    # Parse the function arguments into an array. Do not include sysContext.
124    args = [arg.strip() \
125            for arg in function[function.index('(') + 1:function.index(');')]\
126            .split(',') \
127            if not 'TSS2_SYS_CONTEXT' in arg]
128    # Prepare and Complete functions require different methods of generation.
129    # Call the appropriate function to generate a cpp target specific to that
130    # type of function.
131    if '_Complete' in function_name:
132        return gen_complete(function, function_name, args)
133    if '_Prepare' in function_name:
134        return gen_prepare(function, function_name, args)
135    raise NotImplementedError('Unknown function type %r' % (function_name,))
136
137def gen_complete(function, function_name, args):
138    '''
139    Generate the cpp fuzz target for a SAPI _Complete call
140    '''
141    if not args:
142        # Fill in the no args template. Simple case.
143        return function_name, SAPI_COMPLETE_TEMPLATE_NO_ARGS % (function_name)
144    # Generate the cpp variable definitions.
145    arg_definitions = (';\n' + ' ' * 4).join([
146        arg.replace('*', '') for arg in args]) + ';'
147    # Generate the cpp arguments. For arguments that are pointers find replace *
148    # with & so that we pass a pointer to the definition which has been
149    # allocated on the stack.
150    arg_call = (',\n' + ' ' * 8).join([
151        arg.replace('*', '&').split()[-1] for arg in args])
152    # Fill in the template
153    return function_name, SAPI_COMPLETE_TEMPLATE_HAS_ARGS % (arg_definitions,
154                                                             function_name,
155                                                             arg_call)
156
157def gen_prepare(function, function_name, args):
158    '''
159    Generate the cpp fuzz target for a SAPI _Prepare call
160    '''
161    if not args:
162        return function_name, None
163    # Generate the cpp variable definitions. Make sure to initialize to empty
164    # structs (works for initializing anything) or cpp compiler will complain.
165    arg_definitions = (' = {0};\n' + ' ' * 4).join([
166        arg.replace('*', '').replace('const', '') for arg in args]) + ' = {0};'
167    # Generate the cpp arguments. For arguments that are pointers find replace *
168    # with & so that we pass a pointer to the definition which has been
169    # allocated on the stack.
170    arg_call = (',\n' + ' ' * 8).join([
171        arg.replace('*', '&').split()[-1] for arg in args])
172    # Generate the call to fuzz_fill. The call should be the sysContext, double
173    # the number of arguments for the _Prepare call, and then for each _Prepare
174    # argument pass two to fuzz_fill, the sizeof the _Prepare argument, and a
175    # pointer to it.
176    fill_fuzz_args = (',\n' + ' ' * 8).join([
177        ('sizeof (%s), &%s' % \
178                tuple([arg.replace('*', '').split()[-1]] * 2)) \
179        for arg in args])
180    # Fill in the template
181    return function_name, SAPI_PREPARE_TEMPLATE_HAS_ARGS % (arg_definitions,
182                                                            len(args) * 2,
183                                                            fill_fuzz_args,
184                                                            function_name,
185                                                            arg_call)
186
187def functions_from_include(header):
188    '''
189    Parse out and yield each function definition from a header file.
190    '''
191    with open(header, 'r') as header_fd:
192        current_function = ''
193        for line in header_fd:
194            # Functions we are interested in start with _Complete or _Prepare
195            if '_Complete' in line or '_Prepare' in line:
196                # Set the current_function to this line
197                current_function = line
198            elif current_function and ');' in line:
199                # When we reach the closing parenthesis yield the function
200                yield current_function + line.rstrip()
201                current_function = ''
202            elif current_function:
203                # Add all the arguments to the function
204                current_function += line
205
206def gen_files(header):
207    # Generate a fuzz target cpp file from each function in the header file
208    for current_function in functions_from_include(header):
209        function_name, contents = gen_file(current_function)
210        # Skip the yield if there is no fuzz target that can be generated
211        if contents is None:
212            continue
213        # Yield the function name and the contents of its generated file
214        yield function_name, contents
215
216def main():
217    parser = argparse.ArgumentParser(description='Generate libfuzzer for sapi')
218    parser.add_argument('--header', default='include/tss2/tss2_sys.h',
219            help='Header file to look in (default include/tss2/tss2_sys.h)')
220    args = parser.parse_args()
221
222    functions = dict(gen_files(args.header))
223    # Write the generated target to the file for its function name
224    for function_name, contents in functions.items():
225        filepath = os.path.join('test', 'fuzz', function_name + '.fuzz.cpp')
226        with open(filepath, 'w') as fuzzer_fd:
227            fuzzer_fd.write(contents)
228    # Fill in the Makefile-fuzz-generated.am template using the function names.
229    # Create a list of the compiled fuzz targets
230    files = ' \\\n    '.join(['test/fuzz/%s.fuzz' % (function) \
231                              for function in functions])
232    # Create the Makefile targets for each generated file
233    targets = '\n'.join([MAKEFILE_FUZZ_TARGET % tuple(list(itertools.chain(\
234            ([function] * 6)))) for function in functions])
235    # Write out the Makefile-fuzz-generated.am file
236    with open('Makefile-fuzz-generated.am', 'w') as makefile_fd:
237        makefile_fd.write(MAKEFILE_FUZZ % (files, targets))
238
239if __name__ == '__main__':
240    main()
241