// Copyright 2020 - 2025, project-repo and the NEDM contributors // SPDX-License-Identifier: MIT #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 #include #include #include #if NEDM_HAS_XWAYLAND #include #endif #include #include #include #include #if NEDM_HAS_XWAYLAND #include #endif #include "../idle_inhibit_v1.h" #include "../input_manager.h" #include "../keybinding.h" #include "../output.h" #include "../parse.h" #include "../seat.h" #include "../server.h" #include "../xdg_shell.h" #if NEDM_HAS_XWAYLAND #include "../xwayland.h" #endif #include "fuzz-lib.h" struct nedm_server server; struct wlr_xdg_shell *xdg_shell; struct wlr_xwayland *xwayland; #if NEDM_HAS_XWAYLAND struct wlr_xcursor_manager *xcursor_manager; #endif 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; } void cleanup(void) { server.running = false; #if NEDM_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. */ wlr_output_layout_destroy(server.output_layout); } int LLVMFuzzerInitialize(int *argc, char ***argv) { struct wlr_backend *backend = 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_data_control_manager_v1 *data_control_manager = NULL; struct wlr_viewporter *viewporter = 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; #if NEDM_HAS_XWAYLAND struct wlr_xwayland *xwayland = NULL; #endif wl_list_init(&server.input_config); wl_list_init(&server.output_config); wl_list_init(&server.output_priorities); wl_list_init(&server.outputs); wl_list_init(&server.disabled_outputs); int ret = 0; #ifdef DEBUG wlr_log_init(WLR_DEBUG, NULL); #else wlr_log_init(WLR_ERROR, NULL); #endif server.modes = malloc(4 * sizeof(char *)); server.modecursors = malloc(4 * sizeof(char *)); if(!server.modes || !server.modecursors) { wlr_log(WLR_ERROR, "Error allocating mode array"); goto end; } /* Wayland requires XDG_RUNTIME_DIR to be set. */ if(!getenv("XDG_RUNTIME_DIR")) { wlr_log(WLR_INFO, "XDG_RUNTIME_DIR is not set in the environment"); } server.wl_display = wl_display_create(); if(!server.wl_display) { wlr_log(WLR_ERROR, "Cannot allocate a Wayland display"); free(server.modes); server.modes = NULL; free(server.modecursors); server.modecursors = NULL; goto end; } server.xcursor_size = XCURSOR_SIZE; const char *env_cursor_size = getenv("XCURSOR_SIZE"); if(env_cursor_size && strlen(env_cursor_size) > 0) { errno = 0; char *end; unsigned size = strtoul(env_cursor_size, &end, 10); if(!*end && errno == 0) { server.xcursor_size = size; } } server.running = true; server.modes[0] = strdup("top"); server.modes[1] = strdup("root"); server.modes[2] = strdup("resize"); server.modes[3] = NULL; server.modecursors[0] = NULL; server.modecursors[1] = strdup("cell"); server.modecursors[2] = NULL; server.modecursors[3] = NULL; if(server.modes[0] == NULL || server.modes[1] == NULL || server.modes[2] == NULL || server.modecursors[1] == NULL) { wlr_log(WLR_ERROR, "Error allocating default modes"); goto end; } server.nws = 1; server.views_curr_id = 1; server.tiles_curr_id = 1; server.message_config.fg_color[0] = 0.0; server.message_config.fg_color[1] = 0.0; server.message_config.fg_color[2] = 0.0; server.message_config.fg_color[3] = 1.0; server.message_config.bg_color[0] = 0.9; server.message_config.bg_color[1] = 0.85; server.message_config.bg_color[2] = 0.85; server.message_config.bg_color[3] = 1.0; server.message_config.display_time = 2; server.message_config.font = strdup("pango:Monospace 10"); server.event_loop = wl_display_get_event_loop(server.wl_display); backend = wlr_multi_backend_create(server.event_loop); if(!backend) { wlr_log(WLR_ERROR, "Unable to create the wlroots multi backend"); ret = 1; goto end; } server.backend = backend; struct wlr_backend *headless_backend = wlr_headless_backend_create(server.event_loop); if(!headless_backend) { wlr_log(WLR_ERROR, "Unable to create the wlroots headless backend"); ret = 1; goto end; } wlr_headless_add_output(headless_backend, 600, 300); if(!wlr_multi_backend_add(backend, headless_backend)) { wlr_log(WLR_ERROR, "Unable to insert headless backend into multi backend"); 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; } server.renderer = wlr_renderer_autocreate(backend); if(!server.renderer) { wlr_log(WLR_ERROR, "Unable to create the wlroots renderer"); ret = 1; goto end; } server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); if(!server.allocator) { wlr_log(WLR_ERROR, "Unable to create the wlroots allocator"); ret = 1; goto end; } wlr_renderer_init_wl_display(server.renderer, server.wl_display); server.bg_color = calloc(4, sizeof(float *)); server.output_layout = wlr_output_layout_create(server.wl_display); if(!server.output_layout) { wlr_log(WLR_ERROR, "Unable to create output layout"); ret = 1; goto end; } if(ipc_init(&server) != 0) { wlr_log(WLR_ERROR, "Failed to initialize IPC"); ret = 1; goto end; } server.scene = wlr_scene_create(); if(!server.scene) { wlr_log(WLR_ERROR, "Unable to create scene"); ret = 1; goto end; } server.scene_output_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); compositor = wlr_compositor_create(server.wl_display, 6, server.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; } server.input = input_manager_create(&server); data_control_manager = wlr_data_control_manager_v1_create(server.wl_display); if(!data_control_manager) { wlr_log(WLR_ERROR, "Unable to create the data control 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); if(!server.seat) { wlr_log(WLR_ERROR, "Unable to create the seat"); ret = 1; goto end; } server.idle = wlr_idle_notifier_v1_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, 3); if(!xdg_shell) { wlr_log(WLR_ERROR, "Unable to create the XDG shell interface"); ret = 1; goto end; } server.new_xdg_shell_toplevel.notify = handle_xdg_shell_toplevel_new; wl_signal_add(&xdg_shell->events.new_surface, &server.new_xdg_shell_toplevel); 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); viewporter = wlr_viewporter_create(server.wl_display); if(!viewporter) { wlr_log(WLR_ERROR, "Unable to create the viewporter interface"); ret = 1; goto end; } 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; } if(!wlr_primary_selection_v1_device_manager_create(server.wl_display)) { wlr_log(WLR_ERROR, "Unable to create the primary selection device 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 NEDM_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); 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); } struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( server.seat->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 { fprintf(stderr, "Cagebreak " NEDM_VERSION " is running on Wayland display %s\n", socket); } #if NEDM_HAS_XWAYLAND wlr_xwayland_set_seat(xwayland, server.seat->seat); #endif /* Place the cursor to the top left of the output layout. */ wlr_cursor_warp(server.seat->cursor, NULL, 0, 0); if(!drop_permissions()) { ret = 1; goto end; } /* 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; } void add_output_callback(struct wlr_backend *backend, void *data) { long *dims = data; wlr_headless_add_output(backend, dims[0], dims[1]); } void create_output(char *line, struct nedm_server *server) { char *widthstr = strtok_r(NULL, ";", &line); long dims[2] = {600, 200}; if(widthstr != NULL) { dims[0] = strtol(widthstr, NULL, 10); if(line[0] != '\0') { ++line; } } char *heightstr = strtok_r(NULL, ";", &line); if(heightstr != NULL) { dims[1] = strtol(heightstr, NULL, 10); } long max_dim = 10000; if(dims[0] > max_dim || dims[0] <= 0) { wlr_log(WLR_ERROR, "height out of range."); return; } else if(dims[1] > max_dim || dims[1] <= 0) { wlr_log(WLR_ERROR, "width out of range."); return; } wlr_multi_for_each_backend(server->backend, add_output_callback, dims); } void destroy_output(char *line, struct nedm_server *server) { if(wl_list_length(&server->outputs) < 2) { return; } char *outpnstr = strtok_r(NULL, ";", &line); long outpn = 0; if(outpnstr != NULL) { outpn = strtol(outpnstr, NULL, 10); } outpn = outpn % wl_list_length(&server->outputs); struct nedm_output *it; wl_list_for_each(it, &server->outputs, link) { if(outpn == 0) { break; } else { --outpn; } } }