diff --git a/fuzz/execl_override.c b/fuzz/execl_override.c index 281282f..8ce707b 100644 --- a/fuzz/execl_override.c +++ b/fuzz/execl_override.c @@ -1,11 +1,29 @@ /* This file is used by the fuzzer in order to prevent executing shell commands. */ #define _GNU_SOURCE -#include - -struct tm *(*orig_localtime)(const time_t *timep); +#include +#include "../output.h" +#include +#include +#include int -execl(const char *pathname, const char *arg, ...) { - return 0; +fork() { + return 1; +} + +void wlr_texture_get_size(struct wlr_texture *texture, int *width, + int *height) { + if(width != NULL) { + *width=0; + } + + if(height != NULL) { + *height=0; + } +} + +cairo_surface_t* +cairo_image_surface_create(cairo_format_t fmt, int width, int height) { + return NULL; } diff --git a/fuzz/fuzz-parse.c b/fuzz/fuzz-parse.c index 257f289..746e459 100644 --- a/fuzz/fuzz-parse.c +++ b/fuzz/fuzz-parse.c @@ -1,75 +1,410 @@ -#define _POSIX_C_SOURCE 200812L -#define FUZZING +/* + * Cagebreak: A Wayland tiling compositor. + * + * Copyright (C) 2018-2020 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ -#include "../keybinding.h" -#include "../parse.h" -#include "../seat.h" -#include "../server.h" -#include +#define _POSIX_C_SOURCE 200812L + +#include "config.h" + +#include +#include #include +#include #include #include #include #include + +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if CG_HAS_XWAYLAND +#include +#endif +#include +#include +#include +#include +#if CG_HAS_XWAYLAND +#include +#endif + +#include "idle_inhibit_v1.h" +#include "keybinding.h" +#include "message.h" +#include "output.h" +#include "workspace.h" +#include "parse.h" +#include "seat.h" +#include "server.h" +#include "view.h" +#include "xdg_shell.h" +#if CG_HAS_XWAYLAND +#include "xwayland.h" +#endif #ifndef WAIT_ANY #define WAIT_ANY -1 #endif -void -set_sig_handler(int sig) { - struct sigaction sa; - sa.sa_handler = SIG_IGN; // handle signal by ignoring - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if(sigaction(SIGCHLD, &sa, 0) == -1) { - perror(0); - exit(1); +static bool +drop_permissions(void) { + if(getuid() != geteuid() || getgid() != getegid()) { + if(setuid(getuid()) != 0 || setgid(getgid()) != 0) { + wlr_log(WLR_ERROR, "Unable to drop root, refusing to start"); + return false; + } } + + if(setuid(0) != -1) { + wlr_log(WLR_ERROR, "Unable to drop root (we shouldn't be able to " + "restore it after setuid), refusing to start"); + return false; + } + + return true; } -struct cg_server server; +static bool +parse_args(struct cg_server *server, int argc, char *argv[]) { + server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL; + server->debug_damage_tracking = false; + return true; +} + +struct cg_server server = {0}; +struct wlr_xwayland *xwayland = NULL; +#if CG_HAS_XWAYLAND +struct wlr_xcursor_manager *xcursor_manager = NULL; +#endif + +void +cleanup() { + server.running = false; +#if CG_HAS_XWAYLAND + if(xwayland != NULL) { + wlr_xwayland_destroy(xwayland); + } + if(xcursor_manager != NULL) { + wlr_xcursor_manager_destroy(xcursor_manager); + } +#endif + wl_display_destroy_clients(server.wl_display); + + for(unsigned int i = 0; server.modes[i] != NULL; ++i) { + free(server.modes[i]); + } + free(server.modes); + + keybinding_list_free(server.keybindings); + + seat_destroy(server.seat); + /* This function is not null-safe, but we only ever get here + with a proper wl_display. */ + wl_display_destroy(server.wl_display); + wlr_output_layout_destroy(server.output_layout); +} int LLVMFuzzerInitialize(int *argc, char ***argv) { - set_sig_handler(SIGCHLD); + struct wl_event_loop *event_loop = NULL; + struct wlr_backend *backend = NULL; + struct wlr_renderer *renderer = NULL; + struct wlr_compositor *compositor = NULL; + struct wlr_data_device_manager *data_device_manager = NULL; + struct wlr_server_decoration_manager *server_decoration_manager = NULL; + struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL; + struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL; + struct wlr_screencopy_manager_v1 *screencopy_manager = NULL; + struct wlr_xdg_output_manager_v1 *output_manager = NULL; + struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL; + struct wlr_xdg_shell *xdg_shell = NULL; + int ret = 0; - server.wl_display = NULL; - server.event_loop = NULL; + if(!parse_args(&server, *argc, *argv)) { + return 1; + } - server.seat = malloc(sizeof(struct cg_seat)); - server.seat->mode = 0; - server.seat->default_mode = 0; +#ifdef DEBUG + wlr_log_init(WLR_DEBUG, NULL); +#else + wlr_log_init(WLR_ERROR, NULL); +#endif - server.idle = NULL; - server.idle_inhibit_v1 = NULL; - /* new_idle_inhibitor_v1 */ - wl_list_init(&server.inhibitors); + /* Wayland requires XDG_RUNTIME_DIR to be set. */ + if(!getenv("XDG_RUNTIME_DIR")) { + wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment"); + return 1; + } - server.output_layout = NULL; - wl_list_init(&server.outputs); - server.curr_output = NULL; - - /* new_output */ - - /* xdg_toplevel_decoration */ - /* new_xdg_shell_surface */ - /* new_xwayland_surface */ - server.keybindings = keybinding_list_init(); - server.output_transform = 0; + server.wl_display = wl_display_create(); + if(!server.wl_display) { + wlr_log(WLR_ERROR, "Cannot allocate a Wayland display"); + return 1; + } server.running = true; - server.modes = malloc(sizeof(char *)); - server.modes[0] = NULL; + + server.modes = malloc(4 * sizeof(char *)); + server.modes[0] = strdup("top"); + server.modes[1] = strdup("root"); + server.modes[2] = strdup("resize"); + server.modes[3] = NULL; + server.nws = 1; - server.message_timeout = 0; + server.message_timeout = 2; + + event_loop = wl_display_get_event_loop(server.wl_display); + server.event_loop = event_loop; + + backend = wlr_backend_autocreate(server.wl_display, NULL); + if(!backend) { + wlr_log(WLR_ERROR, "Unable to create the wlroots backend"); + ret = 1; + goto end; + } + server.backend = backend; + + if(!drop_permissions()) { + ret = 1; + goto end; + } + + server.keybindings = keybinding_list_init(); + if(server.keybindings == NULL || server.keybindings->keybindings == NULL) { + wlr_log(WLR_ERROR, "Unable to allocate keybindings"); + ret = 1; + goto end; + } + + renderer = wlr_backend_get_renderer(backend); + wlr_renderer_init_wl_display(renderer, server.wl_display); + server.bg_color = malloc(4 * sizeof(float)); server.bg_color[0] = 0; server.bg_color[1] = 0; server.bg_color[2] = 0; server.bg_color[3] = 1; + wl_list_init(&server.outputs); + server.output_layout = wlr_output_layout_create(); + if(!server.output_layout) { + wlr_log(WLR_ERROR, "Unable to create output layout"); + ret = 1; + goto end; + } + + compositor = wlr_compositor_create(server.wl_display, renderer); + if(!compositor) { + wlr_log(WLR_ERROR, "Unable to create the wlroots compositor"); + ret = 1; + goto end; + } + + data_device_manager = wlr_data_device_manager_create(server.wl_display); + if(!data_device_manager) { + wlr_log(WLR_ERROR, "Unable to create the data device manager"); + ret = 1; + goto end; + } + + /* Configure a listener to be notified when new outputs are + * available on the backend. We use this only to detect the + * first output and ignore subsequent outputs. */ + server.new_output.notify = handle_new_output; + wl_signal_add(&backend->events.new_output, &server.new_output); + + server.seat = seat_create(&server, backend); + if(!server.seat) { + wlr_log(WLR_ERROR, "Unable to create the seat"); + ret = 1; + goto end; + } + + server.idle = wlr_idle_create(server.wl_display); + if(!server.idle) { + wlr_log(WLR_ERROR, "Unable to create the idle tracker"); + ret = 1; + goto end; + } + + server.idle_inhibit_v1 = wlr_idle_inhibit_v1_create(server.wl_display); + if(!server.idle_inhibit_v1) { + wlr_log(WLR_ERROR, "Cannot create the idle inhibitor"); + ret = 1; + goto end; + } + server.new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1_new; + wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor, + &server.new_idle_inhibitor_v1); + wl_list_init(&server.inhibitors); + + xdg_shell = wlr_xdg_shell_create(server.wl_display); + if(!xdg_shell) { + wlr_log(WLR_ERROR, "Unable to create the XDG shell interface"); + ret = 1; + goto end; + } + server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new; + wl_signal_add(&xdg_shell->events.new_surface, + &server.new_xdg_shell_surface); + + xdg_decoration_manager = + wlr_xdg_decoration_manager_v1_create(server.wl_display); + if(!xdg_decoration_manager) { + wlr_log(WLR_ERROR, "Unable to create the XDG decoration manager"); + ret = 1; + goto end; + } + wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration, + &server.xdg_toplevel_decoration); + server.xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration; + + server_decoration_manager = + wlr_server_decoration_manager_create(server.wl_display); + if(!server_decoration_manager) { + wlr_log(WLR_ERROR, "Unable to create the server decoration manager"); + ret = 1; + goto end; + } + wlr_server_decoration_manager_set_default_mode( + server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + + export_dmabuf_manager = + wlr_export_dmabuf_manager_v1_create(server.wl_display); + if(!export_dmabuf_manager) { + wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager"); + ret = 1; + goto end; + } + + screencopy_manager = wlr_screencopy_manager_v1_create(server.wl_display); + if(!screencopy_manager) { + wlr_log(WLR_ERROR, "Unable to create the screencopy manager"); + ret = 1; + goto end; + } + + output_manager = wlr_xdg_output_manager_v1_create(server.wl_display, + server.output_layout); + if(!output_manager) { + wlr_log(WLR_ERROR, "Unable to create the output manager"); + ret = 1; + goto end; + } + + gamma_control_manager = + wlr_gamma_control_manager_v1_create(server.wl_display); + if(!gamma_control_manager) { + wlr_log(WLR_ERROR, "Unable to create the gamma control manager"); + ret = 1; + goto end; + } + +#if CG_HAS_XWAYLAND + xwayland = wlr_xwayland_create(server.wl_display, compositor, true); + if(!xwayland) { + wlr_log(WLR_ERROR, "Cannot create XWayland server"); + ret = 1; + goto end; + } + server.new_xwayland_surface.notify = handle_xwayland_surface_new; + wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface); + + xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE); + if(!xcursor_manager) { + wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager"); + ret = 1; + goto end; + } + + if(setenv("DISPLAY", xwayland->display_name, true) < 0) { + wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.", + "Clients may not be able to connect"); + } else { + wlr_log(WLR_DEBUG, "XWayland is running on display %s", + xwayland->display_name); + } + + if(wlr_xcursor_manager_load(xcursor_manager, 1)) { + wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme"); + } + struct wlr_xcursor *xcursor = + wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1); + if(xcursor) { + struct wlr_xcursor_image *image = xcursor->images[0]; + wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4, + image->width, image->height, image->hotspot_x, + image->hotspot_y); + } +#endif + + const char *socket = wl_display_add_socket_auto(server.wl_display); + if(!socket) { + wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket"); + ret = 1; + goto end; + } + + if(!wlr_backend_start(backend)) { + wlr_log(WLR_ERROR, "Unable to start the wlroots backend"); + ret = 1; + goto end; + } + + if(setenv("WAYLAND_DISPLAY", socket, true) < 0) { + wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.", + "Clients may not be able to connect"); + } else { + wlr_log(WLR_DEBUG, + "Cagebreak " CG_VERSION " is running on Wayland display %s", + socket); + } + +#if CG_HAS_XWAYLAND + wlr_xwayland_set_seat(xwayland, server.seat->seat); +#endif + + /* Place the cursor to the topl left of the output layout. */ + wlr_cursor_warp(server.seat->cursor, NULL, 0, 0); + atexit(cleanup); + return 0; +end: + cleanup(); + return ret; +} + +/* Parse config file. Lines longer than "max_line_size" are ignored */ +int +set_configuration(struct cg_server *server, char *content) { + char *line; + for(unsigned int line_num = 1; + (line = strtok_r(NULL, "\n", &content)) != NULL; ++line_num) { + line[strcspn(line, "\n")] = '\0'; + if(*line != '\0' && *line != '#') { + if(parse_rc_line(server, line) != 0) { + return -1; + } + } + } return 0; } @@ -78,9 +413,33 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { char *str = malloc(size * sizeof(char) + 1); strncpy(str, (char *)data, size); str[size] = 0; - parse_rc_line(&server, str); + set_configuration(&server, str); free(str); keybinding_list_free(server.keybindings); server.keybindings = keybinding_list_init(); + run_action(KEYBINDING_WORKSPACES, &server, + (union keybinding_params){.i = 1}); + run_action(KEYBINDING_LAYOUT_FULLSCREEN, &server, + (union keybinding_params){.c = NULL}); + wl_display_flush_clients(server.wl_display); + wl_display_destroy_clients(server.wl_display); + struct cg_output* output; + wl_list_for_each(output, &server.outputs, link) { + message_clear(output); + struct cg_view *view; + wl_list_for_each(view, &(*output->workspaces)->views, link) { + view_unmap(view); + view_destroy(view); + } + wl_list_for_each(view, &(*output->workspaces)->unmanaged_views, link) { + view_unmap(view); + view_destroy(view); + } + } + for(unsigned int i = 3; server.modes[i] != NULL; ++i) { + free(server.modes[i]); + } + server.modes[3] = NULL; + server.modes = realloc(server.modes, 4 * sizeof(char *)); return 0; } diff --git a/fuzz/meson.build b/fuzz/meson.build index 0266717..8ff982a 100644 --- a/fuzz/meson.build +++ b/fuzz/meson.build @@ -17,6 +17,7 @@ endif override_lib = shared_library('execl_override', [ 'execl_override.c' ], + dependencies: [ pixman,cairo,pango,pangocairo ], install: false ) diff --git a/message.c b/message.c index c41e1a4..40ac033 100644 --- a/message.c +++ b/message.c @@ -45,13 +45,13 @@ create_message_texture(const char *string, const struct cg_output *output) { // Therefore, we cannot use cairo_create(NULL). cairo_surface_t *dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); - cairo_t *c = cairo_create(dummy_surface); - // This occurs when we are fuzzing. In that case, do nothing - if(c == NULL) { + if(dummy_surface == NULL) { return NULL; } + cairo_t *c = cairo_create(dummy_surface); + cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); cairo_font_options_t *fo = cairo_font_options_create(); cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);