From be75a67b1fe182fab9868d15ed618557555f6a0e Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Tue, 22 Sep 2026 16:45:24 +0100 Subject: [PATCH] OSS-Fuzz: Add new fuzzer targets layout processing Signed-off-by: Arthur Chan --- Makefile.am | 5 +- fuzz/layout-fuzzer.c | 111 +++++++++++++++++++++++++++++++++++++ fuzz/layout-fuzzer.dict | 17 ++++++ fuzz/layout-fuzzer.options | 2 + 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 fuzz/layout-fuzzer.c create mode 100644 fuzz/layout-fuzzer.dict create mode 100644 fuzz/layout-fuzzer.options diff --git a/Makefile.am b/Makefile.am index 6d92f0cd4..2341afb7c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -257,7 +257,8 @@ check_PROGRAMS = \ fuzz/input-fuzzer \ fuzz/cmd-parse-fuzzer \ fuzz/format-fuzzer \ - fuzz/style-fuzzer + fuzz/style-fuzzer \ + fuzz/layout-fuzzer fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS) fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS) @@ -266,6 +267,8 @@ fuzz_format_fuzzer_LDFLAGS = $(FUZZING_LIBS) fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS) fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) +fuzz_layout_fuzzer_LDFLAGS = $(FUZZING_LIBS) +fuzz_layout_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) endif # Install tmux.1 in the right format. diff --git a/fuzz/layout-fuzzer.c b/fuzz/layout-fuzzer.c new file mode 100644 index 000000000..b4d577615 --- /dev/null +++ b/fuzz/layout-fuzzer.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2026 Arthur Chan + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Fuzz the custom layout parser. + * + * A layout string such as "bb62,80x24,0,0{40x24,0,0,1,39x24,41,0,2}" is + * accepted by select-layout and is what tmux stores and restores for a + * window, so it is parsed from configuration and from commands. It drives + * layout-custom.c (the string parser and the checksum), then layout.c, which + * resizes and assigns the cells. + * + * layout_parse() refuses a window with no panes, and refuses a layout whose + * cell count is smaller than the pane count, so the window is given a fixed + * number of panes. The count is fixed rather than derived from the input, so + * a mutation always means a different layout string. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +#define FUZZER_MAXLEN 1024 +#define FUZZER_PANES 4 + +struct event_base *libevent; + +int +LLVMFuzzerTestOneInput(const u_char *data, size_t size) +{ + struct window *w; + struct window_pane *wp; + char *buf, *cause = NULL, *dump; + u_int i; + + if (size == 0 || size > FUZZER_MAXLEN) + return 0; + + /* layout_parse() takes a C string. */ + buf = malloc(size + 1); + if (buf == NULL) + return 0; + memcpy(buf, data, size); + buf[size] = '\0'; + + w = window_create(80, 24, 0, 0); + if (w == NULL) { + free(buf); + return 0; + } + window_add_ref(w, __func__); + + for (i = 0; i < FUZZER_PANES; i++) { + wp = window_add_pane(w, NULL, 0, 0); + if (w->active == NULL) + w->active = wp; + } + + if (layout_parse(w, buf, &cause) == 0) { + dump = layout_dump(w, w->layout_root, 1); + free(dump); + } + free(cause); + + window_remove_ref(w, __func__); + + free(buf); + return 0; +} + +int +LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv) +{ + const struct options_table_entry *oe; + + global_environ = environ_create(); + global_options = options_create(NULL); + global_s_options = options_create(NULL); + global_w_options = options_create(NULL); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope & OPTIONS_TABLE_SERVER) + options_default(global_options, oe); + if (oe->scope & OPTIONS_TABLE_SESSION) + options_default(global_s_options, oe); + if (oe->scope & OPTIONS_TABLE_WINDOW) + options_default(global_w_options, oe); + } + + libevent = osdep_event_init(); + + socket_path = xstrdup("dummy"); + + return 0; +} diff --git a/fuzz/layout-fuzzer.dict b/fuzz/layout-fuzzer.dict new file mode 100644 index 000000000..34953764a --- /dev/null +++ b/fuzz/layout-fuzzer.dict @@ -0,0 +1,17 @@ +# tmux custom layout strings: ",x,,{...}" / "[...]" +"," +"x" +"{" +"}" +"[" +"]" +"0" +"1" +"80x24,0,0" +"40x24,0,0" +"bb62," +"cafe," +",0,0,0" +",0,0{" +",0,0[" +"@" diff --git a/fuzz/layout-fuzzer.options b/fuzz/layout-fuzzer.options new file mode 100644 index 000000000..794888809 --- /dev/null +++ b/fuzz/layout-fuzzer.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 1024