1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
|
#!/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_CFLAGS = $(FUZZ_CFLAGS)
test_fuzz_%s_fuzz_LDADD = $(FUZZLDADD)
nodist_test_fuzz_%s_fuzz_SOURCES = test/fuzz/main-sys.c \\
test/fuzz/%s.fuzz.c
DISTCLEANFILES += test/fuzz/%s.fuzz.c"""
# Common include definitions needed for fuzzing an SYS call
SYS_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>
#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 "tss2-sys/sysapi_util.h"
#include "tcti/tcti-fuzzing.h"
int
test_invoke (
TSS2_SYS_CONTEXT *sysContext)"""
# Template to call a SYS _Complete function which takes no arguments
SYS_COMPLETE_TEMPLATE_NO_ARGS = (
SYS_TEMPLATE_HEADER
+ """
{
%s (sysContext);
return EXIT_SUCCESS;
}
"""
)
# Template to call a SYS _Complete function which takes arguments
SYS_COMPLETE_TEMPLATE_HAS_ARGS = (
SYS_TEMPLATE_HEADER
+ """
{
%s
%s (
sysContext,
%s
);
return EXIT_SUCCESS;
}
"""
)
# Template to call a SYS _Prepare function
SYS_PREPARE_TEMPLATE_HAS_ARGS = (
SYS_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 c 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 c 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 c fuzz target for a SYS _Complete call
"""
if not args:
# Fill in the no args template. Simple case.
return function_name, SYS_COMPLETE_TEMPLATE_NO_ARGS % (function_name)
# Generate the c variable definitions.
arg_definitions = (";\n" + " " * 4).join(
[arg.replace("*", "") for arg in args]
) + ";"
# Generate the c 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,
SYS_COMPLETE_TEMPLATE_HAS_ARGS % (arg_definitions, function_name, arg_call),
)
def gen_prepare(function, function_name, args):
"""
Generate the c fuzz target for a SYS _Prepare call
"""
if not args:
return function_name, None
# Generate the c variable definitions. Make sure to initialize to empty
# structs (works for initializing anything) or c compiler will complain.
arg_definitions = (" = {0};\n" + " " * 4).join(
[arg.replace("*", "").replace("const", "") for arg in args]
) + " = {0};"
# Generate the c 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,
SYS_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 c 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 sys")
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.c")
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()
|