diff --git a/.remill_commit_id b/.remill_commit_id index a822a68a1..5fbd18712 100644 --- a/.remill_commit_id +++ b/.remill_commit_id @@ -1 +1 @@ -9bddfa16afa6fea266c27b3538380ee17d75cb4b \ No newline at end of file +3b9b36f4efbfe46ef82f1eaae8d117b3755878e0 diff --git a/tests/integration_tests/README.md b/tests/integration_tests/README.md new file mode 100644 index 000000000..4f572fe4c --- /dev/null +++ b/tests/integration_tests/README.md @@ -0,0 +1,153 @@ +New attempt to create a better test suite. In development process, use at your own peril. + +# Use: + + +1) You can use `populate.py` to copy binaries to be tested from `/bin` or `/usr/bin`. +Directory that contains configs is iterated and each binary that has present at least one config file is searched for. +In case it is not found error is written on standard output, but the script continues with the rest. + + Example: + + ``` + ./populate.py + > not_exists not found anywhere + > Found /bin/echo + > Found /bin/grep + ``` + +2) In case you want to try your own sources you can use `compile.py` which compiles all sources from `src` directory. If option `--stub_tags` is used, the corresponding tag files are created. + +3) First create a batch of cfg file using `get_cfg.py` -- you can select tags to run only subset of all tests (they are specified in `tags` directory). + + tag all will get cfg for every file with at least one config present + + there are several policies that allows modification of already existing batches + + Example: + + ``` + # Every file present in the `bin` directory will be lifted + # into cfg file. + # If a batch with name first_batch is present it will be deleted. + + python get_cfg.py --disass dyninst --tags all + --batch first_batch --batch_policy D + + # Lifts all missing files that have tags C. + # In case some file has already present cfg, + # it is not replaced + + python get_cfg.py --disass dyninst --tags C + --batch first_batch --batch_policy C + + # Updates all files with tag echo, leaves rest of the batch untouched + + python get_cfg.py --disass dyninst --tags echo + --batch first_batch --batch_policy U + ``` + +4) Once batch is created `run_tests.py` can be run. + +# Config/Test files (tags/): + +Directory tags (name to be changed) contains two types of files (beware, whitespaces are used as delimiters, therefore they matter): + +1) `binary.kind.config`, which has following internal structure: + + ``` + TAGS: one two ... + LIFT_OPTS: +one +two 87 !three + ``` + + `TAGS` specify tags of this config, while `LIFT_OPTS` represent specific lift options. Options prefixed by `+` are added and prefix `!` means that the options is not used even though it would normally be by default. + One binary can have multiple config files. + + At the moment each config should contain exactly one of the following two tags: `c`, `cpp`. They are later used to determine which compiler to use when recompiling. + +2) `binary.test`, which has following internal structure: + + ``` + TEST: cmd_option1 ... + STDIN: -Fpath/to/file/ or string + FILES: Not implemented yet + TEST: ... + ... + ``` + + For simple test cases it is easier to write this kind of test specification than the one used in python sources. + + `STDIN:` after file is optional and so is `FILES:`. If value of `STDIN:` is prefixed by F corresponding file is loaded (it can be relative to the root of test dir) and used as stdin. + +# src/ + +Files presented in `src` are meant to be compiled by `compile.py` and can have special header: +``` +/* TAGS: ... */ +/* CC_OPTS: ... */ +/* LD_OPTS: ... */ +/* LIFT_OPTS: kind1 ... */ +/* LIFT_OPTS: kind2 ... */ +... +/* TEST: */ +/* STDIN: */ +... +``` +Everything except `CC_OPTS:` and `LD_OPTS:` is used to generated appropriate `.config/.test` files. `CC_OPTS:` and `LD_OPTS:` are forwarded to the compiler. + +# Complex tests + +Not every test can be described by simple "language" of the `.test` files, therefore it is needed to have a way to define those more complex ones. +In `run_tests.py` a global array `g_complex_test` is present, which is used to store more advanced configurations. It has following data type: + +``` +{ str : [ TestDetails ] } +``` +The structure is rather intuitive -- each binary has one entry and can have multiple test cases stored in array of `TestDetails`. `TestDetails` currently have following options: +``` +cmd (set in __init___): array of command line arguments +files: files that are used by the program + -> it is needed to copy them into appropriate location +check: files that are output of the program + -> it is needed to compare them +``` +As usual, files can be specified by relative paths from root directory of tests. + + + +# Directory structure: + +``` +get_cfg.py # script to create new cfg files +run_tests.py # runs the selected batches +populate.py # tries to copy binaries from `/bin` or `/usr/bin` based on tags files +bin + |- base_test + |- ... rest of tested binaries + +tags + |- base_test.default.config + |- ... for each binary at least one config file + |- base.test + |- .. optional test files + +# Folders containing cfg files (there may be a reason you want to have several, +# for example compare frontends or have cfgs of some special flavor) +batch1_cfg + |- base_test.cfg + |- ... + +batch2_cfg + |- base_test.cfg + |- ... + +# Symlinks to shared libraries used by original binaries +# Links themselves are created by get_cfg.py +shared_libs + |- libc.so + |- ... + +# Inputs for the run_tests.py +inputs +|- input1.txt +|- program1 +|- ... +``` diff --git a/tests/integration_tests/colors.py b/tests/integration_tests/colors.py new file mode 100644 index 000000000..e0900a99f --- /dev/null +++ b/tests/integration_tests/colors.py @@ -0,0 +1,74 @@ +# Copyright (c) 2019 Trail of Bits, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class Colors: + class c: + green = '\033[92m' + yellow = '\033[93m' + red = '\033[91m' + magneta = '\033[95m' + bg_yellow = '\033[43m' + orange = '\033[38;5;202m' + RESET = '\033[0m' + + +def get_result_color(total, success): + if total == 0: + return Colors.c.magneta + if total == success: + return Colors.c.green + if success == 0: + return Colors.c.red + return Colors.c.yellow + +def get_bin_result(result): + if result == 1: + return Colors.c.green + if result == 0: + return Colors.c.red + return Colors.c.magneta + +def clean(): + return Colors.RESET + +def c(color, message): + return color + message + clean() + +def fail(): + return Colors.c.red + +def succ(): + return Colors.c.green + +#TODO: Not sure if it's worth to generate these for each color from attrs dynamically +def green(message): + return c(Colors.c.green, message) + +def red(message): + return c(Colors.c.red, message) + +def yellow(message): + return c(Colors.c.yellow, message) + +def magneta(message): + return c(Colors.c.magneta, message) + +def bg_yellow(message): + return c(Colors.c.bg_yellow, message) + +def orange(message): + return c(Colors.c.orange, message) + +def id(message): + return message diff --git a/tests/integration_tests/compile.py b/tests/integration_tests/compile.py new file mode 100755 index 000000000..4ed626dbb --- /dev/null +++ b/tests/integration_tests/compile.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019 Trail of Bits, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os +import subprocess + +tags_dir = "tags" +bin_dir = "bin" +src_dir = "src" + +cxx_comp = 'clang++' +cc_comp = 'clang' + +class Config: + allowed = ['TAGS', 'CC_OPTS', 'LD_OPTS', 'LIFT_OPTS', 'TEST'] + + def __init__(self, filename): + self.lift_opts = [] + self.tests = [] + self.cc_opts = [] + self.ld_opts = [] + self._parse_header(filename) + + def _cc_opts(self, opts): + # Strip leading option specifier + self.cc_opts = opts[1:] + + def _ld_opts(self, opts): + # Strip leading option specifier + self.ld_opts = opts[1:] + + def _lift_opts(self, opts): + self.lift_opts.append((opts[1], opts[2:])) + + def _tags(self, opts): + self.tags = opts[1:] + + def _test(self, opts): + self.tests.append((opts[1:], None)) + + def _stdin(self, opts): + test, stdin = self.tests[-1] + if stdin is not None: + raise Exception("Two consecutive STDINs are not allowed") + self.tests[-1] = (test, ' '.join(opts[1:])) + + def _parse_header(self, filename): + basename, ext = os.path.splitext(os.path.basename(filename)) + self.name = basename + self.ext = ext + with open(filename, 'r') as src: + while 1: + line = src.readline() + + line = line.rstrip('\n') + tokens = line.split(' ') + + # Arrived at line that is not config information + if not tokens or (tokens[0] != '/*' or tokens[-1] != '*/'): + return + + tokens = tokens[1:][:-1] + dispatch = { + 'TAGS:' : Config._tags, + 'CC_OPTS:' : Config._cc_opts, + 'LD_OPTS:' : Config._ld_opts, + 'LIFT_OPTS:' : Config._lift_opts, + 'TEST:' : Config._test, + 'STDIN:' : Config._stdin, + } + + if tokens[0] not in dispatch: + raise Exception(tokens[0] + " is not allowed as entry header!") + dispatch[tokens[0]](self, tokens) + + def create_config(self, name, opts, dst_dir): + with open(os.path.join(dst_dir, self.name + '.' + name + '.config'), 'w') as cfg: + cfg.write("TAGS: " + ' '.join(self.tags) + '\n') + cfg.write("LIFT_OPTS: " + ' '.join(opts) + '\n') + + def create_test(self, dst_dir): + with open(os.path.join(dst_dir, self.name + '.test'), 'w') as test: + for case, stdin in self.tests: + test.write(' '.join(case) + '\n') + if stdin is not None: + test.write('STDIN: ' + stdin + '\n') + + + def create_configs(self, dst_dir): + if not self.lift_opts: + self.create_config('default', [''], dst_dir) + else: + for name, opts in self.lift_opts: + self.create_config(name, opts, dst_dir) + self.create_test(dst_dir) + + def compile(self): + ext_map = { '.cpp' : cxx_comp, + '.c' : cc_comp } + + cc = ext_map.get(self.ext, None) + if cc is None: + print(" > " + self.name + " has unknown extension") + return + + out = os.path.join(bin_dir, self.name) + args = [cc, os.path.join(src_dir, self.name + self.ext), '-o', out] \ + + self.cc_opts + self.ld_opts + + print(args) + pipes = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.PIPE) + std_out, std_err = pipes.communicate() + ret_code = pipes.returncode + + if ret_code: + print("*** Compilation failed ***") + print("\n** stdout:") + print(std_out) + print("\n** stderr:") + print(std_err) + +def main(): + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument( + '--stub_tags', + help='Create tag files with corresponding tags, possibly empty', + required=False, + nargs='*') + + arg_parser.add_argument( + '--cxx', + help='Path to C++ compiler to use', + required=False) + + arg_parser.add_argument( + '--cc', + help='Path to C compiler to use', + required=False) + + args, extra_args = arg_parser.parse_known_args() + + if args.cc is not None: + global cc_comp + cc_comp = args.cc + + if args.cxx is not None: + global cxx_comp + cxx_comp = args.cxx + + # If `bin` does not exist create it first + if not os.path.isdir(bin_dir): + os.mkdir(bin_dir) + + for f in os.listdir(src_dir): + c = Config(os.path.join(src_dir, f)) + if args.stub_tags is not None: + c.tags += args.stub_tags + c.create_configs('tags') + c.compile() + +if __name__ == '__main__': + main() diff --git a/tests/integration_tests/get_cfg.py b/tests/integration_tests/get_cfg.py new file mode 100755 index 000000000..3da537937 --- /dev/null +++ b/tests/integration_tests/get_cfg.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019 Trail of Bits, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import operator +import os +import queue +import threading +import shutil +import sys +import subprocess +import tempfile +from shlex import quote + +import colors +import util + +tags_dir="tags" +so_dir="shared_libs" +bin_dir="bin" + +ALL_TAG="all" + +FAIL = 0 +SUCCESS = 1 +IGNORED = 2 + +MESSAGES = { FAIL: "Fail", SUCCESS: "Success", IGNORED: "Skipped" } + +INDENT = 2 + +log_dir_name = "logs" + +def make_dir(path): + print(" > Creating directory: " + path) + try: + os.makedirs(path) + except: + print("[Error] could not create directory " + path) + + +def is_batch_dir_sane(batch_name): + for filename in os.listdir(batch_name): + name, extension = os.path.splitext(filename) + + fullname = os.path.join(batch_name, filename) + if ((os.path.isfile(fullname) and extension != ".cfg") or \ + (os.path.isdir(fullname) and filename != log_dir_name)): + return False + return True + +def is_valid_binary(binary_name): + bin_path = os.path.join(bin_dir, binary_name) + return os.path.isfile(bin_path) + +# Return (list of filtered names, number of files that are missing) +def get_binaries_from_tags(desired): + binaries = [] + missing = 0 + + # We want to get cfg for everything + get_all = ALL_TAG in desired + + bin2tag = util.get_bin2tags(tags_dir) + + for binary, tags in bin2tag.items(): + if get_all or any(x in desired for x in tags): + if not is_valid_binary(binary): + print(colors.bg_yellow( + " > Skipping " + binary+ " : file missing")) + missing = missing + 1 + else: + print(" > Selecting " + binary) + binaries.append(binary) + + return (binaries, missing) + +def create_batch_dir(batch, policy): + batch_name = batch + "_cfg" + if batch_name not in os.listdir(): + print(" > Batch name is unique") + make_dir(batch_name) + make_dir(os.path.join(batch_name, log_dir_name)) + return batch_name + + print(" > Batch with same name already exists") + print(" > Selected policy is " + policy) + + if policy == "D": + print(" > Removing old batch") + if is_batch_dir_sane(batch_name): + shutil.rmtree(batch_name) + make_dir(batch_name) + make_dir(os.path.join(batch_name, log_dir_name)) + else: + print(" > Batch folder is not sane, remove manually") + sys.exit(1) + return batch_name + +# From lift_program.py +def binary_libraries(binary): + try: + res = subprocess.check_output(['ldd', binary]).decode() + except: + print(" \t[W] ldd failed for " + binary) + return + + for line in res.split("\n"): + if "=>" not in line: + continue + name, path_and_addr = line.split(" => ") + path_and_addr = path_and_addr.strip(" ") + if not path_and_addr.startswith("/"): + continue + + lib = " ".join(path_and_addr.split(" ")[:-1]) + lib = os.path.realpath(lib) + + if os.path.isfile(lib): + yield name.strip(), lib + + +def update_shared_libraries(binary): + if not os.path.isdir(so_dir): + print(" > Creating " + so_dir) + os.mkdir(so_dir) + + for name, path in binary_libraries(binary): + if name in os.listdir(so_dir): + continue + + sym_name = os.path.join(so_dir, name) + + try: + print(" \t> " + sym_name + " => " + path) + os.symlink(path, sym_name) + except: + pass + +# From lift_program.py +# Most likely there will be only x86-64 binaries for the time being, +# but it won't hurt to have it in place once we decide to add another tests +def binary_info(binary): + res = subprocess.check_output(['file', binary]).decode() + is_pie = 'LSB shared object' in res or 'Mach-O 64' in res or 'LSB pie executable' in res + address_size = 64 + + if 'aarch64' in res: + arch = 'aarch64' + elif 'x86-64' in res or 'x86_64' in res: + arch = 'amd64_avx' + elif 'x86' in res: + arch = 'x86_avx' + address_size = 32 + else: + raise Exception("Unknown architecture for file type {}".format(res)) + + return address_size, arch, is_pie + + +def dyninst_frontend(binary, cfg, args, log_file): + address_size, arch, is_pie = binary_info(binary) + + disass_args = [ + "mcsema-dyninst-disass", + "--arch", arch, + # TODO: Portability + "--os", "linux", + "--binary", quote(binary), + "--output", quote(cfg), + "--entrypoint", "main", + "--std_defs", args.std_defs ] + + if is_pie: + disass_args.append("--pie_mode") + # TODO: May not be needed + disass_args.append("true") + + # TODO: This is too verbose for normal output + #print(" \t> " + " ".join(disass_args)) + + ret = subprocess.call(disass_args) + if ret: + return FAIL + return SUCCESS + +# TODO: Testing REQUIRED +def ida_frontend(binary, cfg, args, log_file): + address_size, arch, is_pie = binary_info(binary) + + disass_args = [ + 'mcsema-disass', + '--arch', arch, + '--os', 'linux', + '--binary', quote(binary), + '--output', quote(cfg), + '--entrypoint', 'main', + '--log_file', log_file, + '--disassembler', args.path_to_disass] + + if is_pie: + disass_args.append("--pie_mode") + + print(" \t> " + " ".join(disass_args)) + ret = subprocess.call(disass_args) + if ret: + return FAIL + return SUCCESS + +def binja_frontend(binary, cfg, args, log_file): + print(" > Not implemented") + sys.exit(1) + + +# TODO: We may want for each file to be lifted in separate directory and on a copy +# (in the case frontend is broken and modifies the original itself) +def get_cfg(*t_args, **kwargs): + todo, args, lifter, result = t_args + + while not todo.empty(): + + try: + binary, cfg = todo.get() + except queue.Empty: + return + + bin_path = os.path.join(bin_dir, binary) + + print("\n > Processing " + bin_path) + update_shared_libraries(bin_path) + + log_file = os.path.join(args.batch + "_cfg", log_dir_name, binary + ".log") + result[binary] = lifter(bin_path, cfg, args, log_file) + +# TODO: Handle other frontends +def get_lifter(disass): + if disass == "dyninst": + return dyninst_frontend + if disass == "ida": + return ida_frontend + print(" > Support for chosen frontend was not implemented yet!") + sys.exit(1) + +def print_result(result, missing): + print("\nResults:") + stat = dict() + for key, val in sorted(result.items(), key=operator.itemgetter(0)): + print(key.ljust(30).rjust(30 + INDENT) + colors.get_bin_result(val) + + (MESSAGES[val]).rjust(5) + colors.clean()) + if val in stat: + stat[val] += 1 + else: + stat[val] = 1 + print("\nTotal:") + for key, val in MESSAGES.items(): + print(val) + if key not in stat: + print(" " * INDENT, str(0)) + else: + print(" " * INDENT, stat[key]) + print("\nMissing:") + print(" " * INDENT, missing) + +def main(): + arg_parser = argparse.ArgumentParser() + + arg_parser.add_argument( + "--disass", + help='Frontend tobe used: ida | binja | dyninst', + choices=["ida", "binja", "dyninst"], + required=True) + + arg_parser.add_argument( + "--path_to_disass", + help="Path to disassembler, needed in case ida is chosen as frontend", + default=None, + required=False) + + arg_parser.add_argument( + "--tags", + help="Flavors to be lifted", + nargs="+", + required=True) + + arg_parser.add_argument( + "--dry_run", + help="Should actual commands be executed?", + default=False, + required=False) + + arg_parser.add_argument( + "--batch", + help="Specify batch name", + required=True) + arg_parser.add_argument( + "--batch_policy", + choices=["D", "U", "C"], + help="How to resolve already existing batch\n D: delete all old cfgs\nU: Update all\nC: create only missing", + default="D", + required=False) + + arg_parser.add_argument( + "--std_defs", + help="In case frontend still supports/needs it, can be found with McSema sources", + default="../../tools/mcsema_disass/defs/linux.txt", + required=False) + + arg_parser.add_argument( + '--jobs', + help = "Number of threads to use", + default = 1, + required = False) + + args, command_args = arg_parser.parse_known_args() + + + if args.disass == "ida": + if args.path_to_disass is None: + print("IDA frontend is selected but --path_to_disass is not") + sys.exit(1) + if not os.path.isfile(args.path_to_disass): + print("IDA frontend is selected but --path_to_disass is not a file") + sys.exit(1) + + print("Checking batch name") + batch_dir = create_batch_dir(args.batch, args.batch_policy) + + print("Select all binaries, specified by tags") + binaries, missing = get_binaries_from_tags(args.tags) + + result = dict() + print("\nIterating over binaries") + + todo = queue.Queue() + + for b in binaries: + cfg = os.path.join(batch_dir, b + ".cfg") + if args.batch_policy == "C" and os.path.isfile(cfg): + print(" \t> " + cfg + " is already present, not updating") + result[b] = IGNORED + else: + todo.put((b, cfg)) + + threads = [] + for i in range(int(args.jobs)): + t = threading.Thread( + target=get_cfg, args=(todo, args, get_lifter(args.disass), result)) + t.start() + threads.append(t) + + for t in threads: + t.join() + + print_result(result, missing) + + return 0 + +if __name__ == '__main__': + main() diff --git a/tests/integration_tests/inputs/calc_input1.txt b/tests/integration_tests/inputs/calc_input1.txt new file mode 100644 index 000000000..0a0f48274 --- /dev/null +++ b/tests/integration_tests/inputs/calc_input1.txt @@ -0,0 +1,7 @@ ++ 8 5 ++ 98 -8 ++ -6 -6 +- -5 -8 +! 5 +m 58 8 +q diff --git a/tests/integration_tests/inputs/calc_input2.txt b/tests/integration_tests/inputs/calc_input2.txt new file mode 100644 index 000000000..d5db76758 --- /dev/null +++ b/tests/integration_tests/inputs/calc_input2.txt @@ -0,0 +1,17 @@ +* 9 8 +- 9 8 +* 9 -8 +p 75 +p 9 +p 7 +a 5 +1 +2 +3 +4 +5 +s 3 +1 +-1 +0 +q diff --git a/tests/integration_tests/inputs/calc_input3.txt b/tests/integration_tests/inputs/calc_input3.txt new file mode 100644 index 000000000..2b4386bf2 --- /dev/null +++ b/tests/integration_tests/inputs/calc_input3.txt @@ -0,0 +1,6 @@ +c 8 7 ++ 8 9 +! 5 +d 5 -6 +/ 5 4 +q diff --git a/tests/integration_tests/inputs/calc_input4.txt b/tests/integration_tests/inputs/calc_input4.txt new file mode 100644 index 000000000..01ee4cc9b --- /dev/null +++ b/tests/integration_tests/inputs/calc_input4.txt @@ -0,0 +1,10 @@ +p 78 +p 5 +p 35 +p 57 +p 89 +p 51 +p 4 +p 89 +p 15896 +q diff --git a/tests/integration_tests/inputs/data.txt b/tests/integration_tests/inputs/data.txt new file mode 100644 index 000000000..e9950fe8f --- /dev/null +++ b/tests/integration_tests/inputs/data.txt @@ -0,0 +1,5 @@ +What a beauty! +int main() { + return 0; +} +Cras cras, semper cras sic evadit eatas. diff --git a/tests/integration_tests/inputs/dec_data.txt.gz b/tests/integration_tests/inputs/dec_data.txt.gz new file mode 100644 index 000000000..844941c7c Binary files /dev/null and b/tests/integration_tests/inputs/dec_data.txt.gz differ diff --git a/tests/integration_tests/populate.py b/tests/integration_tests/populate.py new file mode 100755 index 000000000..a93d9016c --- /dev/null +++ b/tests/integration_tests/populate.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019 Trail of Bits, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import shutil +import stat + +import colors +import util + +tags_dir = "tags" +bin_dir = "bin" + +def try_find(locations, basename): + for p in locations: + maybe = os.path.join(p, basename) + if os.path.isfile(maybe): + print(" > " + colors.green("Found " + maybe)) + new_file = os.path.join(bin_dir, basename) + shutil.copyfile(maybe, new_file) + st = os.stat(new_file) + os.chmod(new_file, st.st_mode | stat.S_IEXEC) + + return True + return False + +def main(): + # TODO: Make it portable + locations = [ "/usr/bin", "/bin"] + + current = set() + # If `bin` does not exist create it first + if not os.path.isdir(bin_dir): + os.mkdir(bin_dir) + + for f in os.listdir(bin_dir): + current.add(f) + + for f in os.listdir(tags_dir): + basename = util.strip_whole_config(f) + if not basename: + continue + + if basename in current: + print(" > " + basename + " is present in " + tags_dir) + continue + + if not try_find(locations, basename): + print(" > " + colors.red(basename + " not found anywhere")) + +if __name__ == '__main__': + main() diff --git a/tests/integration_tests/result_data.py b/tests/integration_tests/result_data.py new file mode 100644 index 000000000..5e0e5fc6e --- /dev/null +++ b/tests/integration_tests/result_data.py @@ -0,0 +1,182 @@ +import json +import operator +import os + +import colors + +UNKNOWN = 0 +RUN = 1 +FAIL = 2 +ERROR = 3 +TIMEOUT = 4 + +_color_mapping = { + RUN : colors.green, + FAIL: colors.orange, + ERROR : colors.red, + TIMEOUT: colors.magneta, + UNKNOWN : colors.id, + } + +class TCData: + def __init__(self, basename = None, bin_p = None, recompiled_p = None): + self.binary = bin_p + self.recompiled = recompiled_p + self.basename = basename + self.total = 0 + self.success = 0 + self.cases = {} + self.ces = {} + + def is_recompiled(self): + return self.recompiled is not None + + def get_result_color(self): + if self.total == 0: + return colors.magneta + + if self.total == self.success: + return colors.green + + if self.success == 0: + return colors.red + + return colors.orange + + def print(self, verbosity): + end = "\n" + if verbosity == 0: + end = " " + + print("{:<30s}".format(self.get_result_color()(self.basename)), end=end) + if not self.is_recompiled(): + print("\tRecompilation failed: ERROR") + return + + if self.total == 0: + print(colors.magneta("\tNo tests were executed")) + return + + if verbosity == 0: + print(colors.get_result_color(self.total, self.success) + + "{:>5s}".format(str(self.success) + "/" + str(self.total)) + + colors.clean()) + elif verbosity == 1: + for case, val in sorted(self.cases.items(), key = operator.itemgetter(0)): + print(" " * 2, _color_mapping[val](case)) + + def print_ces(self): + for case, ce in self.ces.items(): + print(colors.red(self.basename) + ': '+ ('without_args' if not case else case)) + print(ce) + + def get(self, test_case): + return self.cases.get(test_case, UNKNOWN) + + def outer_get(self, name, test_case): + if self and name == self.basename: + return self.get(test_case) + return UNKNOWN + +class _MyEncoder(json.JSONEncoder): + def default(self, o): + return o.__dict__ + +def _object_hook(d): + if "binary" in d: + obj = TCData() + obj.__dict__.update(d) + return obj + return d + +# SQL serialization may be useful as well +def store_json(root, filename): + if os.path.isfile(filename): + print("Log file already exists") + return + with open(filename, 'w') as f: + json.dump(root, f, cls=_MyEncoder, indent=4) + +def load_json(filename): + with open(filename, 'r') as f: + return json.load(f, object_hook = _object_hook) + + +class _Format: + l_header = 25 + + def header(self, message): + self._header = message + self.h_queued = True + + def case(self, message): + self._case = message + + def _header_dump(self): + if not self.h_queued: + return + self.h_queued = False + + printed = self._header.ljust(_Format.l_header) + self.h_fill = len(printed) + + print(printed, end="") + self.present_header = True + + def _case_dump(self): + fill = 0 if self.present_header else self.h_fill + printed = " " * fill + "|" + self._case.ljust(_Format.l_header) + "|" + + print(printed, end="") + self.present_header = False + + def _res_dump(self, result): + print(" " + str(result) + " |", end="") + + # TODO: Print something else than numbers with verbosity = 2 + def dump(self, results, verbosity, original): + + # Print everything and really verbose + if verbosity == 2: + self._header_dump() + self._case_dump() + self._res_dump(original) + for r in results: + print(" ", _color_mapping[r](str(r)), " |", end="") + print() + + if verbosity == 0: + if all(r == original for r in results): + return + + self._header_dump() + self._case_dump() + self._res_dump(original) + for r in results: + if r == original: + print(" " * 3 + "|", end="") + else: + print(" ", _color_mapping[r](str(r)), " |", end="") + print() + +# Compare test results to some base and print them in reasonable way +# f is formatter -> first suite name and then case are preset and when dump() is called +# actual results are printed based on verbosity level +def compare(base, results, formatter, full): + # first we need to sort the items + s_base = sorted(base.items(), key=operator.itemgetter(0)) + + for entry in s_base: + + suite_name, tcdata = entry + formatter.header(suite_name) + + for case_name, case_result in tcdata.cases.items(): + + formatter.case(case_name) + r_case_results = [] + + for r in results: + r_tcdata = r.get(suite_name, None) + r_case_results.append(r_tcdata.outer_get(tcdata.basename, case_name)) + formatter.dump(r_case_results, full, case_result) diff --git a/tests/integration_tests/run_tests.py b/tests/integration_tests/run_tests.py new file mode 100755 index 000000000..f9d2e4b25 --- /dev/null +++ b/tests/integration_tests/run_tests.py @@ -0,0 +1,632 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019 Trail of Bits, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import difflib +import filecmp +import operator +import os +import queue +import threading +import shutil +import subprocess +from subprocess import CalledProcessError +import sys +import tempfile +import unittest + +import colors +import result_data +import util + +llvm_version = 8 + +lift = None +bin_dir = "bin" +libmcsema = None +recompiled_dir = "recompiled" +tags_dir = 'tags' + +batches = [] +shared_libs = None +abi_lib_dir = None + +def get_recompiled_name(name): + return name + +# Print results of tests +def print_results(t_cases): + for key, val in sorted(t_cases.items(), key = operator.itemgetter(0)): + val.print(0) + for key, val in sorted(t_cases.items(), key = operator.itemgetter(0)): + val.print_ces() + +def log_results(result, into): + for entry in result.failures: + what = entry[0] + into[what.name].cases[what._testMethodName] = result_data.FAIL + + for entry in result.errors: + what = entry[0] + # Do not overwrite timeouts + if into[what.name].cases[what._testMethodName] != result_data.TIMEOUT: + into[what.name].cases[what._testMethodName] = result_data.ERROR + + +# Fill global variables +# TODO: Rework, this is really ugly +def check_arguments(args): + if not os.path.isfile(args.lift): + print("{} passed to --lift is not a valid file".format(args.lift)) + sys.exit (1) + global lift + lift = args.lift + + if not os.path.isfile(args.runtime_lib): + print ("{} passed to --runtime_lib is not a valid file".format(args.runtime_lib)) + sys.exit (1) + global libmcsema + libmcsema = args.runtime_lib + + if not os.path.isdir(args.bin_dir): + print("{} passed to --bin_dir is not a valid directory".format(args.bin_dir)) + sys.exit (1) + bin_dir = os.path.abspath(args.bin_dir) + + if not os.path.isdir(args.shared_lib_dir): + print("{} passed to --shared_lib_dir is not a valid directory".format(args.shared_lib_dir)) + sys.exit (1) + + global shared_libs + shared_libs = [] + for lib_name in os.listdir(args.shared_lib_dir): + shared_libs.append(os.path.join(args.shared_lib_dir, lib_name )) + + print("\n > Shared libraries: ") + print(shared_libs) + + if not os.path.isdir(args.abi_lib_dir): + print ("{} passed to --abi_lib_dir is not a valid directory".format(args.abi_lib_dir)) + sys.exit (1) + + global abi_lib_dir + abi_lib_dir = '' + for lib_name in os.listdir(args.abi_lib_dir): + if lib_name.endswith(".bc"): + abi_lib_dir += os.path.join(args.abi_lib_dir, lib_name + ',') + + print("\n > --abi_libraries files:") + print(abi_lib_dir) + + # TODO: This whole snippet can be done using argparser and some actions + if args.batch is not None: + for batch in args.batch: + batch_dir = batch + "_cfg" + if not os.path.isdir(batch_dir): + print("{} passed to --batch is not a valid directory".format(batch)) + sys.exit(1) + batches.append(batch_dir) + + if args.batch_dir is not None: + for batch in args.batch_dir: + if not os.path.isdir(batch): + print("{} passed to --batch_dir is not a valid directory".format(batch)) + sys.exit(1) + batches.append(batch_dir) + + if not batches: + print("No batch is selected, exiting") + sys.exit(0) + + print(batches) + + +def exec_and_log_fail(args): + pipes = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.PIPE) + std_out, std_err = pipes.communicate() + ret_code = pipes.returncode + + if ret_code: + print("** stdout:") + print(std_out) + print("** stderr:") + print(std_err) + return False + return True + +class Config: + class Result: + SUCCESS = 0 + LIFT_FAIL = 1 + RECOMPILE_FAIL = 2 + + defaults = [ '-os', 'linux', '-arch', 'amd64' ] + togglable = { + 'abi_libraries' : ['-abi_libraries', abi_lib_dir], + } + + # Since strings are immutable, hotfix is needed once abi_lib_dir is set + def _fix_excludes(self): + flag, opt = self.togglable['abi_libraries'] + self.togglable['abi_libraries'] = (flag, abi_lib_dir) + + def __init__(self, name, src, cfg_path): + self.name = name + self.binary = os.path.join(bin_dir, name) + self.config = src.rsplit('.', 2)[1] + self.id = self.name + '.' + self.config + self.cfg = cfg_path + self.lift_args = [] + self.exclude_args = [] + self.tags = [] + self._fix_excludes() + self._parse_config(src) + + def _tags(self, line): + self.tags = line.split(' ')[1:] + + def _lift_opts(self, line): + tokens = line.split(' ')[1:] + # LIFT_OPTS are empty + if not tokens: + return + + exclude = [] + for opt in tokens: + if not opt: + break + elif opt[0] == '+': + self.lift_args.append(opt[1:]) + elif opt[0] != '!': + self.lift_args.append(opt) + else: + exclude.append(opt[1:]) + + for key, val in Config.togglable.items(): + if key in exclude: + continue + self.lift_args += val + + + def _parse_config(self, src): + print(' > Parsing', src) + with open(os.path.join(tags_dir, src), 'r') as cfg: + for line in cfg: + line = line.rstrip('\n') + header = line.split(' ', 1)[0] + + header_dispatch = { + 'TAGS:' : Config._tags, + 'LIFT_OPTS:' : Config._lift_opts, + } + + if header not in header_dispatch: + raise Exception("Unknown header {}".format(header)) + header_dispatch[header](self, line) + + def lift(self, test_dir): + print(" > Lifting", self.name + self.config) + + self.bc = os.path.join(test_dir, '.'.join([self.name, self.config, 'bc'])) + self.recompiled = os.path.join(test_dir, self.name + '.' + self.config) + + args = [lift] + self.defaults + self.lift_args + \ + ['-output', self.bc, '-cfg', self.cfg] + print(args) + if not exec_and_log_fail(args): + return Config.Result.LIFT_FAIL + + return self.recompile() + + def recompile(self): + compiler = None + if 'c' in self.tags: + compiler = "clang-{}" + elif 'cpp' in self.tags: + compiler = "clang++-{}" + else: + print(" > Cannot decide on compiler when recompiling",\ + self.name + '.' + self.config) + return Config.Result.RECOMPILE_FAIL + + compiler = compiler.format(llvm_version) + args = [compiler, self.bc, '-o', self.recompiled, \ + libmcsema, '-lpthread', '-lm', '-ldl'] + shared_libs + + if not exec_and_log_fail(args): + return Config.Result.RECOMPILE_FAIL + return Config.Result.SUCCESS + + +def parallel_lift(*t_args): + todo, test_dir, results = t_args + while not todo.empty(): + try: + config = todo.get() + except queue.Empty: + return + + res = config.lift(test_dir) + # TODO: More fine grained log + if res != Config.Result.SUCCESS: + results[self.id] = result_data.TCData(self.id, self.recompiled, self.binary) + +def get_configs(directory, allowed_tags, batched): + result = [] + for f in os.listdir(directory): + name = util.strip_whole_config(f) + if not name or name not in batched: + continue + c = Config(name, f, batched[name]) + + if allowed_tags is None: + result.append(c) + elif any(x in allowed_tags for x in c.tags): + result.append(c) + return result + +class TestDetails: + def __init__(self, cmd): + self.cmd = cmd + self.files = None + self.stdin = None + self.f_stdin = None + self.check = [] + + def set_files(self, files): + self.files = files + return self + + def set_check(self, check): + self.check = check + return self + + def set_f_stdin(self, f_stdin): + self.f_stdin = f_stdin + return self + + def set_stdin(self, stdin): + self.stdin = stdin + return self + + def files(self, files): + if self.files is not None: + raise Exception("Incorrect format of test case") + self.files = files + + def stdin(self, stdin): + if stdin[0] != 'F': + self.stdin = stdin.encode() + return + self.f_stdin = os.path.abspath(stdin[1:]) + +class TestCase: + def __init__(self, src=None): + self.details = [] + if src is not None: + self._parse(src) + + def _parse(self, src): + dispatch = { + 'STDIN:' : TestDetails.stdin, + 'FILES:' : TestDetails.files, + } + with open(src, 'r') as f: + for line in f: + line = line.rstrip('\n') + head = line.split(' ', 1)[0] + if head in dispatch: + dispatch[head](self.details[-1], line.split(' ', 1)[1]) + else: + self.details.append(TestDetails(line.split(' '))) + # .test was present but empty + if not self.details: + self.details.append(TestDetails([''])) + + +class Runner: + def __init__(self, config, test_case): + self.config = config + self.test_case = test_case + + def set_up(self): + self.t_bin = tempfile.mkdtemp(dir=os.getcwd(), prefix='bin_t') + self.t_recompiled = tempfile.mkdtemp(dir=os.getcwd(), prefix='recompiled_t') + self.sawed_cwd = os.getcwd() + + cfg = self.config + os.symlink(os.path.abspath(cfg.binary), os.path.join(self.t_bin, cfg.name)) + os.symlink(cfg.recompiled, os.path.join(self.t_recompiled, cfg.name)) + + def tear_down(self): + os.chdir(self.sawed_cwd) + shutil.rmtree(self.t_recompiled) + shutil.rmtree(self.t_bin) + + def copy_files(self, detail): + if detail.files is not None: + for f in detail.files: + basename = os.path.basename(f) + full_name = f + r = os.path.join(self.t_recompiled, basename) + b = os.path.join(self.t_bin, basename) + shutil.copyfile(full_name, r) + shutil.copyfile(full_name, b) + + def compare(self, expected, actual, files): + e_out, e_err, e_ret = expected + a_out, a_err, a_ret = actual + + correct = True + counterexample = '' + # TODO: This cannot be more hacky than this + try: + if a_out != e_out: + counterexample += 'stdout:\n' + counterexample += '\tExpected:\n' + counterexample += e_out.decode() + '\n' + counterexample += '\tGot:\n' + counterexample += a_out.decode() + '\n' + correct = correct and False + if a_err != e_err: + counterexample += 'stderr:'+ '\n' + counterexample += '\tExpected:'+ '\n' + counterexample += e_err.decode()+ '\n' + counterexample += '\tGot:'+ '\n' + counterexample += a_err.decode()+ '\n' + correct = correct and False + except UnicodeDecodeError as e: + correct = False + counterexample += str(e) + '\n' + + if e_ret != e_ret: + counterexample += "Return code: " + str(e_ret) + ' != ' + str(a_ret) + '\n' + correct = correct and False + + for name in files: + base_name = os.path.basename(name) + actual = os.path.join(self.t_recompiled, base_name) + expected = os.path.join(self.t_bin, base_name) + if not filecmp.cmp(expected, actual): + counterexample += 'Files do not match: ' + basename + '\n' + correct = correct and False + + + result = result_data.RUN if correct else result_data.FAIL + return (result, counterexample) + + def open_stdin(self, t_dir, args, stdin): + with open(stdin, 'rb') as f: + r = f.read() + return self.exec_(t_dir, args, r) + + def exec_(self, t_dir, args, stdin): + os.chdir(t_dir) + try: + pipes = subprocess.Popen( + args, stdout=subprocess.PIPE, + stderr = subprocess.PIPE, + stdin = subprocess.PIPE) + out, err = pipes.communicate(stdin, timeout=5) + ret = pipes.returncode + + os.chdir(self.sawed_cwd) + return (out, err, ret) + except subprocess.TimeoutExpired as e: + pipes.terminate() + raise e + + + def exec(self, detail, files): + # To avoid calling system-wide installed binaries + filename = './' + self.config.name + + _exec = Runner.exec_ if detail.f_stdin is None else Runner.open_stdin + stdin = detail.f_stdin if detail.f_stdin is not None else detail.stdin + + try: + print(detail.cmd) + expected = _exec(self, self.t_bin, [filename] + detail.cmd, stdin) + actual = _exec(self, self.t_recompiled, [filename] + detail.cmd, stdin) + return self.compare(expected, actual, files) + except subprocess.TimeoutExpired as e: + return result_data.TIMEOUT + + + def run(self, detail): + self.set_up() + self.copy_files(detail) + result, ce = self.exec(detail, detail.check) + self.tear_down() + return (result, ce) + + + def evaluate(self, results): + c = self.config + results[c.id] = result_data.TCData(c.id, c.recompiled, c.binary) + for tc in self.test_case: + for d in tc.details: + result, ce = self.run(d) + results[c.id].cases[' '.join(d.cmd)] = result + results[c.id].total += 1 + if result == result_data.RUN: + results[c.id].success += 1 + else: + results[c.id].ces[' '.join(d.cmd)] = ce + +class Tester: + + def __init__(self, configs, test_def_dir): + # Config -> TestCase + self.cases = {} + + for c in configs: + self.cases[c] = [] + + for f in os.listdir(test_def_dir): + basename, ext = os.path.splitext(f) + if ext != '.test': + continue + + tc = TestCase(os.path.join(test_def_dir, f)) + for key, val in self.cases.items(): + if key.name == basename: + val.append(tc) + + for key, val in g_complex_test.items(): + for k, v in self.cases.items(): + if k.name == key: + tc = TestCase() + tc.details = val + v.append(tc) + + def run(self, results): + for key, val in self.cases.items(): + print(key.id, 'number of tests:', len(val)) + r = Runner(key, val) + r.evaluate(results) + + +g_complex_test = { + "gzip" : + [ + TestDetails(['--help']), + TestDetails(['--version']), + TestDetails(['-asda']), + TestDetails(['-f', './data.txt']).set_files(['inputs/data.txt']). \ + set_check(['data.txt.gz']), + TestDetails(['-l', './dec_data.txt.gz']).set_files(['inputs/dec_data.txt.gz']), + TestDetails(['-df', './dec_data.txt.gz']).set_files(['inputs/dec_data.txt.gz']).\ + set_check(['dec_data.txt']), + ], + + "cat": + [ + TestDetails(['--help']), + TestDetails(['--version']), + TestDetails(['data.txt']).set_files(['inputs/data.txt']), + TestDetails(['-n', 'data.txt']).set_files(['inputs/data.txt']), + ], + + "echo": + [ + TestDetails(['--help']), + TestDetails(['--version']), + TestDetails(['Hello Wordl!']), + ], + + +} + + + +# Right now batches are combined, maybe it would make sense to separate batches from each other +# that can be useful when comparing performance of frontends +def main(): + arg_parser = argparse.ArgumentParser( + formatter_class = argparse.RawDescriptionHelpFormatter) + + arg_parser.add_argument('--lift', + help = "Path to the mcsema-lift binary", + required = True) + + arg_parser.add_argument('--runtime_lib', + help = "Runtime library for lifted bitcode \ + (e.g. libmcsema-rt64-6.0.a", + required = True) + + arg_parser.add_argument('--shared_lib_dir', + help = "Directory that contains shared libraries used by original binaries", + default = "shared_libs", + required = False) + + + arg_parser.add_argument('--bin_dir', + help = "Directory that contains original binaries", + default = "bin", + required = False) + + + arg_parser.add_argument('--batch', + help = "Batch names", + nargs = '+', + required = False) + + arg_parser.add_argument('--batch_dir', + help = 'Directories with cfgs', + nargs = '+', + required = False) + + arg_parser.add_argument('--abi_lib_dir', + help = "Directory that contains bitcode types of external functions", + required = True) + + arg_parser.add_argument('--dry_run', + help = 'Do not run any tests', + required = False) + + arg_parser.add_argument('--save_log', + help = "Name of file to save result in json format", + required = False) + + arg_parser.add_argument('--jobs', + help = "Number of threads to use", + default = 1, + required = False) + + arg_parser.add_argument('--tags', + help = "Test only these tags from batch", + required = False) + + args, command_args = arg_parser.parse_known_args() + check_arguments(args) + + + # Create directory to store recompiled binaries + # TemporaryDirectory() is not used, since we may want to have a look at recompiled + # code, maybe we want some --preserve option + test_dir = tempfile.mkdtemp(dir=os.getcwd()) + + # basename -> path + batched = {} + for batch in batches: + for f in os.listdir(batch): + if os.path.isfile(os.path.join(batch, f)): + basename, ext = os.path.splitext(f) + batched[basename] = os.path.join(batch, f) + + + configs = get_configs(tags_dir, args.tags, batched) + _todo = queue.Queue() + for c in configs: + _todo.put(c) + + threads = [] + results = {} + for i in range(int(args.jobs)): + t = threading.Thread(target=parallel_lift, args=(_todo, test_dir, results)) + t.start() + threads.append(t) + for t in threads: + t.join() + + Tester(configs, tags_dir).run(results) + print_results(results) + + return + +if __name__ == '__main__': + main() diff --git a/tests/integration_tests/src/all_data_array.c b/tests/integration_tests/src/all_data_array.c new file mode 100644 index 000000000..1cb655112 --- /dev/null +++ b/tests/integration_tests/src/all_data_array.c @@ -0,0 +1,44 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include + +int main(int argc, char **args) +{ + uint8_t fold = 0xAF; + void (*obf_funcs[]) (void) = { + (void (*) (void))main, + (void (*) (void))main, + (void (*) (void))main, + (void (*) (void))main, + (void (*) (void))main, + (void (*) (void))main, + (void (*) (void))main, + (void (*) (void))main, + (void (*) (void))main, + }; + + printf("hrm: %zu\n", sizeof (obf_funcs)); + printf("hrm: %zu\n", sizeof (void *)); + printf("div: %zu\n", (sizeof (obf_funcs) / sizeof (void *))); + fold %= (sizeof (obf_funcs) / sizeof (void *)); + printf("so answer: %d\n", fold); + return 0; +} diff --git a/tests/integration_tests/src/all_globals.c b/tests/integration_tests/src/all_globals.c new file mode 100644 index 000000000..af39c1ebb --- /dev/null +++ b/tests/integration_tests/src/all_globals.c @@ -0,0 +1,37 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +static char someglobal = 1; + +int writeit() +{ + write(2,&someglobal,1); + someglobal++; + write(2,&someglobal,1); + return 0; +} + +int main(void) +{ + someglobal = 0x68; + writeit(); + + return 0; +} diff --git a/tests/integration_tests/src/all_stringpool.c b/tests/integration_tests/src/all_stringpool.c new file mode 100644 index 000000000..23b99b78c --- /dev/null +++ b/tests/integration_tests/src/all_stringpool.c @@ -0,0 +1,23 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main(int argc, const char *argv[]) { + printf("%s: %s\n", "partial string test", "string test"); + return 0; +} diff --git a/tests/integration_tests/src/all_switch.c b/tests/integration_tests/src/all_switch.c new file mode 100644 index 000000000..9f93fbc53 --- /dev/null +++ b/tests/integration_tests/src/all_switch.c @@ -0,0 +1,93 @@ +/* TAGS: min c */ +/* TEST: 12 */ +/* TEST: 15 */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +int main(int argc, const char *argv[]) { + + if(argc < 2) { + return -1; + } + + int input = atoi(argv[1]); + + switch(input) { + case 0: + printf("Input was zero\n"); + break; + case 1: + printf("Input was one\n"); + break; + case 2: + printf("Input was two\n"); + break; + case 4: + printf("Input was four\n"); + break; + case 6: + printf("Input was six\n"); + break; + case 12: + printf("Input was twelve\n"); + break; + case 13: + printf("Input was thirteen\n"); + break; + case 19: + printf("Input was nineteen\n"); + break; + case 255: + printf("Input was two hundred fifty-five\n"); + break; + case 0x12389: + printf("Really big input: 0x12389\n"); + break; + case 0x1238A: + printf("Really big input: 0x1238A\n"); + break; + case 0x1238B: + printf("Really big input: 0x1238B\n"); + break; + case 0x1238C: + printf("Really big input: 0x1238C\n"); + break; + case 0x1238D: + printf("Really big input: 0x1238D\n"); + break; + case 0x1238F: + printf("Really big input: 0x1238F\n"); + break; + case 0x12390: + printf("Really big input: 0x12390\n"); + break; + case 0x12391: + printf("Really big input: 0x12391\n"); + break; + case 0x12392: + printf("Really big input: 0x12392\n"); + break; + case 0x12393: + printf("Really big input: 0x12393\n"); + break; + default: + printf("Unknown input: %d\n", input); + } + return 0; +} diff --git a/tests/integration_tests/src/array.c b/tests/integration_tests/src/array.c new file mode 100644 index 000000000..7728521fc --- /dev/null +++ b/tests/integration_tests/src/array.c @@ -0,0 +1,34 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main (void) +{ + int array [10]; + int sum = 0; + + for (int i = 0; i < 10; ++i) + array [i] = 2 * i; + + for (int i = 0; i < 10; ++i) + sum += array [i]; + + printf ("Result: %d\n", sum); + + return 0; +} diff --git a/tests/integration_tests/src/bubblesort.c b/tests/integration_tests/src/bubblesort.c new file mode 100644 index 000000000..2c068bdff --- /dev/null +++ b/tests/integration_tests/src/bubblesort.c @@ -0,0 +1,73 @@ +/* TAGS: min c */ +/* TEST: 12 */ +/* TEST: 14 */ +/* TEST: 0 */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +void bubbleSort (int a [], int array_size) +{ + int i, j, temp; + + for (i = 0; i < array_size - 1; ++i) + { + for (j = 0; j < array_size - 1 - i; ++j) + { + if (a [j] > a [j+1]) + { + temp = a [j+1]; + a [j+1] = a [j]; + a [j] = temp; + } + } + } +} + +int main (int argc, char **argv) +{ + if (argc < 2) + return 1; + + int size = atoi (argv [1]); + int i = 0; + int array [size]; + + for (int i = 0; i < size; ++i) + array [i] = size - i; + + printf ("Before sorting the list is: \n"); + + for(i = 0; i < size; ++i) + { + printf ("%d ", array [i]); + } + + bubbleSort (array, size); + + printf ("\nAfter sorting the list is: \n"); + + for(i = 0; i < size; ++i) + { + printf ("%d ", array [i]); + } + + printf("\n"); + + return 0; +} diff --git a/tests/integration_tests/src/calc.c b/tests/integration_tests/src/calc.c new file mode 100644 index 000000000..7ae97e4a9 --- /dev/null +++ b/tests/integration_tests/src/calc.c @@ -0,0 +1,232 @@ +/* TAGS: min c */ +/* TEST: */ +/* STDIN: Finputs/calc_input1.txt */ +/* TEST: */ +/* STDIN: Finputs/calc_input2.txt */ +/* TEST: */ +/* STDIN: Finputs/calc_input3.txt */ +/* TEST: */ +/* STDIN: Finputs/calc_input4.txt */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int sum(); +int difference(); +float divideFloat(); +int divideInt(); +int modulo(); +int times(); +long power(); +long long int factorial(); +int sums(); +float average(); +long long int binomialCoeficient(); +int is_prime(); +long long int factorialWithArgument(int a); +void cleanInput(); + +int main(void) { + char c; + do { + printf("> "); + c = getchar(); + switch (c) { + case '+': + printf("# sum\n"); + printf("# %i\n", sum()); + break; + case '-': + printf("# difference\n"); + printf("# %i\n", difference()); + break; + case '/': + printf("# %.2f\n", divideFloat()); + break; + case 'd': + printf("# %i\n", divideInt()); + break; + case 'm': + printf("# %i\n", modulo()); + break; + case '*': + printf("# %i\n", times()); + break; + case '^': + printf("# %li\n", power()); + break; + case '!': + printf("# %lli\n", factorial()); + break; + case 's': + printf("# %i\n", sums()); + break; + case 'a': + printf("# %.2f\n", average()); + break; + case 'c': + printf("# %lli\n", binomialCoeficient()); + + break; + case 'p': + if (is_prime()) { + printf("# y\n"); + } else { + printf("# n\n"); + } + break; + default: + break; + } + cleanInput(); + + } while (c != 'q'); + return 0; +} + +void cleanInput() { + while (getchar() != '\n') + ; +} + +int sum() { + int a, b; + scanf(" %i %i", &a, &b); + return (a + b); +} + +int difference() { + int a, b; + scanf(" %i %i", &a, &b); + return (a - b); +} + +float divideFloat() { + float a, b; + scanf(" %f %f", &a, &b); + return (a / b); +} + +int divideInt() { + float a, b; + scanf(" %f %f", &a, &b); + return (a / b); +} + +int modulo() { + int a, b; + scanf(" %i %i", &a, &b); + return (a % b); +} + +int times() { + int a, b; + scanf(" %i %i", &a, &b); + return (a * b); +} + +long int power() { + int base, exp, result = 1; + scanf(" %i %i", &base, &exp); + if (exp == 0) + return 1; + for (int i = 1; i <= exp; i++) { + result *= base; + } + return result; +} + +long long int factorial() { + int number; + long long int result = 1; + scanf(" %i", &number); + if (number < 0) { + return -1; + } else if (number == 0) { + return 1; + } else { + for (int i = 0; i < number; i++) { + result *= (number - i); + } + return result; + } +} + +int sums() { + int member, result = 0, numberOfMembers; + scanf(" %i", &numberOfMembers); + int temp = numberOfMembers; + while (temp > 0) { + scanf("%i", &member); + result += member; + temp--; + } + return result; +} + +float average() { + float result = 0; + int member, numberOfMembers; + scanf(" %i", &numberOfMembers); + int temp = numberOfMembers; + if (numberOfMembers == 0) { + return 0; + } else { + while (temp > 0) { + scanf(" %i", &member); + result += member; + --temp; + } + return (result / numberOfMembers); + } +} + +long long int binomialCoeficient() { + int n, k; + scanf(" %i %i", &n, &k); + if (n < k || k < 0 || n < 0) { + return -1; + } + long long int test = (factorialWithArgument(n) / (factorialWithArgument(n - k) * factorialWithArgument(k))); + return test; +} + +int is_prime() { + int a; + scanf(" %i", &a); + if (a <= 1) + return 0; + for (int i = 2; i < a; i++) { + if (a % i == 0) + return 0; + } + return 1; +} + +long long int factorialWithArgument(int a) { + long long int result = 1; + if (a < 0) { + return -1; + } else if (a == 0) { + return 1; + } else { + for (int i = 0; i < a; i++) { + result *= (a - i); + } + return result; + } +} diff --git a/tests/integration_tests/src/complex_double.c b/tests/integration_tests/src/complex_double.c new file mode 100644 index 000000000..39580367e --- /dev/null +++ b/tests/integration_tests/src/complex_double.c @@ -0,0 +1,39 @@ +/* TAGS: min c */ +/* LD_OPTS: -lm */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +int main(void) +{ + double complex z = 2 + 2*I; + double complex res = cexp( z ); + + double img = cimag(res); + double real = creal(res); + + int i_img = (int) img; + + printf("%i %i\n", (int) real, (int) img); + if ( i_img != 6 ) printf("NOK!\n"); + + printf("%i\n\t%i + %ii\n", 42, (int)real, (int)img); + +} diff --git a/tests/integration_tests/src/complex_float.c b/tests/integration_tests/src/complex_float.c new file mode 100644 index 000000000..b63394016 --- /dev/null +++ b/tests/integration_tests/src/complex_float.c @@ -0,0 +1,41 @@ +/* TAGS: min c */ +/* LD_OPTS: -lm */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +int main(void) +{ + float complex beg = 4.54 + 2.56*I; + float complex z = cexpf(beg); + + float img = cimagf(z); + float real = crealf(z); + + int i_img = (int) img; + int i_real = (int) real; + + printf("%i %i\n", (int) real, (int) i_img); + if ( i_img != 2 ) printf("NOK img!\n"); + if ( i_real != 4 ) printf("NOK real!\n"); + + printf("%i\n\t%i + %i\n", 42, (int)real, (int)img); + +} diff --git a/tests/integration_tests/src/complex_long_double.c b/tests/integration_tests/src/complex_long_double.c new file mode 100644 index 000000000..6266297be --- /dev/null +++ b/tests/integration_tests/src/complex_long_double.c @@ -0,0 +1,42 @@ +/* TAGS: min c */ +/* LD_OPTS: -lm */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +int main(void) +{ + long double complex z = 1.25 + 2.54*I; + long double complex res = cexpl( z ); + + + long double img = cimagl(res); + long double real = creall(res); + + if ( real + 2.877 < 0 + && real + 2.8776 > 0 + && img - 1.975 > 0 + && img - 1.976 < 0 ) + printf( "OK\n" ); + else + printf( "NOK\n" ); + + printf("%i\n\t%i + %i\n", 42, (int)real, (int)img); +} diff --git a/tests/integration_tests/src/complex_numbers.c b/tests/integration_tests/src/complex_numbers.c new file mode 100644 index 000000000..8b035b625 --- /dev/null +++ b/tests/integration_tests/src/complex_numbers.c @@ -0,0 +1,83 @@ +/* TAGS: min c */ +/* LD_OPTS: -lm */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +void FooFloat() { + float complex initial_value = 3.14f + 4.2*I; + float complex exponent = 2.0f - 1.12*I; + float complex result = cpowf(initial_value, exponent); + + float real_part = crealf(result); + float imag_part = cimagf(result); + + int int_real = (int) real_part; + int int_imag = (int) imag_part; + printf("%i %i\n", int_real, int_imag); + if (int_real == 77 && int_imag == 0) puts("Okay"); + else puts("Nok"); + + result = csinf(initial_value); + + real_part = crealf(result); + imag_part = cimagf(result); + + int_real = (int) real_part; + int_imag = (int) imag_part; + printf("%i %i\n", int_real, int_imag); + + if (int_real == 0 && int_imag == -33) puts("Okay"); + else puts("Nok"); +} + +void FooDouble() { + double complex initial_value = 3.14 + 4.2*I; + double complex exponent = 2.0f - 1.12*I; + double complex result = cpow(initial_value, exponent); + + double real_part = creal(result); + double imag_part = cimag(result); + + int int_real = (int) real_part; + int int_imag = (int) imag_part; + printf("%i %i\n", int_real, int_imag); + if (int_real == 77 && int_imag == 0) puts("Okay"); + else puts("Nok"); + + result = csin(initial_value); + + real_part = creal(result); + imag_part = cimag(result); + + int_real = (int) real_part; + int_imag = (int) imag_part; + + printf("%i %i\n", int_real, int_imag); + if (int_real == 0 && int_imag == -33) puts("Okay"); + else puts("Nok"); +} + +int main(void) +{ + FooFloat(); + FooDouble(); + //TODO: Crashing with floating point exception + //FooLongDouble(); +} diff --git a/tests/integration_tests/src/dot_product.c b/tests/integration_tests/src/dot_product.c new file mode 100644 index 000000000..01269a358 --- /dev/null +++ b/tests/integration_tests/src/dot_product.c @@ -0,0 +1,32 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +static const int a [] = { 4, 3, 7, 5, 9, -4 }; +static const int b [] = { 1, -1, 5, -2, 3, 5 }; + +int main (void) +{ + int result = 0; + + for (int i = 0; i < 6; ++i) + result += a[i] * b[i]; + + printf ("%d\n", result); + return 0; +} diff --git a/tests/integration_tests/src/fibonacci.c b/tests/integration_tests/src/fibonacci.c new file mode 100644 index 000000000..872a5a489 --- /dev/null +++ b/tests/integration_tests/src/fibonacci.c @@ -0,0 +1,43 @@ +/* TAGS: min c */ +/* TEST: 12 */ +/* TEST: 26 */ +/* TEST: 2 */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +unsigned int fib (unsigned int i) +{ + if (i == 0) + return 0; + else if ((i == 1) || (i == 2)) + return 1; + else + return fib (i - 1) + fib (i - 2); +} + +int main (int argc, char **argv) +{ + if (argc < 2) + return 1; + + int n = atoi (argv [1]); + printf ("%u\n", fib (n)); + + return 0; +} diff --git a/tests/integration_tests/src/fmodf.cpp b/tests/integration_tests/src/fmodf.cpp new file mode 100644 index 000000000..0f68083dd --- /dev/null +++ b/tests/integration_tests/src/fmodf.cpp @@ -0,0 +1,37 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +void do_calc( float first, float second ) { + float result = fmodf( first, second ); + printf( "%i\n", (int)result ); + if ( fabsf( result - 0.4f ) < FLT_EPSILON ) printf( "Okay.\n" ); + else printf( "Nok.\n" ); + +} + +int main() { + printf( "Begin %i %i %i\n\t***\n", (int)123.456f, 2, 4); + float fix = 5.4f; + for ( float i = 0.1f; i <= 0.5f; i += 0.1f ) { + do_calc( fix, i ); + } + +} diff --git a/tests/integration_tests/src/global-ctors-dtors-return-type.c b/tests/integration_tests/src/global-ctors-dtors-return-type.c new file mode 100644 index 000000000..87ef84064 --- /dev/null +++ b/tests/integration_tests/src/global-ctors-dtors-return-type.c @@ -0,0 +1,34 @@ +/* TAGS: min c */ +#include +#include + +// Compiler should generate bitcast in llvm.global_ctors and llvm.global_dtors. + +int x = 0, y = 0; + +__attribute__((constructor(1), destructor(1))) void assert_zeroes() { + assert( x == 0 ); + assert( y == 0 ); + putchar( 'c' ); +} + + +__attribute__((constructor(2))) int increase_and_return() { + ++x; + return x; +} + + +__attribute__((destructor(2))) int decrease_and_return() { + --x; + return x; +} + + +int main() { + assert( x == 1 ); + int inc_x = increase_and_return(); + assert( x == inc_x ); + int dec_x = decrease_and_return(); + assert( x == dec_x ); +} diff --git a/tests/integration_tests/src/global-ctors-dtors.c b/tests/integration_tests/src/global-ctors-dtors.c new file mode 100644 index 000000000..0a77862d2 --- /dev/null +++ b/tests/integration_tests/src/global-ctors-dtors.c @@ -0,0 +1,32 @@ +/* TAGS: min c */ +#include +#include + +int x = 0, y = 0; + +#define CTOR(I) __attribute__((constructor(I))) void ct ## I() { assert( x == I - 1 ); ++x; } + +__attribute__((constructor(0))) void ct_first() { assert( x == 0 ); } +CTOR( 1 ); +CTOR( 2 ); +CTOR( 3 ); +__attribute__((constructor)) void ct_outOfOrder() { ++y; } +CTOR( 4 ); + +int main() { + assert( x == 4 ); + assert( y == 1 ); +} + +#define DTOR(I) __attribute__((destructor(I))) void dt ## I() { assert( x == I ); --x; } + +DTOR( 4 ); +DTOR( 3 ); +DTOR( 2 ); +DTOR( 1 ); + +__attribute__((destructor(0))) void dt_last() { + assert( x == 0 ); + assert( y == 1 ); + putchar( 'a' ); +} diff --git a/tests/integration_tests/src/global_array.c b/tests/integration_tests/src/global_array.c new file mode 100644 index 000000000..3d14e472e --- /dev/null +++ b/tests/integration_tests/src/global_array.c @@ -0,0 +1,33 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +static int gArr[10] = {1, 2, 3, 3, 2, 1, 1, 2, 3, 42}; + +int main() { + printf("%i\n", gArr[0]); + printf("%i\n", gArr[1]); + printf("%i\n", gArr[2]); + printf("%i\n", gArr[3]); + printf("%i\n", gArr[4]); + printf("%i\n", gArr[5]); + printf("%i\n", gArr[6]); + printf("%i\n", gArr[7]); + printf("%i\n", gArr[8]); + printf("%i\n", gArr[9]); +} diff --git a/tests/integration_tests/src/global_var.cpp b/tests/integration_tests/src/global_var.cpp new file mode 100644 index 000000000..21e49edaa --- /dev/null +++ b/tests/integration_tests/src/global_var.cpp @@ -0,0 +1,42 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2017 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +class A { + public: + char name[256]; + + A(const char *nameIn) { + printf("class A constructor!\n"); + strcpy(name, nameIn); + } + + ~A(void) { + printf("Class A destructor!\n"); + } +}; + +A global("Global"); + +int main(void) { + printf("Variable name %s\n", global.name); + return 0; +} + + diff --git a/tests/integration_tests/src/globals_and_io.c b/tests/integration_tests/src/globals_and_io.c new file mode 100644 index 000000000..13efc9b1f --- /dev/null +++ b/tests/integration_tests/src/globals_and_io.c @@ -0,0 +1,41 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +static char someglobal = 1; +static int gInt[2] = {42, 43}; + +int writeit() +{ + write(2,&someglobal,1); + someglobal++; + write(2,&someglobal,1); + return 0; +} + +int main(void) +{ + someglobal = 0x68; + writeit(); + gInt[1] = 44; + printf("\n"); + printf("%i, %i\n", gInt[0], gInt[1]); + return 0; +} diff --git a/tests/integration_tests/src/hello_c.c b/tests/integration_tests/src/hello_c.c new file mode 100644 index 000000000..5bf725789 --- /dev/null +++ b/tests/integration_tests/src/hello_c.c @@ -0,0 +1,22 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2019 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main() { + puts("Hello world!"); +} diff --git a/tests/integration_tests/src/hello_cpp.cpp b/tests/integration_tests/src/hello_cpp.cpp new file mode 100644 index 000000000..5f124ac46 --- /dev/null +++ b/tests/integration_tests/src/hello_cpp.cpp @@ -0,0 +1,22 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2019 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main() { + std::cout << "Hello world!" << std::endl; +} diff --git a/tests/integration_tests/src/helloworld.c b/tests/integration_tests/src/helloworld.c new file mode 100644 index 000000000..4c284a5ab --- /dev/null +++ b/tests/integration_tests/src/helloworld.c @@ -0,0 +1,61 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +static const int g = 8; + +int foo (void) +{ + const int *p = &g; + + if ((*p) > 12) + return 17; + + char str [] = "Hello, world!\n"; + + for (int i = 0; i < 14; ++i) + putchar (str [i]); + + putchar('H'); + putchar('e'); + putchar('l'); + putchar('l'); + putchar('o'); + putchar(','); + putchar(' '); + putchar('w'); + putchar('o'); + putchar('r'); + putchar('l'); + putchar('d'); + putchar('!'); + putchar('\n'); + + printf ("And now from printf()!\n"); + + int i; + i = 4 + (*p); + printf ("Calculated %d\n", i + 42); + + return 0; +} + +int main (int argc, char **argv) +{ + return foo (); +} diff --git a/tests/integration_tests/src/iostream_basics.cpp b/tests/integration_tests/src/iostream_basics.cpp new file mode 100644 index 000000000..bd27b8c3c --- /dev/null +++ b/tests/integration_tests/src/iostream_basics.cpp @@ -0,0 +1,37 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +struct A { + std::string value; + + A( std::string&& str ) : value( std::move( str ) ) { + std::cout << "I was stolen from!:" << str << std::endl; + } + + void shout() { + std::cout << value << std::endl; + } +}; + +int main() { + std::cout << "Hello World!" << std::endl; + std::string precious = "My little precious"; + A a( std::move( precious ) ); + a.shout(); +} diff --git a/tests/integration_tests/src/local-array.c b/tests/integration_tests/src/local-array.c new file mode 100644 index 000000000..981fa1c74 --- /dev/null +++ b/tests/integration_tests/src/local-array.c @@ -0,0 +1,34 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main (int argc, char **argv) +{ + int array [] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + + for (int i = 0; i < 10; ++i) + { + result += array [i]; + printf ("result now: %d\n", result); + } + + printf ("final result: %d\n", result); + + return 0; +} diff --git a/tests/integration_tests/src/matrix_vector_mult.c b/tests/integration_tests/src/matrix_vector_mult.c new file mode 100644 index 000000000..aee8ba238 --- /dev/null +++ b/tests/integration_tests/src/matrix_vector_mult.c @@ -0,0 +1,21 @@ +/* TAGS: min c */ +#include + +static const int A [] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +static const int b [] = { 4, 7, 11 }; +static int c [3] = { 0 }; + +int main (int argc, char **argv) +{ + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + c [i] += A [3 * i + j] * b [j]; + } + } + + printf ("c = ( %d, %d, %d )^T\n", c [0], c [1], c [2]); + + return 0; +} diff --git a/tests/integration_tests/src/open_close_dir.c b/tests/integration_tests/src/open_close_dir.c new file mode 100644 index 000000000..f0b138cc8 --- /dev/null +++ b/tests/integration_tests/src/open_close_dir.c @@ -0,0 +1,42 @@ +/* TAGS: min c */ +/* TEST: /usr */ +/* TEST: /eqeqeqwe */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +int main (int argc, char **argv) +{ + if (argc != 2) + { + puts ("error: need exactly one argument"); + return 1; + } + + DIR *dir = opendir (argv [1]); + if (!dir) + perror ("opendir"); + else + closedir (dir); + + printf ("errno: %d\n", errno); + + return 0; +} diff --git a/tests/integration_tests/src/operator_new.cpp b/tests/integration_tests/src/operator_new.cpp new file mode 100644 index 000000000..5db41b994 --- /dev/null +++ b/tests/integration_tests/src/operator_new.cpp @@ -0,0 +1,40 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +struct A { + std::string message; + + void shout( const std::string& target ) { + std::cout << "He shouted at " << target << " with this words:" << std::endl; + std::cout << "\t" << message << std::endl; + } +}; + +void challenge( A*& a ) { + a = new A( { "How dare you touch my duck?" } ); + std::cout << "INFO: challenge is prepared" << std::endl; +} + +int main() { + A* ptr; + challenge( ptr ); + ptr->shout( "Ivan" ); + delete ptr; +} diff --git a/tests/integration_tests/src/pointers.c b/tests/integration_tests/src/pointers.c new file mode 100644 index 000000000..6f3e90b89 --- /dev/null +++ b/tests/integration_tests/src/pointers.c @@ -0,0 +1,31 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main (int argc, char **argv) +{ + int x = 93; + int *y = &x; + int **z = &y; + + printf (" x: %d\n", x); + printf (" *y: %d\n", *y); + printf ("**z: %d\n", **z); + + return 0; +} diff --git a/tests/integration_tests/src/printf_floats.cpp b/tests/integration_tests/src/printf_floats.cpp new file mode 100644 index 000000000..d74cf468a --- /dev/null +++ b/tests/integration_tests/src/printf_floats.cpp @@ -0,0 +1,22 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main() { + printf("Hello %.2f %.2f %i %.2f %.2f %.2f %.2f %.2f %i %.2f and World!\n", 4.40f, 4.41f, -30, 4.424324f, 4.43f, 4.44f, 4.45f, 4.46f, 30, 4.47f ); +} diff --git a/tests/integration_tests/src/pthread.cpp b/tests/integration_tests/src/pthread.cpp new file mode 100644 index 000000000..7622039be --- /dev/null +++ b/tests/integration_tests/src/pthread.cpp @@ -0,0 +1,97 @@ +/* TAGS: min cpp */ +/* LD_OPTS: -lpthread */ +/* + * Copyright (c) 2017 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +static pthread_cond_t gCond; +static pthread_mutex_t gLock; +static int gFlag = 0; + +__thread int tls_data1; +__thread int tls_data2; + +typedef struct { + int data1; + int data2; +} thread_parm_t; + +void bar() { + printf("bar(), tls data=%d %d\n", tls_data1, tls_data2); + return; +} + +void foo() { + printf("foo(), tls data=%d %d\n", tls_data1, tls_data2); + bar(); +} + +void *theThread(void *parm) +{ + thread_parm_t *gData; + + pthread_mutex_lock(&gLock); + pthread_cond_wait(&gCond, &gLock); + + gFlag += 1; + + gData = (thread_parm_t*)parm; + tls_data1 = gData->data1; + tls_data2 = gData->data2; + foo(); + + pthread_mutex_unlock(&gLock); + return NULL; +} + +int main(int argc, char **argv) { + int rc=0, i; + pthread_t thread[2]; + thread_parm_t gData[2]; + + printf("Create threads\n"); + pthread_mutex_init(&gLock, NULL); + pthread_cond_init(&gCond, NULL); + for (i=0; i < 2; i++) { + gData[i].data1 = i; + gData[i].data2 = (i+1)*2; + rc = pthread_create(&thread[i], NULL, theThread, &gData[i]); + if (rc) { + printf("Failed with %d at pthread_create()", rc); + exit(1); + } + } + + // synchronize output. this gets printed before threads print + printf("Wait for the threads to complete, and release their resources\n"); + fflush(stdout); + while (gFlag < 2) { + pthread_cond_signal(&gCond); + } + for (i=0; i < 2; i++) { + rc = pthread_join(thread[i], NULL); + if (rc) { + printf("Failed with %d at pthread_join()", rc); + exit(1); + } + } + + printf("Main completed\n"); + return 0; +} diff --git a/tests/integration_tests/src/qsort.c b/tests/integration_tests/src/qsort.c new file mode 100644 index 000000000..731351f50 --- /dev/null +++ b/tests/integration_tests/src/qsort.c @@ -0,0 +1,45 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +void fill() { + printf(" F "); +} + +int compare( const void* a, const void* b ) { + int arg1 = *( const int* ) a; + int arg2 = *( const int* ) b; + if ( arg1 < arg2 ) return -1; + if ( arg1 > arg2 ) return 1; + return 0; +} + +int main() { + int arr[] = { - 2, 5, 6, 8, 10, 12 }; + int size = sizeof arr / sizeof *arr; + + qsort( arr, size, sizeof( int ), compare ); + int prev = -42; + printf( "Sorted: " ); + for ( int i = 0; i < size; ++i ) { + printf("%i ", arr[i] ); + fill(); + } + printf("\n"); +} diff --git a/tests/integration_tests/src/qsort_function_ptrs.cpp b/tests/integration_tests/src/qsort_function_ptrs.cpp new file mode 100644 index 000000000..3545c4723 --- /dev/null +++ b/tests/integration_tests/src/qsort_function_ptrs.cpp @@ -0,0 +1,96 @@ +/* TAGS: min cpp */ +/* TEST: 23 */ +/* TEST: 43 */ +/* TEST: 435 */ + + +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +int zero() { + return 0; +} + +int one() { + return 1; +} + +int two() { + return 2; +} + +int four() { + return 4; +} + +int many() { + return 42; +} + + +using zTy = int(*)(); + +template +using fTy = int(baseTy*, size_t, int); + + +int compare(const void* a, const void* b) { + printf("Comparing:\n"); + zTy arg1 = *(zTy *) a; + printf("\tArg1 %i\n", arg1()); + + zTy arg2 = *(zTy *) b; + printf("\tArg2 %i\n", arg2()); + + int rhs = arg1(); + int lhs = arg2(); + if (rhs < lhs) return -1; + if (rhs > lhs) return 1; + return 0; +} + +template +int firstLevel(Func *f, size_t size, int iter) { + int base = f[0](); + for (auto i = 0U; i < iter; ++i) { + base += f[i % size](); + printf("Iter: %i \tbase: %i\n", i, base); + } + + printf("Before sort:\n"); + for (auto i = 0U; i < size; ++i) { + printf("\t%i", f[i]()); + } + printf("\n"); + + qsort(f, size, sizeof(zTy), compare); + printf("Sorted:\n"); + for (auto i = 0U; i < size; ++i) { + printf("\t%i", f[i]()); + } + printf("\n"); + return base; +} + +int main(int argc, char* argv[]) { + int a = atoi(argv[1]); + int(*funcs[])() = {two, many, four, zero, one}; + int result = firstLevel(funcs, 5, a); +} + diff --git a/tests/integration_tests/src/rand_and_strtol.c b/tests/integration_tests/src/rand_and_strtol.c new file mode 100644 index 000000000..24df5c325 --- /dev/null +++ b/tests/integration_tests/src/rand_and_strtol.c @@ -0,0 +1,43 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +int main (void) +{ + int x = -11; + int y = abs (x); + + printf ("11 : %i\n", y); + + srand (13); + + int i = rand (); + printf ("rand: %i\n", i); + + char str [30] = "2030300 This is a test"; + char *ptr; + long ret; + + printf ("str: \"%s\"\n", str); + ret = strtol (str, &ptr, 10); + printf ("Number: %ld\n", ret); + printf ("Remainder: \"%s\"\n", ptr); + + return 0; +} diff --git a/tests/integration_tests/src/readdir.c b/tests/integration_tests/src/readdir.c new file mode 100644 index 000000000..cb1e88375 --- /dev/null +++ b/tests/integration_tests/src/readdir.c @@ -0,0 +1,66 @@ +/* TAGS: min c */ +/* TEST: readdir.c */ +/* TEST: /tmp */ +/* TEST: file-that-does-not-exist */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +static void lookup (const char *arg) +{ + DIR *dirp; + struct dirent *dp; + + if ((dirp = opendir (".")) == NULL) + { + perror ("couldn't open '.'"); + return; + } + + do + { + errno = 0; + if ((dp = readdir (dirp)) != NULL) + { + if (strcmp (dp->d_name, arg) != 0) + continue; + + printf ("found %s\n", arg); + closedir (dirp); + return; + } + } while (dp != NULL); + + if (errno != 0) + perror ("error reading directory"); + else + printf ("failed to find %s\n", arg); + + closedir (dirp); + return; +} + +int main (int argc, char *argv[]) +{ + for (int i = 1; i < argc; i++) + lookup (argv [i]); + + return 0; +} diff --git a/tests/integration_tests/src/simple_array.c b/tests/integration_tests/src/simple_array.c new file mode 100644 index 000000000..e10c5540c --- /dev/null +++ b/tests/integration_tests/src/simple_array.c @@ -0,0 +1,32 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +static const int array [10] = { 4, 7, 9, 13, 1, 7, 1, 0, 4, 5 }; + +int main (void) +{ + int result = 0; + + for (int i = 0; i < 10; ++i) + result += array [i]; + + printf ("Result: %d\n", result); + + return 0; +} diff --git a/tests/integration_tests/src/simple_exit.c b/tests/integration_tests/src/simple_exit.c new file mode 100644 index 000000000..f7e9d0684 --- /dev/null +++ b/tests/integration_tests/src/simple_exit.c @@ -0,0 +1,24 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main (int argc, char **argv) +{ + exit (EXIT_SUCCESS); + return 1; +} diff --git a/tests/integration_tests/src/simple_for_loop.c b/tests/integration_tests/src/simple_for_loop.c new file mode 100644 index 000000000..fe8539dca --- /dev/null +++ b/tests/integration_tests/src/simple_for_loop.c @@ -0,0 +1,29 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int main (void) +{ + int i = 5; + + for (i = 0; i < 85; ++i) { } + + printf ("i: %d\n", i); + + return 0; +} diff --git a/tests/integration_tests/src/simple_main.c b/tests/integration_tests/src/simple_main.c new file mode 100644 index 000000000..17d4f2d04 --- /dev/null +++ b/tests/integration_tests/src/simple_main.c @@ -0,0 +1,29 @@ +/* TAGS: min c */ +/* CC_OPTS: */ +/* LD_OPTS: */ +/* LIFT_OPTS: EXP +explicit_args +explicit_args_count 8 !abi_libraries */ +/* TEST: --help */ +/* STDIN: F/inputs.txt */ +/* TEST: --version */ +/* TEST: --extract nonsense.txt */ +/* STDIN: Hello World\nThis Should Be Together */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +int main (int argc, char **argv) +{ + return 0; +} diff --git a/tests/integration_tests/src/struct.c b/tests/integration_tests/src/struct.c new file mode 100644 index 000000000..4af84ce76 --- /dev/null +++ b/tests/integration_tests/src/struct.c @@ -0,0 +1,59 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +struct Book +{ + char title [50]; + char author [50]; + char subject [100]; + int book_id; +}; + +void printBook (struct Book book); + +int main () +{ + struct Book Book1; + struct Book Book2; + + strcpy (Book1.title, "C Programming"); + strcpy (Book1.author, "Nuha Ali"); + strcpy (Book1.subject, "C Programming Tutorial"); + Book1.book_id = 6495407; + + strcpy (Book2.title, "Telecom Billing"); + strcpy (Book2.author, "Zara Ali"); + strcpy (Book2.subject, "Telecom Billing Tutorial"); + Book2.book_id = 6495700; + + printBook (Book1); + printBook (Book2); + + return 0; +} + +void printBook (struct Book book) +{ + printf ("Book title: %s\n", book.title); + printf ("Book author: %s\n", book.author); + printf ("Book subject: %s\n", book.subject); + printf ("Book book_id: %d\n", book.book_id); + printf ("\n"); +} diff --git a/tests/integration_tests/src/struct_func_ptr.cpp b/tests/integration_tests/src/struct_func_ptr.cpp new file mode 100644 index 000000000..e0294a875 --- /dev/null +++ b/tests/integration_tests/src/struct_func_ptr.cpp @@ -0,0 +1,47 @@ +/* TAGS: min cpp */ +/* TEST: */ +/* STDIN: 4\n4\n */ +/* TEST: */ +/* STDIN: 5\n5\n */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +template< typename F > +struct Holder { + F a; + F b; +}; + +int foo( std::string str ) { + std::cout << "Foo: " << str << std::endl; + return 1; +} + +int boo( std::string str ) { + std::cout << "Boo: " << str << std::endl; + return 2; +} + +int main() { + Holder< int(*)(std::string) > h { foo, boo }; + int a; + std::cin >> a; + if ( a % 2 ) h.a( "hello" ); + else h.b( "world" ); +} diff --git a/tests/integration_tests/src/template_function_ptrs.cpp b/tests/integration_tests/src/template_function_ptrs.cpp new file mode 100644 index 000000000..7a62bfb40 --- /dev/null +++ b/tests/integration_tests/src/template_function_ptrs.cpp @@ -0,0 +1,73 @@ +/* TAGS: min cpp */ +/* TEST: 42 */ +/* TEST: -543 */ +/* TEST: 21 */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +template< typename FuncPtr, typename Num = int > +int specialSum( Num* arr, int size, FuncPtr ptr, int base = 0 ) { + int result = base; + for ( int i = 0; i < size; ++i ) { + result = ptr( result, arr[i] ); + } + return result; +} + + +int add( int a, int b ) { + return a + b; +} + +int dec( int a, int b ) { + return a - 1; +} + +int joker( int a, int b ) { + if ( a > b ) return dec ( a, b ); + if ( a < b ) return add ( a, b ); + return 0; +} + +template< typename ordFunc, typename getFunc > +auto specialFunc( ordFunc ord, getFunc get, int input ) { + int salt = 42; + + auto g = get( input, salt ); + int arr[3] = { input, salt, g }; + + if ( specialSum( arr, 3, ord) > 0 ) return add; + else return dec; +} + +int main( int argc, char* argv[] ) { + int arr[10] = { 1, 4, 32, -54, 5, 6, 76, 12, 45, -89 }; + int res = specialSum( arr, 10, add ); + printf( "%i\n", res ); + + res = specialSum( arr, 10, dec ); + printf( "%i\n", res ); + + res = specialSum( arr, 10, joker ); + printf( "%i\n", res ); + + auto special = specialFunc( add, dec, std::stoi( argv[1] ) ); + printf( "%i\n", special( 37, 5 ) ); +} diff --git a/tests/integration_tests/src/virtual.cpp b/tests/integration_tests/src/virtual.cpp new file mode 100644 index 000000000..4c982da40 --- /dev/null +++ b/tests/integration_tests/src/virtual.cpp @@ -0,0 +1,68 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +struct Parent { +protected: + std::string name; +public: + Parent( const std::string& str ) : name( str ) { + std::cout << "Parent is setting name!" << std::endl; + } + + virtual ~Parent() = default; + + virtual void shout() = 0; +}; + +struct Angry : Parent { +protected: + int age = 42; +public: + Angry( const std::string& str ) : Parent( str ) { + std::cout << "Angry person was born" << std::endl; + } + + void shout() override { + std::cout << "I am angry! I am: " << name << std::endl; + } +}; + +struct Calm : Parent { + Calm( const std::string& str ) : Parent( str ) { + std::cout << "Calm person was born" << std::endl; + } + + void shout() override { + std::cout << "Me calm. Me: " << name << std::endl; + } +}; + +int main() { + Calm a( "Caleb" ); + Parent* oldMan = new Angry( "Ivan" ); + + // This is the troublesome one + oldMan->shout(); + + std::cout << "And so shout happened" << std::endl; + delete oldMan; + std::cout << "Story is now over" << std::endl; +} diff --git a/tests/integration_tests/src/virtual_simpler.cpp b/tests/integration_tests/src/virtual_simpler.cpp new file mode 100644 index 000000000..5563f6175 --- /dev/null +++ b/tests/integration_tests/src/virtual_simpler.cpp @@ -0,0 +1,63 @@ +/* TAGS: min cpp */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +struct Parent { +protected: + std::string name; +public: + Parent( const std::string& str ) : name( str ) { + std::cout << "Parent is setting name!" << std::endl; + } + + virtual ~Parent() = default; + + virtual void shout() = 0; +}; + +struct Angry : Parent { +protected: + int age = 42; +public: + Angry( const std::string& str ) : Parent( str ) { + //Empty + } + + void shout() override { + std::cout << "I am angry! I am: " << name << std::endl; + } +}; + +struct Calm : Parent { + Calm( const std::string& str ) : Parent( str ) { + //Empty + } + + void shout() override { + std::cout << "Me calm. Me: " << name << std::endl; + } +}; + +int main() { + Parent* oldMan = new Angry( "Ivan" ); + oldMan->shout(); + delete oldMan; + +} diff --git a/tests/integration_tests/src/x86_bts.c b/tests/integration_tests/src/x86_bts.c new file mode 100644 index 000000000..7999832dc --- /dev/null +++ b/tests/integration_tests/src/x86_bts.c @@ -0,0 +1,30 @@ +/* TAGS: min c */ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +int main(void) +{ + unsigned int x = 0xdeadbee0; + unsigned int n = 3; + __asm__ __volatile__ ( "bts %1,%0": "+rm"(x) : "r"(n)); + printf("x is: %08x\n", x); + return 0; +} diff --git a/tests/integration_tests/stdin/empty.stdin b/tests/integration_tests/stdin/empty.stdin new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration_tests/tags/gzip.default.config b/tests/integration_tests/tags/gzip.default.config new file mode 100644 index 000000000..af2cf8f57 --- /dev/null +++ b/tests/integration_tests/tags/gzip.default.config @@ -0,0 +1,2 @@ +TAGS: gzip c gnu +LIFT_OPTS: diff --git a/tests/integration_tests/util.py b/tests/integration_tests/util.py new file mode 100644 index 000000000..b6b794270 --- /dev/null +++ b/tests/integration_tests/util.py @@ -0,0 +1,55 @@ +# Copyright (c) 2019 Trail of Bits, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +def strip_whole_config(filename): + if not filename.endswith(".config"): + return "" + filename = filename.rstrip(".config") + basename, ext = os.path.splitext(filename) + return basename + +def get_binaries(directory): + result = set() + for f in os.listdir(directory): + filename = strip_whole_config(f) + if filename: + result.add(filename) + return result + +def get_tags(config): + with open(config, 'r') as f: + line = f.readline().rstrip('\n') + tokens = line.split(' ') + if tokens[0] != 'TAGS:': + return [] + return tokens[1:] + +def get_bin2tags(directory): + result = {} + for f in os.listdir(directory): + filename = strip_whole_config(f) + if not filename: + continue + + tags = get_tags(os.path.join(directory, f)) + if filename not in result: + result[filename] = tags + else: + result[filename].append(tags) + return result + +def get_cfg(directory, name): + return os.path.join(directory, name + '.cfg')