|
| 1 | +/* Copyright 2025 Google LLC |
| 2 | + * |
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | + * you may not use this file except in compliance with the License. |
| 5 | + * You may obtain a copy of the License at |
| 6 | + * |
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | + * |
| 9 | + * Unless required by applicable law or agreed to in writing, software |
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | + * See the License for the specific language governing permissions and |
| 13 | + * limitations under the License. |
| 14 | + */ |
| 15 | + |
| 16 | +#include "rsync.h" |
| 17 | +#include <setjmp.h> |
| 18 | + |
| 19 | +static jmp_buf fuzz_exit_jmp; |
| 20 | + |
| 21 | +/* These are defined in main.c which we don't link against */ |
| 22 | +int am_receiver = 0; |
| 23 | +int am_generator = 0; |
| 24 | +int local_server = 0; |
| 25 | +mode_t orig_umask = 022; |
| 26 | +int batch_gen_fd = -1; |
| 27 | +int sender_keeps_checksum = 0; |
| 28 | +char *raw_argv[1] = {NULL}; |
| 29 | +int raw_argc = 0; |
| 30 | +int cooked_argc = 0; |
| 31 | +char **cooked_argv = NULL; |
| 32 | +uid_t our_uid = 0; |
| 33 | +gid_t our_gid = 0; |
| 34 | +int daemon_connection = 0; |
| 35 | + |
| 36 | +/* Stubs for functions defined in main.c */ |
| 37 | +void remember_children(UNUSED(int val)) {} |
| 38 | +void start_server(UNUSED(int f_in), UNUSED(int f_out), |
| 39 | + UNUSED(int argc), UNUSED(char *argv[])) {} |
| 40 | +int client_run(UNUSED(int f_in), UNUSED(int f_out), |
| 41 | + UNUSED(pid_t pid), UNUSED(int argc), UNUSED(char *argv[])) { return 0; } |
| 42 | +pid_t wait_process(UNUSED(pid_t pid), UNUSED(int *status_ptr), |
| 43 | + UNUSED(int flags)) { return -1; } |
| 44 | +int shell_exec(UNUSED(const char *cmd)) { return -1; } |
| 45 | +void read_del_stats(UNUSED(int f)) {} |
| 46 | +void write_del_stats(UNUSED(int f)) {} |
| 47 | + |
| 48 | +/* Stubs for cleanup.c which we exclude to avoid exit() calls */ |
| 49 | +pid_t cleanup_child_pid = -1; |
| 50 | +int cleanup_got_literal = 0; |
| 51 | +int called_from_signal_handler = 0; |
| 52 | +BOOL flush_ok_after_signal = False; |
| 53 | +NORETURN void _exit_cleanup(UNUSED(int code), UNUSED(const char *file), UNUSED(int line)) { |
| 54 | + longjmp(fuzz_exit_jmp, 1); |
| 55 | +} |
| 56 | +void cleanup_disable(void) {} |
| 57 | +void cleanup_set(UNUSED(const char *fnametmp), UNUSED(const char *fname), |
| 58 | + UNUSED(struct file_struct *file), UNUSED(int fd_r), UNUSED(int fd_w)) {} |
| 59 | +void cleanup_set_pid(UNUSED(pid_t pid)) {} |
| 60 | +void close_all(void) {} |
| 61 | + |
| 62 | +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
| 63 | + if (size < 1 || size > 8192) |
| 64 | + return 0; |
| 65 | + |
| 66 | + /* Null-terminate the input */ |
| 67 | + char *input = (char *)malloc(size + 1); |
| 68 | + if (!input) |
| 69 | + return 0; |
| 70 | + |
| 71 | + memcpy(input, data, size); |
| 72 | + input[size] = '\0'; |
| 73 | + |
| 74 | + /* Use setjmp to catch rsync's exit_cleanup() calls */ |
| 75 | + if (setjmp(fuzz_exit_jmp) != 0) { |
| 76 | + free(input); |
| 77 | + return 0; |
| 78 | + } |
| 79 | + |
| 80 | + /* Initialize a fresh filter list */ |
| 81 | + filter_rule_list flist = { .head = NULL, .tail = NULL, .debug_type = "" }; |
| 82 | + |
| 83 | + /* Parse the input as a filter rule string */ |
| 84 | + parse_filter_str(&flist, input, rule_template(0), 0); |
| 85 | + |
| 86 | + /* Also try with OLD_PREFIXES flag for compatibility parsing */ |
| 87 | + filter_rule_list flist2 = { .head = NULL, .tail = NULL, .debug_type = "" }; |
| 88 | + parse_filter_str(&flist2, input, rule_template(FILTRULE_INCLUDE), XFLG_OLD_PREFIXES); |
| 89 | + |
| 90 | + /* Try with WORD_SPLIT (like daemon config parsing) */ |
| 91 | + filter_rule_list flist3 = { .head = NULL, .tail = NULL, .debug_type = "" }; |
| 92 | + parse_filter_str(&flist3, input, |
| 93 | + rule_template(FILTRULE_WORD_SPLIT), 0); |
| 94 | + |
| 95 | + /* Exercise check_filter if we got any rules */ |
| 96 | + if (flist.head) { |
| 97 | + check_filter(&flist, FINFO, "test/path/file.c", 0); |
| 98 | + check_filter(&flist, FINFO, "test/path/dir", NAME_IS_DIR); |
| 99 | + check_filter(&flist, FINFO, ".hidden", 0); |
| 100 | + } |
| 101 | + |
| 102 | + free(input); |
| 103 | + |
| 104 | + return 0; |
| 105 | +} |
0 commit comments