diff --git a/configure.ac b/configure.ac index 113a549d..47819b39 100644 --- a/configure.ac +++ b/configure.ac @@ -135,6 +135,20 @@ if test "x$want_profile_coverage" = "xyes"; then FKO_CHECK_COMPILER_ARG_LDFLAGS_ONLY([-lgcov]) fi +dnl Decide whether or not to compile in certain features that enable fuzzing +dnl of fwknop code - this is for testing purposes only. +dnl +want_fuzzing_interfaces=no +AC_ARG_ENABLE([fuzzing-interfaces], + [AS_HELP_STRING([--enable-fuzzing-interfaces], + [Build fwknop binaries with support for fuzzing interfaces @<:@default is to disable@:>@])], + [want_fuzzing_interfaces=$enableval], + []) + +if test "x$want_fuzzing_interfaces" = "xyes"; then + AC_DEFINE([FUZZING_INTERFACES], [1], [Define for fuzzing interfaces support]) +fi + dnl Decide whether or not to enable all warnings with -Wall dnl use_wall=yes diff --git a/lib/fko.h b/lib/fko.h index 33304ed3..7a259bfa 100644 --- a/lib/fko.h +++ b/lib/fko.h @@ -381,6 +381,10 @@ DLL_API int fko_set_spa_hmac(fko_ctx_t ctx, const char * const hmac_key, DLL_API int fko_get_spa_hmac(fko_ctx_t ctx, char **enc_data); DLL_API int fko_get_encoded_data(fko_ctx_t ctx, char **enc_data); +#if FUZZING_INTERFACES +DLL_API int fko_set_encoded_data(fko_ctx_t ctx, const char * const encoded_msg, + const int msg_len, const int do_digest, const int digest_type); +#endif /* Get context data functions */ diff --git a/lib/fko_encode.c b/lib/fko_encode.c index 889efeb5..5a34b352 100644 --- a/lib/fko_encode.c +++ b/lib/fko_encode.c @@ -241,4 +241,76 @@ fko_get_encoded_data(fko_ctx_t ctx, char **enc_msg) return(FKO_SUCCESS); } +/* Set the fko SPA encoded data (this is a convenience + * function mostly used for tests that involve fuzzing). +*/ +#if FUZZING_INTERFACES +int +fko_set_encoded_data(fko_ctx_t ctx, + const char * const encoded_msg, const int msg_len, + const int require_digest, const int digest_type) +{ + char *tbuf = NULL; + int res = FKO_SUCCESS, mlen; + + /* Must be initialized + */ + if(!CTX_INITIALIZED(ctx)) + return(FKO_ERROR_CTX_NOT_INITIALIZED); + + if(encoded_msg == NULL) + return(FKO_ERROR_INVALID_DATA); + + ctx->encoded_msg = strdup(encoded_msg); + + ctx->state |= FKO_DATA_MODIFIED; + + if(ctx->encoded_msg == NULL) + return(FKO_ERROR_MEMORY_ALLOCATION); + + /* allow arbitrary length (i.e. let the decode routines validate + * SPA message length). + */ + ctx->encoded_msg_len = msg_len; + + if(require_digest) + { + fko_set_spa_digest_type(ctx, digest_type); + if((res = fko_set_spa_digest(ctx)) != FKO_SUCCESS) + { + return res; + } + + /* append the digest to the encoded message buffer + */ + mlen = ctx->encoded_msg_len + ctx->digest_len + 2; + tbuf = calloc(1, mlen); + if(tbuf == NULL) + return(FKO_ERROR_MEMORY_ALLOCATION); + + /* memcpy since the provided encoded buffer might + * have an embedded NULL? + */ + mlen = snprintf(tbuf, mlen, "%s:%s", ctx->encoded_msg, ctx->digest); + + if(ctx->encoded_msg != NULL) + free(ctx->encoded_msg); + + ctx->encoded_msg = strdup(tbuf); + if(ctx->encoded_msg == NULL) + { + free(tbuf); + return(FKO_ERROR_MEMORY_ALLOCATION); + } + + ctx->encoded_msg_len = mlen; + free(tbuf); + } + + FKO_CLEAR_SPA_DATA_MODIFIED(ctx); + + return(FKO_SUCCESS); +} +#endif + /***EOF***/ diff --git a/test/conf/client-gpg-no-pw/trustdb.gpg b/test/conf/client-gpg-no-pw/trustdb.gpg index 4c30150c..58ec2d6d 100644 Binary files a/test/conf/client-gpg-no-pw/trustdb.gpg and b/test/conf/client-gpg-no-pw/trustdb.gpg differ diff --git a/test/fko-wrapper/Makefile b/test/fko-wrapper/Makefile index e88a5ab2..3e970ba2 100644 --- a/test/fko-wrapper/Makefile +++ b/test/fko-wrapper/Makefile @@ -2,5 +2,8 @@ all : fko_wrapper.c gcc -Wall -g -I../../lib fko_wrapper.c -o fko_wrapper -L../../lib/.libs -lfko +fuzzing: fko_wrapper.c + gcc -Wall -g -DFUZZING_INTERFACES -I../../lib fko_wrapper.c -o fko_wrapper -L../../lib/.libs -lfko + clean: rm -f fko_wrapper diff --git a/test/fko-wrapper/fko_wrapper.c b/test/fko-wrapper/fko_wrapper.c index cbb4944c..0ece661f 100644 --- a/test/fko-wrapper/fko_wrapper.c +++ b/test/fko-wrapper/fko_wrapper.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "fko.h" #define ENABLE_GPG_TESTS 0 @@ -23,12 +24,20 @@ #define NO_DIGEST 0 #define DO_DIGEST 1 #define RAW_DIGEST 2 +#define MAX_LINE_LEN 3000 /* really long for fuzzing tests */ #define ENC_KEY "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /* 32 bytes */ #define HMAC_KEY "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" /* 32 bytes */ +#define IS_EMPTY_LINE(x) ( \ + x == '#' || x == '\n' || x == '\r' || x == ';' || x == '\0' \ +) + static void display_ctx(fko_ctx_t ctx); static void test_loop(int new_ctx_flag, int destroy_ctx_flag); static void test_loop_compounded(void); +#if FUZZING_INTERFACES +static void spa_encoded_msg_fuzzing(void); +#endif static void ctx_update(fko_ctx_t *ctx, int new_ctx_flag, int destroy_ctx_flag, int print_flag); static void spa_default_ctx(fko_ctx_t *ctx); @@ -67,9 +76,90 @@ int main(void) { printf("[+] Total libfko function calls (after compounded tests): %d\n\n", spa_calls); +#if FUZZING_INTERFACES + printf("[+] libfko fuzzing by setting SPA buffer manually...\n"); + spa_encoded_msg_fuzzing(); +#endif + return 0; } +#if FUZZING_INTERFACES +static void +spa_encoded_msg_fuzzing(void) +{ + fko_ctx_t decode_ctx = NULL; + int res = 0, pkt_id, require_success, require_digest, digest_type, msg_len; + int line_ctr = 0, spa_payload_ctr = 0; + FILE *fz = NULL; + char line[MAX_LINE_LEN] = {0}; + char b64_encoded_msg[MAX_LINE_LEN] = {0}; + unsigned char b64_decoded_msg[MAX_LINE_LEN] = {0}; + + /* fuzzing file contents (or from stdin) are formatted like this: + * + * + */ + + if ((fz = fopen("fuzz_spa_payloads", "r")) == NULL) + return; + + while ((fgets(line, MAX_LINE_LEN, fz)) != NULL) + { + line_ctr++; + line[MAX_LINE_LEN-1] = '\0'; + + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + + if(IS_EMPTY_LINE(line[0])) + continue; + + if(sscanf(line, "%d %d %d %d %s", &pkt_id, &require_success, + &require_digest, &digest_type, b64_encoded_msg) != 5) + { + printf("[+] fuzzing parsing error at line: %d\n", line_ctr); + continue; + } + + msg_len = fko_base64_decode(b64_encoded_msg, b64_decoded_msg); + + spa_payload_ctr++; + + fko_new(&decode_ctx); + + if ((res = fko_set_encoded_data(decode_ctx, (char *) b64_decoded_msg, + msg_len, require_digest, digest_type)) != FKO_SUCCESS) { + printf("[-] pkt_id: %d, fko_set_encoded_data(): %s\n", pkt_id, fko_errstr(res)); + } + + res = fko_decode_spa_data(decode_ctx); + if (require_success) { + if (res != FKO_SUCCESS) { + printf("[-] pkt_id: %d, expected decode success but: fko_decode_spa_data(): %s\n", + pkt_id, fko_errstr(res)); + } + } else { + if (res == FKO_SUCCESS) { + printf("[-] pkt_id: %d, expected decode failure but: fko_decode_spa_data(): %s\n", + pkt_id, fko_errstr(res)); + } + } + + fko_destroy(decode_ctx); + + memset(line, 0x0, MAX_LINE_LEN); + memset(b64_encoded_msg, 0x0, MAX_LINE_LEN); + } + + fclose(fz); + + printf("[+] Sent %d SPA payloads through libfko encode/decode cycle...\n", + spa_payload_ctr); + return; +} +#endif + static void test_loop_compounded(void) { diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py new file mode 100755 index 00000000..ee642f94 --- /dev/null +++ b/test/spa_fuzzing.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python +# +# Purpose: This script generates SPA packet payloads that are designed to +# act as fuzzer against libfko SPA decoding routines. +# +# Fuzzing file format: +# +# +# +# SPA payload format (before: +# +# ::::: 0: + orig_field = spa_payload[repl_end+1:idx] + else: + orig_field = spa_payload[repl_end:idx] + + repl_start = repl_end + repl_end = idx + + ### now generate fuzzing data for this field + for c in range(0, 3) + range(33, 47) + range(65, 67) + range(127, 130) + range(252, 256): + for l in [1, 2, 3, 4, 5, 6, 10, 14, 15, 16, 17, 24, 31, 32, 33, \ + 63, 64, 127, 128, 129, 150, 220, 230, 254, 255, 256, 257, 258]: + + fuzzing_field = '' + require_b64 = orig_field.isdigit() + + for n in range(0, l): + fuzzing_field += chr(c) + + new_payloads = [ + spa_payload[:repl_start], ### for payloads without original field + spa_payload[:repl_start], ### prepend fuzzing field with original + spa_payload[:repl_start] ### append fuzzing field to original + ] + + if field_num > 1: + for i in range(0, len(new_payloads)): + new_payloads[i] += ':' + + if idx == len(spa_payload)-1: + field_variants(new_payloads, fuzzing_field, orig_field, require_b64) + else: + if field_num == 1 or field_num in range(3, 6): + ### fields: rand val, time stamp, version, and SPA type + field_variants(new_payloads, fuzzing_field, orig_field, False) + elif field_num == 2: ### user field + field_variants(new_payloads, fuzzing_field, orig_field, True) + else: + field_variants(new_payloads, fuzzing_field, orig_field, require_b64) + + for i in range(0, len(new_payloads)): + new_payloads[i] += spa_payload[repl_end:] + + for s in new_payloads: + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(s) + pkt_id += 1 + + return pkt_id + +def field_variants(new_payloads, fuzzing_field, orig_field, require_b64): + if require_b64: + decoded_orig_field = spa_base64_decode(orig_field) + new_payloads[0] += base64.b64encode(fuzzing_field) + new_payloads[1] += base64.b64encode(fuzzing_field+decoded_orig_field) + new_payloads[2] += base64.b64encode(decoded_orig_field+fuzzing_field) + else: + new_payloads[0] += fuzzing_field + new_payloads[1] += fuzzing_field+orig_field + new_payloads[2] += orig_field+fuzzing_field + return + +def spa_base64_decode(b64str): + ### account for how fwknop strips '=' chars + remainder = len(b64str) % 4 + if remainder != 0: + for i in range(0, remainder): + b64str += '=' + + return base64.b64decode(b64str) + +def valid_payloads(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " valid payload + valid digest types..." + for digest_type in range(0, 6): + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) + pkt_id += 1 + return pkt_id + +def invalid_digest_types(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " invalid digest types..." + for digest_type in [-1, 6, 7]: + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) + pkt_id += 1 + return pkt_id + +def truncated_lengths(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " truncated lengths..." + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[:l]) + pkt_id += 1 + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[l:]) + pkt_id += 1 + + return pkt_id + +def rm_chunks(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " splice blocks of chars..." + for bl in range(1, 20): + for l in range(0, len(spa_payload)): + new_payload = spa_payload[:l] + spa_payload[l+bl:] + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + return pkt_id + +def append_data_to_end(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " payloads too long..." + for l in [1, 10, 50, 127, 128, 129, 200, 399, \ + 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ + 1200, 1499, 1500, 1501, 2000]: + for non_ascii in range(0, 5) + range(127, 130) + range(252, 256): + new_payload = spa_payload + for p in range(0, l): + new_payload += chr(non_ascii) + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + return pkt_id + +def embedded_separators(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " additional embedded : chars..." + for pos in range(0, len(spa_payload)): + if spa_payload[pos] == ':': + continue + new_payload = list(spa_payload) + new_payload[pos] = ':' + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(''.join(new_payload)) + pkt_id += 1 + return pkt_id + +def embedded_chars(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " non-ascii char tests..." + for pos in range(0, len(spa_payload)): + for non_ascii in range(0, 31) + range(44, 48) + range(127, 131) + range(253, 255): + new_payload = list(spa_payload) + new_payload[pos] = chr(non_ascii) + ### write out the fuzzing line + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(''.join(new_payload)) + pkt_id += 1 + return pkt_id + +def print_hdr(): + print "# " + return + +def parse_cmdline(): + + ### parse command line args + parser = argparse.ArgumentParser() + + parser.add_argument("-c", "--max-packet-count", type=int, help="packet count", default=1000000) + + args = parser.parse_args() + return args + +if __name__ == "__main__": + main() diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 36fb3376..391327dc 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -28,6 +28,7 @@ my $data_tmp = 'data.tmp'; my $key_tmp = 'key.tmp'; my $enc_save_tmp = 'openssl_save.enc'; my $test_suite_path = 'test-fwknop.pl'; +my $username = ''; my $gpg_dir_orig_tar = 'gpg_dirs_orig.tar.gz'; our $gpg_client_home_dir = "$conf_dir/client-gpg"; our $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw"; @@ -322,6 +323,7 @@ my $perl_libfko_constants_file = '../perl/FKO/lib/FKO_Constants.pl'; my $python_libfko_constants_file = '../python/fko.py'; my $fko_wrapper_dir = 'fko-wrapper'; my $python_spa_packet = ''; +my $enable_fuzzing_interfaces_tests = 0; my $enable_client_ip_resolve_test = 0; my $enable_all = 0; my $saved_last_results = 0; @@ -431,6 +433,7 @@ exit 1 unless GetOptions( 'valgrind-disable-suppressions' => \$valgrind_disable_suppressions, 'valgrind-disable-child-silent' => \$valgrind_disable_child_silent, 'enable-all' => \$enable_all, + 'enable-fuzzing-interfaces-tests' => \$enable_fuzzing_interfaces_tests, 'valgrind-path=s' => \$valgrind_path, 'valgrind-suppression-file' => \$valgrind_suppressions_file, ### can set the following to "output.last/valgrind-coverage" if @@ -789,13 +792,6 @@ if ($enable_profile_coverage_check) { } if ($enable_valgrind) { - &run_test({ - 'category' => 'valgrind', - 'subcategory' => 'fko-wrapper', - 'detail' => 'multiple libfko calls', - 'function' => \&compile_execute_fko_wrapper} - ); - &run_test({ 'category' => 'valgrind output', 'subcategory' => 'flagged functions', @@ -1123,6 +1119,15 @@ sub compile_warnings() { my $curr_pwd = cwd() or die $!; + if ($enable_profile_coverage_check) { + ### we're recompiling, so remove any existing profile coverage + ### files since they will be invalidated by the recompile + for my $extension ('*.gcno', '*.gcda', '*.gcov') { + ### remove profile output from any previous run + system qq{find .. -name $extension | xargs rm 2> /dev/null}; + } + } + chdir '..' or die $!; ### 'make clean' as root @@ -1133,10 +1138,6 @@ sub compile_warnings() { } if ($sudo_path) { - my $username = getpwuid((stat("test/$test_suite_path"))[4]); - die "[*] Could not determine test/$test_suite_path owner" - unless $username; - unless (&run_cmd("$sudo_path -u $username make", $cmd_out_tmp, "test/$curr_test_file")) { unless (&run_cmd('make', $cmd_out_tmp, @@ -1220,6 +1221,13 @@ sub profile_coverage() { $cmd_out_tmp, $curr_test_file); } + if ($username) { + for my $extension ('*.gcno', '*.gcda', '*.gcov') { + ### remove profile output from any previous run + system qq{find .. -name $extension | xargs chown $username}; + } + } + return 1; } @@ -5993,6 +6001,12 @@ sub init() { $git_path = &find_command('git') unless $git_path; $perl_path = &find_command('perl') unless $perl_path; + if ($sudo_path) { + $username = getpwuid((stat($test_suite_path))[4]); + die "[*] Could not determine $test_suite_path owner" + unless $username; + } + ### On Mac OS X look for otool instead of ldd unless ($lib_view_cmd) { $lib_view_cmd = &find_command('otool'); @@ -6015,18 +6029,11 @@ sub init() { if ($gcov_path) { if ($enable_profile_coverage_check - and not $preserve_previous_coverage_files and not $list_mode) { if ($profile_coverage_init) { - print "[+] --profile-coverage-init mode, ", - "removing existing .gcda, .gcno, and .gcov files...\n"; - } - for my $extension ('*.gcno', '*.gcda', '*.gcov') { - ### remove profile output from any previous run - system qq{find .. -name $extension | xargs rm 2> /dev/null}; - } - if ($profile_coverage_init) { - print "[+] Recompiling fwknop...\n"; + print "[+] Recompiling fwknop and removing previous coverage files...\n"; + ### if we recompile then remove the .gcno files (which are + ### generated at compile time) &compile_warnings(); if (&file_find_regex([qr/profile\-arcs.*test\-coverage/], $MATCH_ALL, $APPEND_RESULTS, $curr_test_file)) { @@ -6250,8 +6257,7 @@ sub import_previous_valgrind_coverage_info() { return; } -sub compile_execute_fko_wrapper() { - my $rv = 1; +sub compile_wrapper() { unless (-d $fko_wrapper_dir) { &write_test_file("[-] fko wrapper directory " . @@ -6268,14 +6274,13 @@ sub compile_execute_fko_wrapper() { return 0; } - if ($sudo_path) { - my $username = getpwuid((stat("../$test_suite_path"))[4]); - die "[*] Could not determine ../$test_suite_path owner" - unless $username; + my $make_str = 'make'; + $make_str .= ' fuzzing' if $enable_fuzzing_interfaces_tests; - unless (&run_cmd("$sudo_path -u $username make", + if ($sudo_path) { + unless (&run_cmd("$sudo_path -u $username $make_str", "../$cmd_out_tmp", "../$curr_test_file")) { - unless (&run_cmd('make', "../$cmd_out_tmp", + unless (&run_cmd($make_str, "../$cmd_out_tmp", "../$curr_test_file")) { chdir '..' or die $!; return 0; @@ -6284,7 +6289,7 @@ sub compile_execute_fko_wrapper() { } else { - unless (&run_cmd('make', "../$cmd_out_tmp", + unless (&run_cmd($make_str, "../$cmd_out_tmp", "../$curr_test_file")) { chdir '..' or die $!; return 0; @@ -6298,74 +6303,45 @@ sub compile_execute_fko_wrapper() { return 0; } - &run_cmd('./run_valgrind.sh', "../$cmd_out_tmp", "../$curr_test_file"); - - $rv = 0 unless &file_find_regex([qr/no\sleaks\sare\spossible/], - $MATCH_ALL, $APPEND_RESULTS, "../$curr_test_file"); - chdir '..' or die $!; + return 1; +} + +sub compile_execute_fko_wrapper() { + + my $rv = &compile_wrapper(); + + if ($rv) { + + chdir $fko_wrapper_dir or die $!; + &run_cmd('./run_valgrind.sh', "../$cmd_out_tmp", "../$curr_test_file"); + + $rv = 0 unless &file_find_regex([qr/no\sleaks\sare\spossible/], + $MATCH_ALL, $APPEND_RESULTS, "../$curr_test_file"); + + chdir '..' or die $!; + } return $rv; } sub compile_execute_fko_wrapper_no_valgrind() { - my $rv = 1; - unless (-d $fko_wrapper_dir) { - &write_test_file("[-] fko wrapper directory " . - "$fko_wrapper_dir does not exist.\n", $curr_test_file); - return 0; - } + my $rv = &compile_wrapper(); - chdir $fko_wrapper_dir or die $!; + if ($rv) { + + chdir $fko_wrapper_dir or die $!; + &run_cmd('./run_no_valgrind.sh', "../$cmd_out_tmp", "../$curr_test_file"); - ### 'make clean' as root - unless (&run_cmd('make clean', "../$cmd_out_tmp", - "../$curr_test_file")) { chdir '..' or die $!; - return 0; - } - if ($sudo_path) { - my $username = getpwuid((stat("../$test_suite_path"))[4]); - die "[*] Could not determine ../$test_suite_path owner" - unless $username; - - unless (&run_cmd("$sudo_path -u $username make", - "../$cmd_out_tmp", "../$curr_test_file")) { - unless (&run_cmd('make', "../$cmd_out_tmp", - "../$curr_test_file")) { - chdir '..' or die $!; - return 0; - } + if (&file_find_regex([qr/segmentation\sfault/i, qr/core\sdumped/i], + $MATCH_ANY, $NO_APPEND_RESULTS, $curr_test_file)) { + &write_test_file("[-] crash message found in: $curr_test_file\n", + $curr_test_file); + $rv = 0; } - - } else { - - unless (&run_cmd('make', "../$cmd_out_tmp", - "../$curr_test_file")) { - chdir '..' or die $!; - return 0; - } - } - - unless (-e 'fko_wrapper' and -e 'run_no_valgrind.sh') { - &write_test_file("[-] fko_wrapper or run_valgrind.sh does not exist.\n", - "../$curr_test_file"); - chdir '..' or die $!; - return 0; - } - - &run_cmd('./run_no_valgrind.sh', - "../$cmd_out_tmp", "../$curr_test_file"); - - chdir '..' or die $!; - - if (&file_find_regex([qr/segmentation\sfault/i, qr/core\sdumped/i], - $MATCH_ANY, $NO_APPEND_RESULTS, $curr_test_file)) { - &write_test_file("[-] crash message found in: $curr_test_file\n", - $curr_test_file); - $rv = 0; } return $rv;