diff --git a/0001-vnc-Drop-frames-if-client-is-gone.patch b/0001-vnc-Drop-frames-if-client-is-gone.patch new file mode 100644 index 0000000..2ce0d97 --- /dev/null +++ b/0001-vnc-Drop-frames-if-client-is-gone.patch @@ -0,0 +1,81 @@ +From e607efe6584cffc3284776e5795af05bd28da10d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 9 Sep 2020 10:14:20 +0200 +Subject: [PATCH] vnc: Drop frames if client is gone + +Frames from PipeWire are posted asynchronously from a I/O thread to the +main thread where they are turned into VNC frame updates and cursor +movements. On the other hand, sessions are closed asynchronously when +the VNC client disappears. If a frame ended up on the main thread after +a client disappeared but before the session and stream was closed, we'd +try to turn the new frames into VNC updates without a client being +available, causing use after free. + +Fix this by dropping frames that happens during this time frame. + +Closes: https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/issues/43 +(cherry picked from commit ab97841629f5f3f4fab9993b6255b6ae04828b9c) +--- + src/grd-session-vnc.c | 7 +++++++ + src/grd-session-vnc.h | 2 ++ + src/grd-vnc-pipewire-stream.c | 8 ++++++++ + 3 files changed, 17 insertions(+) + +diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c +index 21735bf..3a8852d 100644 +--- a/src/grd-session-vnc.c ++++ b/src/grd-session-vnc.c +@@ -219,6 +219,12 @@ maybe_queue_close_session_idle (GrdSessionVnc *session_vnc) + g_idle_add (close_session_idle, session_vnc); + } + ++gboolean ++grd_session_vnc_is_client_gone (GrdSessionVnc *session_vnc) ++{ ++ return !session_vnc->rfb_client; ++} ++ + static void + handle_client_gone (rfbClientPtr rfb_client) + { +@@ -229,6 +235,7 @@ handle_client_gone (rfbClientPtr rfb_client) + grd_session_vnc_pause (session_vnc); + + maybe_queue_close_session_idle (session_vnc); ++ session_vnc->rfb_client = NULL; + } + + static void +diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h +index a065857..ffd8653 100644 +--- a/src/grd-session-vnc.h ++++ b/src/grd-session-vnc.h +@@ -76,4 +76,6 @@ void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc); + + GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc); + ++gboolean grd_session_vnc_is_client_gone (GrdSessionVnc *session_vnc); ++ + #endif /* GRD_SESSION_VNC_H */ +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index 1feaef1..71357f0 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -287,6 +287,14 @@ do_render (struct spa_loop *loop, + if (!frame) + return 0; + ++ if (grd_session_vnc_is_client_gone (stream->session)) ++ { ++ g_free (frame->data); ++ g_clear_pointer (&frame->rfb_cursor, rfbFreeCursor); ++ g_free (frame); ++ return 0; ++ } ++ + if (frame->rfb_cursor) + grd_session_vnc_set_cursor (stream->session, frame->rfb_cursor); + +-- +2.26.2 + diff --git a/0001-vnc-pipewire-stream-Handle-stride-mismatch.patch b/0001-vnc-pipewire-stream-Handle-stride-mismatch.patch index dcdad19..536634c 100644 --- a/0001-vnc-pipewire-stream-Handle-stride-mismatch.patch +++ b/0001-vnc-pipewire-stream-Handle-stride-mismatch.patch @@ -1,7 +1,7 @@ -From 78c5bcb181fe2b0b9fc17eea696feac8b504df54 Mon Sep 17 00:00:00 2001 +From b38d39072dd34e1128b1d43c0d692606311d5753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 7 May 2020 15:48:22 +0200 -Subject: [PATCH] vnc/pipewire-stream: Handle stride mismatch +Subject: [PATCH 1/6] vnc/pipewire-stream: Handle stride mismatch The VNC server framebuffer assumes a particular stride; but there is no guarantee that we'll get the same from PipeWire. Handle this gracefully @@ -14,7 +14,34 @@ diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c index 88c07be..261292a 100644 --- a/src/grd-vnc-pipewire-stream.c +++ b/src/grd-vnc-pipewire-stream.c -@@ -187,8 +187,6 @@ on_stream_param_changed (void *user_data, +@@ -160,84 +160,77 @@ on_stream_state_changed (void *user_data, + enum pw_stream_state state, + const char *error) + { + g_debug ("Pipewire stream state changed from %s to %s", + pw_stream_state_as_string (old), + pw_stream_state_as_string (state)); + + switch (state) + { + case PW_STREAM_STATE_ERROR: + g_warning ("PipeWire stream error: %s", error); + break; + case PW_STREAM_STATE_PAUSED: + case PW_STREAM_STATE_STREAMING: + case PW_STREAM_STATE_UNCONNECTED: + case PW_STREAM_STATE_CONNECTING: + break; + } + } + + static void + on_stream_param_changed (void *user_data, + uint32_t id, + const struct spa_pod *format) + { + GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data); + uint8_t params_buffer[1024]; struct spa_pod_builder pod_builder; int width; int height; @@ -23,7 +50,14 @@ index 88c07be..261292a 100644 const struct spa_pod *params[3]; if (!format || id != SPA_PARAM_Format) -@@ -203,14 +201,9 @@ on_stream_param_changed (void *user_data, + return; + + spa_format_video_raw_parse (format, &stream->spa_format); + + pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer)); + + width = stream->spa_format.size.width; + height = stream->spa_format.size.height; grd_session_vnc_queue_resize_framebuffer (stream->session, width, height); @@ -38,7 +72,61 @@ index 88c07be..261292a 100644 SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (8, 1, 8), 0); -@@ -319,6 +312,10 @@ process_buffer (GrdVncPipeWireStream *stream, + params[1] = spa_pod_builder_add_object ( + &pod_builder, + SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, + SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Header), + SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_header)), + 0); + + params[2] = spa_pod_builder_add_object( + &pod_builder, + SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, + SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor), + SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int (CURSOR_META_SIZE (64,64), + CURSOR_META_SIZE (1,1), + CURSOR_META_SIZE (256,256)), + 0); + + pw_stream_update_params (stream->pipewire_stream, + params, G_N_ELEMENTS (params)); + } + + static gboolean + spa_pixel_format_to_grd_pixel_format (uint32_t spa_format, + GrdPixelFormat *out_format) + { + if (spa_format == SPA_VIDEO_FORMAT_RGBA) + *out_format = GRD_PIXEL_FORMAT_RGBA8888; + else +@@ -292,101 +285,115 @@ do_render (struct spa_loop *loop, + g_mutex_unlock (&stream->frame_mutex); + + if (!frame) + return 0; + + if (frame->rfb_cursor) + grd_session_vnc_set_cursor (stream->session, frame->rfb_cursor); + + if (frame->cursor_moved) + { + grd_session_vnc_move_cursor (stream->session, + frame->cursor_x, + frame->cursor_y); + } + + if (frame->data) + grd_session_vnc_take_buffer (stream->session, frame->data); + + g_free (frame); + + return 0; + } + + static GrdVncFrame * + process_buffer (GrdVncPipeWireStream *stream, + struct spa_buffer *buffer) + { size_t size; uint8_t *map; void *src_data; @@ -49,7 +137,40 @@ index 88c07be..261292a 100644 struct spa_meta_cursor *spa_meta_cursor; g_autofree GrdVncFrame *frame = NULL; -@@ -359,7 +356,17 @@ process_buffer (GrdVncPipeWireStream *stream, + frame = g_new0 (GrdVncFrame, 1); + + if (buffer->datas[0].chunk->size == 0) + { + size = 0; + map = NULL; + src_data = NULL; + } + else if (buffer->datas[0].type == SPA_DATA_MemFd) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0); + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_DmaBuf) + { + int fd; + + fd = buffer->datas[0].fd; + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + sync_dma_buf (fd, DMA_BUF_SYNC_START); + + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_MemPtr) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = NULL; + src_data = buffer->datas[0].data; + } + else + { return NULL; } @@ -63,11 +184,38 @@ index 88c07be..261292a 100644 + { + memcpy (((uint8_t *) frame->data) + y * dst_stride, + ((uint8_t *) src_data) + y * src_stride, -+ stream->spa_format.size.width * 4); ++ dst_stride); + } if (map) { + if (buffer->datas[0].type == SPA_DATA_DmaBuf) + sync_dma_buf (buffer->datas[0].fd, DMA_BUF_SYNC_END); + munmap (map, size); + } + + spa_meta_cursor = spa_buffer_find_meta_data (buffer, SPA_META_Cursor, + sizeof *spa_meta_cursor); + if (spa_meta_cursor && spa_meta_cursor_is_valid (spa_meta_cursor)) + { + struct spa_meta_bitmap *spa_meta_bitmap; + GrdPixelFormat format; + + if (spa_meta_cursor->bitmap_offset) + { + spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, + spa_meta_cursor->bitmap_offset, + struct spa_meta_bitmap); + } + else + { + spa_meta_bitmap = NULL; + } + + if (spa_meta_bitmap && + spa_meta_bitmap->size.width > 0 && + spa_meta_bitmap->size.height > 0 && + spa_pixel_format_to_grd_pixel_format (spa_meta_bitmap->format, -- 2.26.2 diff --git a/0002-vnc-pipewire-stream-Properly-process-cursor-change-o.patch b/0002-vnc-pipewire-stream-Properly-process-cursor-change-o.patch new file mode 100644 index 0000000..d33318a --- /dev/null +++ b/0002-vnc-pipewire-stream-Properly-process-cursor-change-o.patch @@ -0,0 +1,151 @@ +From 685bdbbcc909fa3beb5a4c910befcd1f44131bc3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 17:12:58 +0200 +Subject: [PATCH 2/6] vnc-pipewire-stream: Properly process cursor-change-only + frames + +Such frames will have the buffer data size set to 0, as it is empty, but +may contain metadata carrying the cursor update. +--- + src/grd-vnc-pipewire-stream.c | 31 +++++++++++++++++-------------- + 1 file changed, 17 insertions(+), 14 deletions(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index 261292a..763f5eb 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -285,114 +285,117 @@ do_render (struct spa_loop *loop, + g_mutex_unlock (&stream->frame_mutex); + + if (!frame) + return 0; + + if (frame->rfb_cursor) + grd_session_vnc_set_cursor (stream->session, frame->rfb_cursor); + + if (frame->cursor_moved) + { + grd_session_vnc_move_cursor (stream->session, + frame->cursor_x, + frame->cursor_y); + } + + if (frame->data) + grd_session_vnc_take_buffer (stream->session, frame->data); + + g_free (frame); + + return 0; + } + + static GrdVncFrame * + process_buffer (GrdVncPipeWireStream *stream, + struct spa_buffer *buffer) + { + size_t size; + uint8_t *map; + void *src_data; +- int src_stride; +- int dst_stride; +- int height; +- int y; + struct spa_meta_cursor *spa_meta_cursor; + g_autofree GrdVncFrame *frame = NULL; + + frame = g_new0 (GrdVncFrame, 1); + + if (buffer->datas[0].chunk->size == 0) + { +- size = 0; + map = NULL; + src_data = NULL; + } + else if (buffer->datas[0].type == SPA_DATA_MemFd) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0); + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_DmaBuf) + { + int fd; + + fd = buffer->datas[0].fd; + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + sync_dma_buf (fd, DMA_BUF_SYNC_START); + + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_MemPtr) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = NULL; + src_data = buffer->datas[0].data; + } + else + { + return NULL; + } + +- src_stride = buffer->datas[0].chunk->stride; +- dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); +- height = stream->spa_format.size.height; +- +- frame->data = g_malloc (height * dst_stride); +- for (y = 0; y < height; y++) ++ if (src_data) + { +- memcpy (((uint8_t *) frame->data) + y * dst_stride, +- ((uint8_t *) src_data) + y * src_stride, +- dst_stride); ++ int src_stride; ++ int dst_stride; ++ int height; ++ int y; ++ ++ src_stride = buffer->datas[0].chunk->stride; ++ dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); ++ height = stream->spa_format.size.height; ++ ++ frame->data = g_malloc (height * dst_stride); ++ for (y = 0; y < height; y++) ++ { ++ memcpy (((uint8_t *) frame->data) + y * dst_stride, ++ ((uint8_t *) src_data) + y * src_stride, ++ dst_stride); ++ } + } + + if (map) + { + if (buffer->datas[0].type == SPA_DATA_DmaBuf) + sync_dma_buf (buffer->datas[0].fd, DMA_BUF_SYNC_END); + munmap (map, size); + } + + spa_meta_cursor = spa_buffer_find_meta_data (buffer, SPA_META_Cursor, + sizeof *spa_meta_cursor); + if (spa_meta_cursor && spa_meta_cursor_is_valid (spa_meta_cursor)) + { + struct spa_meta_bitmap *spa_meta_bitmap; + GrdPixelFormat format; + + if (spa_meta_cursor->bitmap_offset) + { + spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, + spa_meta_cursor->bitmap_offset, + struct spa_meta_bitmap); + } + else + { + spa_meta_bitmap = NULL; + } + + if (spa_meta_bitmap && + spa_meta_bitmap->size.width > 0 && + spa_meta_bitmap->size.height > 0 && +-- +2.26.2 + diff --git a/0003-session-vnc-Add-API-to-flush.patch b/0003-session-vnc-Add-API-to-flush.patch new file mode 100644 index 0000000..f512b12 --- /dev/null +++ b/0003-session-vnc-Add-API-to-flush.patch @@ -0,0 +1,149 @@ +From def96b6f225636b3b9edf1993ca2e1ba9ed82f30 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 17:03:52 +0200 +Subject: [PATCH 3/6] session-vnc: Add API to flush + +When no damage is to be reported, but e.g. cursor moved, we need to +flush, so add API to make this possible. +--- + src/grd-session-vnc.c | 6 ++++++ + src/grd-session-vnc.h | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c +index a93a2e3..21735bf 100644 +--- a/src/grd-session-vnc.c ++++ b/src/grd-session-vnc.c +@@ -152,60 +152,66 @@ grd_session_vnc_queue_resize_framebuffer (GrdSessionVnc *session_vnc, + if (session_vnc->rfb_client->preferredEncoding == -1) + { + session_vnc->pending_framebuffer_resize = TRUE; + session_vnc->pending_framebuffer_width = width; + session_vnc->pending_framebuffer_height = height; + return; + } + + resize_vnc_framebuffer (session_vnc, width, height); + } + + void + grd_session_vnc_take_buffer (GrdSessionVnc *session_vnc, + void *data) + { + if (session_vnc->pending_framebuffer_resize) + { + free (data); + return; + } + + free (session_vnc->rfb_screen->frameBuffer); + session_vnc->rfb_screen->frameBuffer = data; + + rfbMarkRectAsModified (session_vnc->rfb_screen, 0, 0, + session_vnc->rfb_screen->width, + session_vnc->rfb_screen->height); + rfbProcessEvents (session_vnc->rfb_screen, 0); + } + ++void ++grd_session_vnc_flush (GrdSessionVnc *session_vnc) ++{ ++ rfbProcessEvents (session_vnc->rfb_screen, 0); ++} ++ + void + grd_session_vnc_set_cursor (GrdSessionVnc *session_vnc, + rfbCursorPtr rfb_cursor) + { + rfbSetCursor (session_vnc->rfb_screen, rfb_cursor); + } + + void + grd_session_vnc_move_cursor (GrdSessionVnc *session_vnc, + int x, + int y) + { + if (session_vnc->rfb_screen->cursorX == x || + session_vnc->rfb_screen->cursorY == y) + return; + + LOCK (session_vnc->rfb_screen->cursorMutex); + session_vnc->rfb_screen->cursorX = x; + session_vnc->rfb_screen->cursorY = y; + UNLOCK (session_vnc->rfb_screen->cursorMutex); + + session_vnc->rfb_client->cursorWasMoved = TRUE; + } + + static void + maybe_queue_close_session_idle (GrdSessionVnc *session_vnc) + { + if (session_vnc->close_session_idle_id) + return; + +diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h +index 294860e..a065857 100644 +--- a/src/grd-session-vnc.h ++++ b/src/grd-session-vnc.h +@@ -22,56 +22,58 @@ + + #ifndef GRD_SESSION_VNC_H + #define GRD_SESSION_VNC_H + + #include + #include + #include + + #include "grd-session.h" + #include "grd-types.h" + + #define GRD_TYPE_SESSION_VNC (grd_session_vnc_get_type ()) + G_DECLARE_FINAL_TYPE (GrdSessionVnc, + grd_session_vnc, + GRD, SESSION_VNC, + GrdSession); + + typedef gboolean (* GrdVncSocketGrabFunc) (GrdSessionVnc *session_vnc, + GError **error); + + GrdSessionVnc *grd_session_vnc_new (GrdVncServer *vnc_server, + GSocketConnection *connection); + + void grd_session_vnc_queue_resize_framebuffer (GrdSessionVnc *session_vnc, + int width, + int height); + + void grd_session_vnc_take_buffer (GrdSessionVnc *session_vnc, + void *data); + ++void grd_session_vnc_flush (GrdSessionVnc *session_vnc); ++ + void grd_session_vnc_set_cursor (GrdSessionVnc *session_vnc, + rfbCursorPtr rfb_cursor); + + void grd_session_vnc_move_cursor (GrdSessionVnc *session_vnc, + int x, + int y); + + int grd_session_vnc_get_fd (GrdSessionVnc *session_vnc); + + int grd_session_vnc_get_framebuffer_stride (GrdSessionVnc *session_vnc); + + rfbClientPtr grd_session_vnc_get_rfb_client (GrdSessionVnc *session_vnc); + + void grd_session_vnc_grab_socket (GrdSessionVnc *session_vnc, + GrdVncSocketGrabFunc grab_func); + + void grd_session_vnc_ungrab_socket (GrdSessionVnc *session_vnc, + GrdVncSocketGrabFunc grab_func); + + gboolean grd_session_vnc_is_paused (GrdSessionVnc *session_vnc); + + void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc); + + GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc); + + #endif /* GRD_SESSION_VNC_H */ +-- +2.26.2 + diff --git a/0004-vnc-pipewire-stream-Flush-connection-if-no-new-pixel.patch b/0004-vnc-pipewire-stream-Flush-connection-if-no-new-pixel.patch new file mode 100644 index 0000000..fa52082 --- /dev/null +++ b/0004-vnc-pipewire-stream-Flush-connection-if-no-new-pixel.patch @@ -0,0 +1,82 @@ +From 8c92fe8bc6c3003aacdfd96fed2d82bc542f3f27 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 17:13:58 +0200 +Subject: [PATCH 4/6] vnc-pipewire-stream: Flush connection if no new pixel + buffer + +Otherwise we'll wait on input until we flush out our new cursor move +only output. +--- + src/grd-vnc-pipewire-stream.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index 763f5eb..ec23b0a 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -272,60 +272,62 @@ sync_dma_buf (int fd, + static int + do_render (struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) + { + GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data); + GrdVncFrame *frame; + + g_mutex_lock (&stream->frame_mutex); + frame = g_steal_pointer (&stream->pending_frame); + g_mutex_unlock (&stream->frame_mutex); + + if (!frame) + return 0; + + if (frame->rfb_cursor) + grd_session_vnc_set_cursor (stream->session, frame->rfb_cursor); + + if (frame->cursor_moved) + { + grd_session_vnc_move_cursor (stream->session, + frame->cursor_x, + frame->cursor_y); + } + + if (frame->data) + grd_session_vnc_take_buffer (stream->session, frame->data); ++ else ++ grd_session_vnc_flush (stream->session); + + g_free (frame); + + return 0; + } + + static GrdVncFrame * + process_buffer (GrdVncPipeWireStream *stream, + struct spa_buffer *buffer) + { + size_t size; + uint8_t *map; + void *src_data; + struct spa_meta_cursor *spa_meta_cursor; + g_autofree GrdVncFrame *frame = NULL; + + frame = g_new0 (GrdVncFrame, 1); + + if (buffer->datas[0].chunk->size == 0) + { + map = NULL; + src_data = NULL; + } + else if (buffer->datas[0].type == SPA_DATA_MemFd) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0); + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_DmaBuf) +-- +2.26.2 + diff --git a/0005-vnc-pipewire-stream-Correct-memcpy-size-calculation.patch b/0005-vnc-pipewire-stream-Correct-memcpy-size-calculation.patch new file mode 100644 index 0000000..25e5f46 --- /dev/null +++ b/0005-vnc-pipewire-stream-Correct-memcpy-size-calculation.patch @@ -0,0 +1,105 @@ +From 9ad54e84aaa5f5ddae5cb16a2726b314d6229076 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 27 Aug 2020 14:04:03 -0400 +Subject: [PATCH 5/6] vnc-pipewire-stream: Correct memcpy size calculation + +commit 78c5bcb181fe2b0b9fc17eea696feac8b504df54 introduced code +to try to account for a row stride mismatch between source and +destination. + +That commit, however, erroneously copies destination stride bytes +from the source, which will lead to out of bounds access if the +source has a smaller row stride. + +This commit addresses the problem, by instead, copying just the +pixel data, and none of the row margin. + +https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/18 +--- + src/grd-vnc-pipewire-stream.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index ec23b0a..b94e761 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -330,73 +330,75 @@ process_buffer (GrdVncPipeWireStream *stream, + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0); + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_DmaBuf) + { + int fd; + + fd = buffer->datas[0].fd; + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + sync_dma_buf (fd, DMA_BUF_SYNC_START); + + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_MemPtr) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = NULL; + src_data = buffer->datas[0].data; + } + else + { + return NULL; + } + + if (src_data) + { + int src_stride; + int dst_stride; ++ int width; + int height; + int y; + + src_stride = buffer->datas[0].chunk->stride; + dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); + height = stream->spa_format.size.height; ++ width = stream->spa_format.size.width; + + frame->data = g_malloc (height * dst_stride); + for (y = 0; y < height; y++) + { + memcpy (((uint8_t *) frame->data) + y * dst_stride, + ((uint8_t *) src_data) + y * src_stride, +- dst_stride); ++ width * 4); + } + } + + if (map) + { + if (buffer->datas[0].type == SPA_DATA_DmaBuf) + sync_dma_buf (buffer->datas[0].fd, DMA_BUF_SYNC_END); + munmap (map, size); + } + + spa_meta_cursor = spa_buffer_find_meta_data (buffer, SPA_META_Cursor, + sizeof *spa_meta_cursor); + if (spa_meta_cursor && spa_meta_cursor_is_valid (spa_meta_cursor)) + { + struct spa_meta_bitmap *spa_meta_bitmap; + GrdPixelFormat format; + + if (spa_meta_cursor->bitmap_offset) + { + spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, + spa_meta_cursor->bitmap_offset, + struct spa_meta_bitmap); + } + else + { + spa_meta_bitmap = NULL; + } + + if (spa_meta_bitmap && + spa_meta_bitmap->size.width > 0 && +-- +2.26.2 + diff --git a/0006-vnc-pipewire-stream-Handle-mmap-failure-better.patch b/0006-vnc-pipewire-stream-Handle-mmap-failure-better.patch new file mode 100644 index 0000000..80890a0 --- /dev/null +++ b/0006-vnc-pipewire-stream-Handle-mmap-failure-better.patch @@ -0,0 +1,116 @@ +From e8181aa61ca8e72ea607390b4368639d71d65700 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 28 Aug 2020 12:34:45 -0400 +Subject: [PATCH 6/6] vnc-pipewire-stream: Handle mmap failure better + +Right now if mmap fails, we just sort of crash. + +This commit changes the code to warn why it failed, and then +continue marching along. +--- + src/grd-vnc-pipewire-stream.c | 25 ++++++++++++++++++++++--- + 1 file changed, 22 insertions(+), 3 deletions(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index b94e761..1feaef1 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -301,73 +301,92 @@ do_render (struct spa_loop *loop, + grd_session_vnc_take_buffer (stream->session, frame->data); + else + grd_session_vnc_flush (stream->session); + + g_free (frame); + + return 0; + } + + static GrdVncFrame * + process_buffer (GrdVncPipeWireStream *stream, + struct spa_buffer *buffer) + { + size_t size; + uint8_t *map; + void *src_data; + struct spa_meta_cursor *spa_meta_cursor; + g_autofree GrdVncFrame *frame = NULL; + + frame = g_new0 (GrdVncFrame, 1); + + if (buffer->datas[0].chunk->size == 0) + { + map = NULL; + src_data = NULL; + } + else if (buffer->datas[0].type == SPA_DATA_MemFd) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0); +- src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); ++ ++ if (map != MAP_FAILED) ++ { ++ src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); ++ } ++ else ++ { ++ g_warning ("Could not map memfd: %m"); ++ map = NULL; ++ src_data = NULL; ++ } + } + else if (buffer->datas[0].type == SPA_DATA_DmaBuf) + { + int fd; + + fd = buffer->datas[0].fd; + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); +- sync_dma_buf (fd, DMA_BUF_SYNC_START); + +- src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); ++ if (map != MAP_FAILED) ++ { ++ sync_dma_buf (fd, DMA_BUF_SYNC_START); ++ src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); ++ } ++ else ++ { ++ g_warning ("Could not map dma-buf: %m"); ++ map = NULL; ++ src_data = NULL; ++ } + } + else if (buffer->datas[0].type == SPA_DATA_MemPtr) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = NULL; + src_data = buffer->datas[0].data; + } + else + { + return NULL; + } + + if (src_data) + { + int src_stride; + int dst_stride; + int width; + int height; + int y; + + src_stride = buffer->datas[0].chunk->stride; + dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); + height = stream->spa_format.size.height; + width = stream->spa_format.size.width; + + frame->data = g_malloc (height * dst_stride); + for (y = 0; y < height; y++) + { + memcpy (((uint8_t *) frame->data) + y * dst_stride, + ((uint8_t *) src_data) + y * src_stride, +-- +2.26.2 + diff --git a/gnome-remote-desktop.spec b/gnome-remote-desktop.spec index 5c85c4c..81033a3 100644 --- a/gnome-remote-desktop.spec +++ b/gnome-remote-desktop.spec @@ -2,7 +2,7 @@ Name: gnome-remote-desktop Version: 0.1.8 -Release: 3%{?dist} +Release: 5%{?dist} Summary: GNOME Remote Desktop screen share service License: GPLv2+ @@ -12,7 +12,13 @@ Source0: https://gitlab.gnome.org/jadahl/gnome-remote-desktop/uploads/20e # Adds encryption support (requires patched LibVNCServer) Patch0: anon-tls-support.patch -Patch1: 0001-vnc-pipewire-stream-Handle-stride-mismatch.patch +Patch10001: 0001-vnc-pipewire-stream-Handle-stride-mismatch.patch +Patch10002: 0002-vnc-pipewire-stream-Properly-process-cursor-change-o.patch +Patch10003: 0003-session-vnc-Add-API-to-flush.patch +Patch10004: 0004-vnc-pipewire-stream-Flush-connection-if-no-new-pixel.patch +Patch10005: 0005-vnc-pipewire-stream-Correct-memcpy-size-calculation.patch +Patch10006: 0006-vnc-pipewire-stream-Handle-mmap-failure-better.patch +Patch10007: 0001-vnc-Drop-frames-if-client-is-gone.patch BuildRequires: git BuildRequires: gcc @@ -71,6 +77,17 @@ GNOME desktop environment. %changelog +* Wed Sep 16 2020 Jonas Ã…dahl - 0.1.8-5 +- Fix crash when reconnecting + +* Thu Aug 27 2020 Ray Strode - 0.1.8-4 +- Fix other crash + Related: #1844993 + +* Thu Aug 27 2020 Ray Strode - 0.1.8-3 +- Fix crash + Related: #1844993 + * Mon Jun 1 2020 Felipe Borges - 0.1.8-2 - Fix black screen issue in remote connections on Wayland