You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
8.4 KiB
241 lines
8.4 KiB
4 months ago
|
#!/usr/bin/env python3
|
||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||
|
import os
|
||
|
import argparse
|
||
|
import itertools
|
||
|
|
||
|
# Makefile-fuzz-generated.am is created from this template.
|
||
|
MAKEFILE_FUZZ = '''# SPDX-License-Identifier: BSD-2-Clause
|
||
|
# Copyright (c) 2018 Intel Corporation
|
||
|
# All rights reserved.
|
||
|
|
||
|
if ENABLE_TCTI_FUZZING
|
||
|
TESTS_FUZZ = %s
|
||
|
%s
|
||
|
endif # ENABLE_TCTI_FUZZING
|
||
|
'''
|
||
|
# Each fuzz target in Makefile-fuzz-generated.am is created from this template.
|
||
|
MAKEFILE_FUZZ_TARGET = '''
|
||
|
noinst_PROGRAMS += test/fuzz/%s.fuzz
|
||
|
test_fuzz_%s_fuzz_CPPFLAGS = $(FUZZ_CPPFLAGS)
|
||
|
test_fuzz_%s_fuzz_LDADD = $(FUZZLDADD)
|
||
|
nodist_test_fuzz_%s_fuzz_SOURCES = test/fuzz/main-sapi.cpp \\
|
||
|
test/fuzz/%s.fuzz.cpp
|
||
|
|
||
|
DISTCLEANFILES += test/fuzz/%s.fuzz.cpp'''
|
||
|
# Common include definitions needed for fuzzing an SAPI call
|
||
|
SAPI_TEMPLATE_HEADER = '''/* SPDX-License-Identifier: BSD-2-Clause */
|
||
|
/***********************************************************************
|
||
|
* Copyright (c) 2018, Intel Corporation
|
||
|
*
|
||
|
* All rights reserved.
|
||
|
***********************************************************************/
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include <config.h>
|
||
|
#endif
|
||
|
|
||
|
#include <inttypes.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <poll.h>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
#include <setjmp.h>
|
||
|
|
||
|
extern "C" {
|
||
|
#include "tss2_mu.h"
|
||
|
#include "tss2_sys.h"
|
||
|
#include "tss2_tcti_device.h"
|
||
|
|
||
|
#include "tss2-tcti/tcti-common.h"
|
||
|
#include "tss2-tcti/tcti-device.h"
|
||
|
|
||
|
#define LOGMODULE fuzz
|
||
|
#include "tss2_tcti.h"
|
||
|
#include "util/log.h"
|
||
|
#include "test.h"
|
||
|
#include "test-options.h"
|
||
|
#include "context-util.h"
|
||
|
#include "tss2-sys/sysapi_util.h"
|
||
|
#include "tcti/tcti-fuzzing.h"
|
||
|
}
|
||
|
|
||
|
extern "C"
|
||
|
int
|
||
|
test_invoke (
|
||
|
TSS2_SYS_CONTEXT *sysContext)'''
|
||
|
# Template to call a SAPI _Complete function which takes no arguments
|
||
|
SAPI_COMPLETE_TEMPLATE_NO_ARGS = SAPI_TEMPLATE_HEADER + '''
|
||
|
{
|
||
|
%s (sysContext);
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
'''
|
||
|
# Template to call a SAPI _Complete function which takes arguments
|
||
|
SAPI_COMPLETE_TEMPLATE_HAS_ARGS = SAPI_TEMPLATE_HEADER + '''
|
||
|
{
|
||
|
%s
|
||
|
|
||
|
%s (
|
||
|
sysContext,
|
||
|
%s
|
||
|
);
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
'''
|
||
|
# Template to call a SAPI _Prepare function
|
||
|
SAPI_PREPARE_TEMPLATE_HAS_ARGS = SAPI_TEMPLATE_HEADER + '''
|
||
|
{
|
||
|
int ret;
|
||
|
%s
|
||
|
|
||
|
ret = fuzz_fill (
|
||
|
sysContext,
|
||
|
%d,
|
||
|
%s
|
||
|
);
|
||
|
if (ret) {
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
%s (
|
||
|
sysContext,
|
||
|
%s
|
||
|
);
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
'''
|
||
|
|
||
|
def gen_file(function):
|
||
|
'''
|
||
|
Generate a cpp file used as the fuzz target given the function definition
|
||
|
from a header file.
|
||
|
'''
|
||
|
# Parse the function name from the function definition
|
||
|
function_name = function.split('\n')[0]\
|
||
|
.replace('TSS2_RC', '')\
|
||
|
.replace('(', '')\
|
||
|
.strip()
|
||
|
# Parse the function arguments into an array. Do not include sysContext.
|
||
|
args = [arg.strip() \
|
||
|
for arg in function[function.index('(') + 1:function.index(');')]\
|
||
|
.split(',') \
|
||
|
if not 'TSS2_SYS_CONTEXT' in arg]
|
||
|
# Prepare and Complete functions require different methods of generation.
|
||
|
# Call the appropriate function to generate a cpp target specific to that
|
||
|
# type of function.
|
||
|
if '_Complete' in function_name:
|
||
|
return gen_complete(function, function_name, args)
|
||
|
if '_Prepare' in function_name:
|
||
|
return gen_prepare(function, function_name, args)
|
||
|
raise NotImplementedError('Unknown function type %r' % (function_name,))
|
||
|
|
||
|
def gen_complete(function, function_name, args):
|
||
|
'''
|
||
|
Generate the cpp fuzz target for a SAPI _Complete call
|
||
|
'''
|
||
|
if not args:
|
||
|
# Fill in the no args template. Simple case.
|
||
|
return function_name, SAPI_COMPLETE_TEMPLATE_NO_ARGS % (function_name)
|
||
|
# Generate the cpp variable definitions.
|
||
|
arg_definitions = (';\n' + ' ' * 4).join([
|
||
|
arg.replace('*', '') for arg in args]) + ';'
|
||
|
# Generate the cpp arguments. For arguments that are pointers find replace *
|
||
|
# with & so that we pass a pointer to the definition which has been
|
||
|
# allocated on the stack.
|
||
|
arg_call = (',\n' + ' ' * 8).join([
|
||
|
arg.replace('*', '&').split()[-1] for arg in args])
|
||
|
# Fill in the template
|
||
|
return function_name, SAPI_COMPLETE_TEMPLATE_HAS_ARGS % (arg_definitions,
|
||
|
function_name,
|
||
|
arg_call)
|
||
|
|
||
|
def gen_prepare(function, function_name, args):
|
||
|
'''
|
||
|
Generate the cpp fuzz target for a SAPI _Prepare call
|
||
|
'''
|
||
|
if not args:
|
||
|
return function_name, None
|
||
|
# Generate the cpp variable definitions. Make sure to initialize to empty
|
||
|
# structs (works for initializing anything) or cpp compiler will complain.
|
||
|
arg_definitions = (' = {0};\n' + ' ' * 4).join([
|
||
|
arg.replace('*', '').replace('const', '') for arg in args]) + ' = {0};'
|
||
|
# Generate the cpp arguments. For arguments that are pointers find replace *
|
||
|
# with & so that we pass a pointer to the definition which has been
|
||
|
# allocated on the stack.
|
||
|
arg_call = (',\n' + ' ' * 8).join([
|
||
|
arg.replace('*', '&').split()[-1] for arg in args])
|
||
|
# Generate the call to fuzz_fill. The call should be the sysContext, double
|
||
|
# the number of arguments for the _Prepare call, and then for each _Prepare
|
||
|
# argument pass two to fuzz_fill, the sizeof the _Prepare argument, and a
|
||
|
# pointer to it.
|
||
|
fill_fuzz_args = (',\n' + ' ' * 8).join([
|
||
|
('sizeof (%s), &%s' % \
|
||
|
tuple([arg.replace('*', '').split()[-1]] * 2)) \
|
||
|
for arg in args])
|
||
|
# Fill in the template
|
||
|
return function_name, SAPI_PREPARE_TEMPLATE_HAS_ARGS % (arg_definitions,
|
||
|
len(args) * 2,
|
||
|
fill_fuzz_args,
|
||
|
function_name,
|
||
|
arg_call)
|
||
|
|
||
|
def functions_from_include(header):
|
||
|
'''
|
||
|
Parse out and yield each function definition from a header file.
|
||
|
'''
|
||
|
with open(header, 'r') as header_fd:
|
||
|
current_function = ''
|
||
|
for line in header_fd:
|
||
|
# Functions we are interested in start with _Complete or _Prepare
|
||
|
if '_Complete' in line or '_Prepare' in line:
|
||
|
# Set the current_function to this line
|
||
|
current_function = line
|
||
|
elif current_function and ');' in line:
|
||
|
# When we reach the closing parenthesis yield the function
|
||
|
yield current_function + line.rstrip()
|
||
|
current_function = ''
|
||
|
elif current_function:
|
||
|
# Add all the arguments to the function
|
||
|
current_function += line
|
||
|
|
||
|
def gen_files(header):
|
||
|
# Generate a fuzz target cpp file from each function in the header file
|
||
|
for current_function in functions_from_include(header):
|
||
|
function_name, contents = gen_file(current_function)
|
||
|
# Skip the yield if there is no fuzz target that can be generated
|
||
|
if contents is None:
|
||
|
continue
|
||
|
# Yield the function name and the contents of its generated file
|
||
|
yield function_name, contents
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser(description='Generate libfuzzer for sapi')
|
||
|
parser.add_argument('--header', default='include/tss2/tss2_sys.h',
|
||
|
help='Header file to look in (default include/tss2/tss2_sys.h)')
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
functions = dict(gen_files(args.header))
|
||
|
# Write the generated target to the file for its function name
|
||
|
for function_name, contents in functions.items():
|
||
|
filepath = os.path.join('test', 'fuzz', function_name + '.fuzz.cpp')
|
||
|
with open(filepath, 'w') as fuzzer_fd:
|
||
|
fuzzer_fd.write(contents)
|
||
|
# Fill in the Makefile-fuzz-generated.am template using the function names.
|
||
|
# Create a list of the compiled fuzz targets
|
||
|
files = ' \\\n '.join(['test/fuzz/%s.fuzz' % (function) \
|
||
|
for function in functions])
|
||
|
# Create the Makefile targets for each generated file
|
||
|
targets = '\n'.join([MAKEFILE_FUZZ_TARGET % tuple(list(itertools.chain(\
|
||
|
([function] * 6)))) for function in functions])
|
||
|
# Write out the Makefile-fuzz-generated.am file
|
||
|
with open('Makefile-fuzz-generated.am', 'w') as makefile_fd:
|
||
|
makefile_fd.write(MAKEFILE_FUZZ % (files, targets))
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|