diff --git a/Makefile.am b/Makefile.am index 3d1ff922ff6fdec80a9af8e109a45492d7550b0e..616eb3dc8b892809dd6852d8ccd20841227b9226 100644 --- a/Makefile.am +++ b/Makefile.am @@ -191,6 +191,13 @@ test_cases = \ $(test_cases): @$(TEST_BIN) $(top_srcdir)/$@ +failure_test_cases = \ + tests/failures/fuzz1.svtest \ + tests/failures/fuzz2.svtest + +$(failure_test_cases): + @$(TEST_BIN) -F $(top_srcdir)/$@ + TEST_EXTPROGRAMS_BIN = $(TEST_BIN) \ -P src/plugins/sieve-extprograms/.libs/sieve_extprograms @@ -209,8 +216,8 @@ extprograms_test_cases = \ $(extprograms_test_cases): @$(TEST_EXTPROGRAMS_BIN) $(top_srcdir)/$@ -.PHONY: test test-plugins $(test_cases) $(extprograms_test_cases) -test: all-am $(test_cases) +.PHONY: test test-plugins $(test_cases) $(failure_test_cases) $(extprograms_test_cases) +test: all-am $(test_cases) $(failure_test_cases) test-plugins: all-am $(extprograms_test_cases) check: check-am test diff --git a/src/testsuite/cmd-test-config.c b/src/testsuite/cmd-test-config.c index 75b7247b2acb68afdd3e4bb7637e6d215f1baa89..c1399b0635313d71392857115ffb1cb61a0d374f 100644 --- a/src/testsuite/cmd-test-config.c +++ b/src/testsuite/cmd-test-config.c @@ -494,10 +494,9 @@ cmd_test_config_reload_operation_execute(const struct sieve_runtime_env *renv, ext = sieve_extension_get_by_name(eenv->svinst, str_c(extension)); if (ext == NULL) { - testsuite_test_failf("test_config_reload: " - "unknown extension '%s'", - str_c(extension)); - return SIEVE_EXEC_OK; + return testsuite_test_failf( + renv, "test_config_reload: " + "unknown extension '%s'", str_c(extension)); } sieve_extension_reload(ext); } diff --git a/src/testsuite/cmd-test-fail.c b/src/testsuite/cmd-test-fail.c index 4427afcbd5101beae2f2aa1a7dab9ad4743f5441..9226a3ba097a471f2cdc8141f9a69a43328c3ad0 100644 --- a/src/testsuite/cmd-test-fail.c +++ b/src/testsuite/cmd-test-fail.c @@ -88,17 +88,12 @@ static bool cmd_test_fail_generate(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) { - struct testsuite_generator_context *genctx = - _get_generator_context(cgenv->gentr); - sieve_operation_emit(cgenv->sblock, cmd->ext, &test_fail_operation); /* Generate arguments */ if (!sieve_generate_arguments(cgenv, cmd, NULL)) return FALSE; - sieve_jumplist_add(genctx->exit_jumps, - sieve_binary_emit_offset(cgenv->sblock, 0)); return TRUE; } @@ -110,22 +105,12 @@ static bool cmd_test_fail_operation_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address) { - unsigned int pc; - sieve_offset_t offset; - sieve_code_dumpf(denv, "TEST_FAIL:"); sieve_code_descend(denv); if (!sieve_opr_string_dump(denv, address, "reason")) return FALSE; - sieve_code_mark(denv); - pc = *address; - if (!sieve_binary_read_offset(denv->sblock, address, &offset)) - return FALSE; - sieve_code_dumpf(denv, "offset: %d [%08x]", - offset, pc + offset); - return TRUE; } @@ -147,7 +132,5 @@ cmd_test_fail_operation_execute(const struct sieve_runtime_env *renv, sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "testsuite: " "test_fail command; FAIL current test"); - testsuite_test_fail(reason); - - return sieve_interpreter_program_jump(renv->interp, TRUE, TRUE); + return testsuite_test_fail(renv, reason); } diff --git a/src/testsuite/cmd-test-mailbox.c b/src/testsuite/cmd-test-mailbox.c index 9dcf8c3ae79c04dda335cdd203a9e0a64da26979..3ace33ea983d289428599406d9d52585aea309e6 100644 --- a/src/testsuite/cmd-test-mailbox.c +++ b/src/testsuite/cmd-test-mailbox.c @@ -195,7 +195,8 @@ cmd_test_mailbox_operation_execute(const struct sieve_runtime_env *renv, } /* FIXME: implement */ - testsuite_test_failf("test_mailbox_delete: NOT IMPLEMENTED"); + return testsuite_test_failf( + renv, "test_mailbox_delete: NOT IMPLEMENTED"); } return SIEVE_EXEC_OK; diff --git a/src/testsuite/cmd-test-message.c b/src/testsuite/cmd-test-message.c index f27ee074feb525834b0d2961a5cb744af82729df..dd3c4caff5486d42adf4d1c3096ce0add772f28f 100644 --- a/src/testsuite/cmd-test-message.c +++ b/src/testsuite/cmd-test-message.c @@ -434,8 +434,8 @@ cmd_test_message_smtp_operation_execute(const struct sieve_runtime_env *renv, } if (!result) { - testsuite_test_failf( - "no outgoing SMTP message with index %llu", + return testsuite_test_failf( + renv, "no outgoing SMTP message with index %llu", (unsigned long long)msg_index); } @@ -506,8 +506,8 @@ cmd_test_message_mailbox_operation_execute(const struct sieve_runtime_env *renv, } if (!result) { - testsuite_test_failf( - "no message in folder '%s' with index %llu", + return testsuite_test_failf( + renv, "no message in folder '%s' with index %llu", str_c(folder), (unsigned long long)msg_index); } diff --git a/src/testsuite/cmd-test.c b/src/testsuite/cmd-test.c index 590ad2c27b8d2b3d65df0284dd198ffe1b4813b2..c817aedb0f83f588f2bd4d7006b7f73abab96601 100644 --- a/src/testsuite/cmd-test.c +++ b/src/testsuite/cmd-test.c @@ -121,6 +121,8 @@ cmd_test_generate(const struct sieve_codegen_env *cgenv, /* Prepare jumplist */ sieve_jumplist_reset(genctx->exit_jumps); + sieve_jumplist_add(genctx->exit_jumps, + sieve_binary_emit_offset(cgenv->sblock, 0)); /* Test body */ if (!sieve_generate_block(cgenv, cmd->ast_node)) @@ -142,10 +144,24 @@ static bool cmd_test_operation_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address) { + sieve_size_t tst_begin; + sieve_offset_t tst_end_offset; + sieve_code_dumpf(denv, "TEST:"); sieve_code_descend(denv); - return sieve_opr_string_dump(denv, address, "test name"); + if (!sieve_opr_string_dump(denv, address, "test name")) + return FALSE; + + sieve_code_mark(denv); + tst_begin = *address; + if (!sieve_binary_read_offset(denv->sblock, address, &tst_end_offset)) + return FALSE; + sieve_code_dumpf(denv, "end: %d [%08llx]", + tst_end_offset, + (unsigned long long)tst_begin + tst_end_offset); + + return TRUE; } /* @@ -156,6 +172,8 @@ static int cmd_test_operation_execute(const struct sieve_runtime_env *renv, sieve_size_t *address) { + sieve_size_t tst_begin, tst_end; + sieve_offset_t tst_end_offset; string_t *test_name; int ret; @@ -163,23 +181,28 @@ cmd_test_operation_execute(const struct sieve_runtime_env *renv, if (ret <= 0) return ret; + tst_begin = *address; + if (!sieve_binary_read_offset(renv->sblock, address, &tst_end_offset)) { + sieve_runtime_trace_error(renv, "invalid end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + tst_end = tst_begin + tst_end_offset; + sieve_runtime_trace_sep(renv); sieve_runtime_trace(renv, SIEVE_TRLVL_NONE, - "** Testsuite test start: \"%s\"", - str_c(test_name)); + "** Testsuite test start: \"%s\" (end: %08llx)", + str_c(test_name), + (unsigned long long)tst_end); - testsuite_test_start(test_name); - return SIEVE_EXEC_OK; + return testsuite_test_start(renv, test_name, tst_end); } static int -cmd_test_finish_operation_execute( - const struct sieve_runtime_env *renv ATTR_UNUSED, - sieve_size_t *address ATTR_UNUSED) +cmd_test_finish_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) { sieve_runtime_trace(renv, SIEVE_TRLVL_NONE, "** Testsuite test end"); sieve_runtime_trace_sep(renv); - testsuite_test_succeed(NULL); - return SIEVE_EXEC_OK; + return testsuite_test_succeed(renv, address, NULL); } diff --git a/src/testsuite/testsuite-common.c b/src/testsuite/testsuite-common.c index 33434afca2b28a9e0fea6cf23811061f05b8b87c..050828c620a0747d9587118d6b5b006a7a3590f1 100644 --- a/src/testsuite/testsuite-common.c +++ b/src/testsuite/testsuite-common.c @@ -50,6 +50,7 @@ char *testsuite_test_path = NULL; /* Test context */ static string_t *test_name; +static sieve_size_t test_block_end; static unsigned int test_index; static unsigned int test_failures; @@ -154,35 +155,51 @@ testsuite_interpreter_context_get(struct sieve_interpreter *interp, static void testsuite_test_context_init(void) { test_name = str_new(default_pool, 128); + test_block_end = 0; test_index = 0; test_failures = 0; } -void testsuite_test_start(string_t *name) +int testsuite_test_start(const struct sieve_runtime_env *renv, + string_t *name, sieve_size_t block_end) { + if (test_block_end != 0) { + sieve_runtime_trace_error(renv, "already inside test block"); + return SIEVE_EXEC_BIN_CORRUPT; + } str_truncate(test_name, 0); str_append_str(test_name, name); + test_block_end = block_end; test_index++; + + return SIEVE_EXEC_OK; } -void testsuite_test_fail(string_t *reason) +int testsuite_test_fail(const struct sieve_runtime_env *renv, + string_t *reason) { - testsuite_test_fail_cstr(str_c(reason)); + return testsuite_test_fail_cstr(renv, str_c(reason)); } -void testsuite_test_failf(const char *fmt, ...) +int testsuite_test_failf(const struct sieve_runtime_env *renv, + const char *fmt, ...) { va_list args; - va_start(args, fmt); - - testsuite_test_fail_cstr(t_strdup_vprintf(fmt, args)); + int ret; + va_start(args, fmt); + ret = testsuite_test_fail_cstr(renv, t_strdup_vprintf(fmt, args)); va_end(args); + + return ret; } -void testsuite_test_fail_cstr(const char *reason) +int testsuite_test_fail_cstr(const struct sieve_runtime_env *renv, + const char *reason) { + sieve_size_t end = test_block_end; + if (str_len(test_name) == 0) { if (reason == NULL || *reason == '\0') printf("%2d: Test FAILED\n", test_index); @@ -199,8 +216,11 @@ void testsuite_test_fail_cstr(const char *reason) } str_truncate(test_name, 0); + test_block_end = 0; test_failures++; + + return sieve_interpreter_program_jump_to(renv->interp, end, FALSE); } void testsuite_testcase_fail(const char *reason) @@ -213,8 +233,12 @@ void testsuite_testcase_fail(const char *reason) test_failures++; } -void testsuite_test_succeed(string_t *reason) +int testsuite_test_succeed(const struct sieve_runtime_env *renv, + sieve_size_t *address, string_t *reason) { + sieve_size_t end = test_block_end; + int ret; + if (str_len(test_name) == 0) { if (reason == NULL || str_len(reason) == 0) printf("%2d: Test SUCCEEDED\n", test_index); @@ -231,7 +255,22 @@ void testsuite_test_succeed(string_t *reason) str_c(test_name), str_c(reason)); } } + str_truncate(test_name, 0); + test_block_end = 0; + + if (*address > end) { + sieve_runtime_trace_error( + renv, "invalid test block end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } else if (*address < end) { + ret = sieve_interpreter_program_jump_to( + renv->interp, end, FALSE); + if (ret <= 0) + return ret; + } + + return SIEVE_EXEC_OK; } static void testsuite_test_context_deinit(void) diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h index 644ce5bbcee07c3c62f69ecd365b23ddda600a65..9a40cddd594f291b1d5d7efbf1f51ff703220600 100644 --- a/src/testsuite/testsuite-common.h +++ b/src/testsuite/testsuite-common.h @@ -161,12 +161,17 @@ enum testsuite_operand_code { * Test context */ -void testsuite_test_start(string_t *name); -void testsuite_test_fail(string_t *reason); -void testsuite_test_failf(const char *fmt, ...) ATTR_FORMAT(1, 2); -void testsuite_test_fail_cstr(const char *reason); - -void testsuite_test_succeed(string_t *reason); +int testsuite_test_start(const struct sieve_runtime_env *renv, + string_t *name, sieve_size_t block_end); +int testsuite_test_fail(const struct sieve_runtime_env *renv, + string_t *reason); +int testsuite_test_failf(const struct sieve_runtime_env *renv, + const char *fmt, ...) ATTR_FORMAT(2, 3); +int testsuite_test_fail_cstr(const struct sieve_runtime_env *renv, + const char *reason); + +int testsuite_test_succeed(const struct sieve_runtime_env *renv, + sieve_size_t *address, string_t *reason); void testsuite_testcase_fail(const char *reason); bool testsuite_testcase_result(bool expect_failure); diff --git a/tests/failures/fuzz1.svtest b/tests/failures/fuzz1.svtest new file mode 100644 index 0000000000000000000000000000000000000000..a6fe086c6a6b2f5b0ae0bbd95e01315d912c5a8a --- /dev/null +++ b/tests/failures/fuzz1.svtest @@ -0,0 +1,33 @@ +# Used to cause the test suite to segfault + +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "imap4flags"; +require "mailbox"; + + +test_set "message" text: +Subject: Test message. + +Test message. +. +; + +test "Flag changes between stores" { + fileinto :create "FolderA"; + + if not test_result_execute { + test_fail "failed to execute first result"; + } + + test_message :folder "FolderA" 0; + + test_result_reset; + + test_message :folder "Uninteiesting" 0; + + if not hasflag "$label1" { + test_fail "flags not stored for fired for third message"; + } + +} diff --git a/tests/failures/fuzz2.svtest b/tests/failures/fuzz2.svtest new file mode 100644 index 0000000000000000000000000000000000000000..9fa63eac5df77483d8ac9f53db4d9af33fd65737 --- /dev/null +++ b/tests/failures/fuzz2.svtest @@ -0,0 +1,37 @@ +require "vnd.dovecot.testsuite"; +require "fileinto"; +require "variables"; +require "mailbox"; + +set "message" text: +From:.org +To:rg +Subject: First message + +Frop +. +; + + +test "sometest" { + test_set "message" "${message}"; + + fileinto :create "Folder"; + + if not test_result_execute { + test_fail ""; + } + + test_message :folder "Folder" 0; + + if not header "subject" "First message" { + test_fail ""; + } + + test_message :folder " .Folder" 1; + + if not header "subject" "Second message" { + test_fail ""; + } + +}