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