/src/pigeonhole/src/testsuite/fuzzsuite.c
Line | Count | Source |
1 | | /* Copyright (c) 2020-2025 Pigeonhole authors, see the included COPYING file |
2 | | */ |
3 | | |
4 | | #include "lib.h" |
5 | | #include "fuzzer.h" |
6 | | #include "lib-signals.h" |
7 | | #include "ioloop.h" |
8 | | #include "env-util.h" |
9 | | #include "istream.h" |
10 | | #include "hostpid.h" |
11 | | #include "settings.h" |
12 | | |
13 | | #include "sieve.h" |
14 | | #include "sieve-extensions.h" |
15 | | #include "sieve-script.h" |
16 | | #include "sieve-storage.h" |
17 | | #include "sieve-binary.h" |
18 | | #include "sieve-result.h" |
19 | | #include "sieve-interpreter.h" |
20 | | |
21 | | #include "sieve-settings.h" |
22 | | |
23 | | #include "sieve-tool.h" |
24 | | |
25 | | #include "testsuite-common.h" |
26 | | #include "testsuite-log.h" |
27 | | #include "testsuite-settings.h" |
28 | | #include "testsuite-result.h" |
29 | | #include "testsuite-message.h" |
30 | | #include "testsuite-script.h" |
31 | | #include "testsuite-smtp.h" |
32 | | #include "testsuite-mailstore.h" |
33 | | |
34 | | #include <stdio.h> |
35 | | #include <unistd.h> |
36 | | #include <fcntl.h> |
37 | | #include <pwd.h> |
38 | | #include <sysexits.h> |
39 | | |
40 | | //#define FUZZSUITE_DEBUG |
41 | | |
42 | | const struct sieve_script_env *testsuite_scriptenv; |
43 | | |
44 | | /* |
45 | | * Configuration |
46 | | */ |
47 | | |
48 | | extern const struct setting_parser_info ext_duplicate_setting_parser_info; |
49 | | extern const struct setting_parser_info ext_editheader_header_setting_parser_info; |
50 | | extern const struct setting_parser_info ext_editheader_setting_parser_info; |
51 | | extern const struct setting_parser_info ext_extlists_list_setting_parser_info; |
52 | | extern const struct setting_parser_info ext_extlists_setting_parser_info; |
53 | | extern const struct setting_parser_info ext_include_setting_parser_info; |
54 | | extern const struct setting_parser_info ext_spamtest_setting_parser_info; |
55 | | extern const struct setting_parser_info ext_virustest_setting_parser_info; |
56 | | extern const struct setting_parser_info ext_subaddress_setting_parser_info; |
57 | | extern const struct setting_parser_info ext_vacation_setting_parser_info; |
58 | | extern const struct setting_parser_info ext_variables_setting_parser_info; |
59 | | extern const struct setting_parser_info ext_vnd_environment_setting_parser_info; |
60 | | extern const struct setting_parser_info ext_report_setting_parser_info; |
61 | | extern const struct setting_parser_info ntfy_mailto_setting_parser_info; |
62 | | extern const struct setting_parser_info sieve_dict_storage_setting_parser_info; |
63 | | extern const struct setting_parser_info sieve_file_storage_setting_parser_info; |
64 | | extern const struct setting_parser_info sieve_setting_parser_info; |
65 | | extern const struct setting_parser_info sieve_storage_setting_parser_info; |
66 | | |
67 | | static const struct setting_parser_info *set_infos[] = { |
68 | | &ext_duplicate_setting_parser_info, |
69 | | &ext_editheader_header_setting_parser_info, |
70 | | &ext_editheader_setting_parser_info, |
71 | | &ext_extlists_list_setting_parser_info, |
72 | | &ext_extlists_setting_parser_info, |
73 | | &ext_include_setting_parser_info, |
74 | | &ext_spamtest_setting_parser_info, |
75 | | &ext_subaddress_setting_parser_info, |
76 | | &ext_vacation_setting_parser_info, |
77 | | &ext_variables_setting_parser_info, |
78 | | &ext_virustest_setting_parser_info, |
79 | | &ext_vnd_environment_setting_parser_info, |
80 | | &ext_report_setting_parser_info, |
81 | | &ntfy_mailto_setting_parser_info, |
82 | | |
83 | | &sieve_file_storage_setting_parser_info, |
84 | | &sieve_dict_storage_setting_parser_info, |
85 | | |
86 | | &sieve_setting_parser_info, |
87 | | &sieve_storage_setting_parser_info, |
88 | | }; |
89 | | unsigned int set_infos_count = N_ELEMENTS(set_infos); |
90 | | |
91 | | /* |
92 | | * Testsuite execution |
93 | | */ |
94 | | |
95 | | static void |
96 | | fuzz_die(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) |
97 | 0 | { |
98 | 0 | printf("COMMAND LINE INTERRUPT\n"); |
99 | |
|
100 | 0 | testsuite_tmp_dir_deinit(); |
101 | | |
102 | | /* No delays, no fuzzer reports: be gone already */ |
103 | 0 | _exit(0); |
104 | 0 | } |
105 | | |
106 | | FUZZ_BEGIN_DATA(const unsigned char *data, size_t size) |
107 | | { |
108 | | static bool first_run = TRUE; |
109 | | struct sieve_instance *svinst; |
110 | | struct sieve_binary *sbin; |
111 | | const char *sieve_dir = ".", *error; |
112 | | unsigned int i; |
113 | | bool log_stdout = FALSE; |
114 | | int ret; |
115 | | |
116 | | if (first_run) { |
117 | | #ifndef FUZZSUITE_DEBUG |
118 | | testsuite_silent = (getenv("DEBUG") == NULL); |
119 | | #endif |
120 | | first_run = FALSE; |
121 | | } |
122 | | |
123 | | sieve_tool = sieve_tool_init_fuzzer("fuzzsuite"); |
124 | | |
125 | | /* Allow quick command line termination when developing. This is not |
126 | | expected in a normal fuzzer run. */ |
127 | | lib_signals_set_handler(SIGINT, 0, fuzz_die, NULL); |
128 | | |
129 | | #ifdef FUZZSUITE_DEBUG |
130 | | struct sieve_trace_config trace_config; |
131 | | |
132 | | i_zero(&trace_config); |
133 | | trace_config.level = SIEVE_TRLVL_MATCHING; |
134 | | |
135 | | log_stdout = TRUE; |
136 | | #endif |
137 | | |
138 | | // FIXME: very very ugly |
139 | | master_service_parse_option( |
140 | | master_service, 'o', |
141 | | "postmaster_address=postmaster@example.com"); |
142 | | master_service_parse_option(master_service, 'o', "mail_uid="); |
143 | | master_service_parse_option(master_service, 'o', "mail_gid="); |
144 | | master_service_parse_option(master_service, 'o', "default_internal_group="); |
145 | | |
146 | | /* Register settings manually */ |
147 | | for (i = 0; i < set_infos_count; i++) |
148 | | settings_info_register(set_infos[i]); |
149 | | |
150 | | /* Finish testsuite initialization */ |
151 | | svinst = sieve_tool_init_finish(sieve_tool, FALSE, FALSE); |
152 | | #ifdef FUZZSUITE_DEBUG |
153 | | event_set_forced_debug(sieve_get_event(svinst), TRUE); |
154 | | #endif |
155 | | testsuite_init(svinst, sieve_dir, sieve_tool_get_homedir(sieve_tool), |
156 | | log_stdout); |
157 | | |
158 | | if (!testsuite_silent) |
159 | | printf("Fuzz case:\n\n"); |
160 | | |
161 | | struct settings_instance *set_instance = |
162 | | settings_instance_find(svinst->event); |
163 | | |
164 | | /* Configure personal storage */ |
165 | | settings_override(set_instance, "sieve_script+", "included", |
166 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
167 | | settings_override(set_instance, |
168 | | "sieve_script/included/sieve_script_storage", |
169 | | "included", |
170 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
171 | | settings_override(set_instance, |
172 | | "sieve_script/included/sieve_script_type", |
173 | | SIEVE_STORAGE_TYPE_PERSONAL, |
174 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
175 | | settings_override(set_instance, |
176 | | "sieve_script/included/sieve_script_driver", |
177 | | "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
178 | | settings_override(set_instance, |
179 | | "sieve_script/included/sieve_script_path", |
180 | | t_strdup_printf("%s/included", sieve_dir), |
181 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
182 | | |
183 | | /* Configure global storage */ |
184 | | settings_override(set_instance, "sieve_script+", "included-global", |
185 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
186 | | settings_override(set_instance, |
187 | | "sieve_script/included-global/sieve_script_storage", |
188 | | "included-global", |
189 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
190 | | settings_override(set_instance, |
191 | | "sieve_script/included-global/sieve_script_type", |
192 | | SIEVE_STORAGE_TYPE_GLOBAL, |
193 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
194 | | settings_override(set_instance, |
195 | | "sieve_script/included-global/sieve_script_driver", |
196 | | "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
197 | | settings_override(set_instance, |
198 | | "sieve_script/included-global/sieve_script_path", |
199 | | t_strdup_printf("%s/included-global", sieve_dir), |
200 | | SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM); |
201 | | |
202 | | /* Construct Sieve script */ |
203 | | struct sieve_script *script; |
204 | | struct istream *input; |
205 | | |
206 | | input = i_stream_create_from_data(data, size); |
207 | | script = sieve_data_script_create_from_input( |
208 | | svinst, SIEVE_SCRIPT_CAUSE_ANY, "fuzzsuite-main", input); |
209 | | |
210 | | /* Compile Sieve script */ |
211 | | if (sieve_compile_script(script, testsuite_log_main_ehandler, 0, |
212 | | &sbin, NULL) < 0) { |
213 | | testsuite_testcase_fail("failed to compile testcase script"); |
214 | | } else { |
215 | | #ifdef FUZZSUITE_DEBUG |
216 | | struct sieve_trace_log *trace_log = NULL; |
217 | | |
218 | | sieve_tool_dump_binary_to(sbin, "-", FALSE); |
219 | | (void)sieve_trace_log_create(svinst, NULL, &trace_log); |
220 | | #endif |
221 | | |
222 | | struct sieve_exec_status exec_status; |
223 | | struct sieve_script_env scriptenv; |
224 | | |
225 | | testsuite_mailstore_init(); |
226 | | testsuite_message_init(); |
227 | | |
228 | | if (sieve_script_env_init(&scriptenv, |
229 | | testsuite_mailstore_get_user(), |
230 | | &error) < 0) { |
231 | | i_fatal("Failed to initialize script execution: %s", |
232 | | error); |
233 | | } |
234 | | |
235 | | i_zero(&exec_status); |
236 | | |
237 | | scriptenv.default_mailbox = "INBOX"; |
238 | | scriptenv.smtp_start = testsuite_smtp_start; |
239 | | scriptenv.smtp_add_rcpt = testsuite_smtp_add_rcpt; |
240 | | scriptenv.smtp_send = testsuite_smtp_send; |
241 | | scriptenv.smtp_abort = testsuite_smtp_abort; |
242 | | scriptenv.smtp_finish = testsuite_smtp_finish; |
243 | | #ifdef FUZZSUITE_DEBUG |
244 | | scriptenv.trace_log = trace_log; |
245 | | scriptenv.trace_config = trace_config; |
246 | | #endif |
247 | | scriptenv.exec_status = &exec_status; |
248 | | |
249 | | testsuite_scriptenv = &scriptenv; |
250 | | |
251 | | testsuite_result_init(); |
252 | | |
253 | | /* Fuzz the test */ |
254 | | ret = testsuite_run(sbin, testsuite_log_main_ehandler); |
255 | | |
256 | | switch (ret) { |
257 | | case SIEVE_EXEC_OK: |
258 | | break; |
259 | | case SIEVE_EXEC_FAILURE: |
260 | | case SIEVE_EXEC_KEEP_FAILED: |
261 | | case SIEVE_EXEC_TEMP_FAILURE: |
262 | | testsuite_testcase_fail( |
263 | | "test script execution aborted due to error"); |
264 | | break; |
265 | | case SIEVE_EXEC_BIN_CORRUPT: |
266 | | i_panic("BUG: compiled test script binary is corrupt"); |
267 | | break; |
268 | | case SIEVE_EXEC_RESOURCE_LIMIT: |
269 | | i_panic("BUG: resource limit exceeded"); |
270 | | break; |
271 | | } |
272 | | |
273 | | sieve_close(&sbin); |
274 | | |
275 | | /* De-initialize message environment */ |
276 | | testsuite_result_deinit(); |
277 | | testsuite_message_deinit(); |
278 | | testsuite_mailstore_deinit(); |
279 | | |
280 | | #ifdef FUZZSUITE_DEBUG |
281 | | if (trace_log != NULL) |
282 | | sieve_trace_log_free(&trace_log); |
283 | | #endif |
284 | | |
285 | | testsuite_scriptenv = NULL; |
286 | | } |
287 | | |
288 | | sieve_script_unref(&script); |
289 | | i_stream_destroy(&input); |
290 | | |
291 | | /* De-initialize testsuite */ |
292 | | testsuite_deinit(); |
293 | | |
294 | | sieve_tool_deinit(&sieve_tool); |
295 | | |
296 | | testsuite_testcase_result(FALSE); |
297 | | } |
298 | | FUZZ_END |