diff --git a/.gitignore b/.gitignore index 8847226..45a2fae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -SOURCES/spice-gtk-0.37.tar.bz2 +SOURCES/spice-gtk-0.38.tar.xz SOURCES/victortoso-E37A484F.keyring diff --git a/.spice-gtk.metadata b/.spice-gtk.metadata index 41bb860..9afc954 100644 --- a/.spice-gtk.metadata +++ b/.spice-gtk.metadata @@ -1,2 +1,2 @@ -044a5266a613f2605f4f3f7359d7251b2141d203 SOURCES/spice-gtk-0.37.tar.bz2 +2d378aaeaecd0cb35c43b9ab216df840060d1baa SOURCES/spice-gtk-0.38.tar.xz da7a529db1ea28a1540c5892ea9836abeb378c3e SOURCES/victortoso-E37A484F.keyring diff --git a/SOURCES/0001-channel-main-Avoid-macro-side-effects.patch b/SOURCES/0001-channel-main-Avoid-macro-side-effects.patch new file mode 100644 index 0000000..dabb8ad --- /dev/null +++ b/SOURCES/0001-channel-main-Avoid-macro-side-effects.patch @@ -0,0 +1,34 @@ +From d8922b93f52d08fb81cc66842b4df80d1d1d9a7e Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Thu, 19 Mar 2020 15:50:45 +0000 +Subject: [PATCH 1/9] channel-main: Avoid macro side effects + +In big endian system GUINT32_TO_LE macro uses the parameter +multiple time causing serial to be incremented multiple times +instead of one. +Avoid side effects using a temporary variable. + +Signed-off-by: Frediano Ziglio +Acked-by: Victor Toso +(cherry picked from commit 7363d1a0a640b6992d5967621e37c3376958d708) +--- + src/channel-main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/channel-main.c b/src/channel-main.c +index 1e85a36..e89b813 100644 +--- a/src/channel-main.c ++++ b/src/channel-main.c +@@ -1384,7 +1384,8 @@ static void agent_clipboard_grab(SpiceMainChannel *channel, guint selection, + } + + if (test_agent_cap(channel, VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL)) { +- *(uint32_t *)grab = GUINT32_TO_LE(c->clipboard_serial[selection]++); ++ uint32_t serial = c->clipboard_serial[selection]++; ++ *(uint32_t *)grab = GUINT32_TO_LE(serial); + grab = (void *)grab + sizeof(uint32_t); + } + +-- +2.26.2 + diff --git a/SOURCES/0001-quic-Check-we-have-some-data-to-start-decoding-quic-.patch b/SOURCES/0001-quic-Check-we-have-some-data-to-start-decoding-quic-.patch deleted file mode 100644 index 896bfbc..0000000 --- a/SOURCES/0001-quic-Check-we-have-some-data-to-start-decoding-quic-.patch +++ /dev/null @@ -1,34 +0,0 @@ -From d9cc2d4659950df230dfe30e5445b91d4c15604e Mon Sep 17 00:00:00 2001 -From: Frediano Ziglio -Date: Wed, 29 Apr 2020 15:09:13 +0100 -Subject: [PATCH spice-common 1/4] quic: Check we have some data to start - decoding quic image - -All paths already pass some data to quic_decode_begin but for the -test check it, it's not that expensive test. -Checking for not 0 is enough, all other words will potentially be -read calling more_io_words but we need one to avoid a potential -initial buffer overflow or deferencing an invalid pointer. - -Signed-off-by: Frediano Ziglio -Acked-by: Uri Lublin ---- - common/quic.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/subprojects/spice-common/common/quic.c b/subprojects/spice-common/common/quic.c -index 55a5d6c..e03f3af 100644 ---- a/subprojects/spice-common/common/quic.c -+++ b/subprojects/spice-common/common/quic.c -@@ -1136,7 +1136,7 @@ int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_w - int channels; - int bpc; - -- if (!encoder_reset(encoder, io_ptr, io_ptr_end)) { -+ if (!num_io_words || !encoder_reset(encoder, io_ptr, io_ptr_end)) { - return QUIC_ERROR; - } - --- -2.25.4 - diff --git a/SOURCES/0002-channel-main-Check-proper-size-and-caps-handling-VD_.patch b/SOURCES/0002-channel-main-Check-proper-size-and-caps-handling-VD_.patch new file mode 100644 index 0000000..8383ad7 --- /dev/null +++ b/SOURCES/0002-channel-main-Check-proper-size-and-caps-handling-VD_.patch @@ -0,0 +1,77 @@ +From 5324e83bf636b995522088d508e61ba5543777f4 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Thu, 19 Mar 2020 06:07:39 +0000 +Subject: [PATCH 2/9] channel-main: Check proper size and caps handling + VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE + +VDAgentFileXferStatusMessage message can or cannot contain detailed +information attached to it. +Detect this correctly checking capabilities and flags. +This fixes a small buffer overflow reading in case the details are +off the payload. + +Signed-off-by: Frediano Ziglio +Acked-by: Victor Toso +(cherry picked from commit b13fd0664075c951f5418f5828c0803408ad664d) +--- + meson.build | 2 +- + src/channel-main.c | 16 +++++++++++++--- + 2 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/meson.build b/meson.build +index 995268b..8098989 100644 +--- a/meson.build ++++ b/meson.build +@@ -81,7 +81,7 @@ endforeach + # + # check for mandatory dependencies + # +-spice_protocol_version='>= 0.14.1' ++spice_protocol_version='>= 0.14.2' + + glib_version = '2.46' + glib_version_info = '>= @0@'.format(glib_version) +diff --git a/src/channel-main.c b/src/channel-main.c +index e89b813..c4fe02b 100644 +--- a/src/channel-main.c ++++ b/src/channel-main.c +@@ -1891,6 +1891,7 @@ static void file_xfer_read_async_cb(GObject *source_object, + + /* coroutine context */ + static void main_agent_handle_xfer_status(SpiceMainChannel *channel, ++ const VDAgentMessage *msg_hdr, + VDAgentFileXferStatusMessage *msg) + { + SpiceFileTransferTask *xfer_task; +@@ -1917,8 +1918,17 @@ static void main_agent_handle_xfer_status(SpiceMainChannel *channel, + _("The spice agent reported an error during the file transfer")); + break; + case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE: { +- uint64_t *free_space = SPICE_ALIGNED_CAST(uint64_t *, msg->data); +- gchar *free_space_str = g_format_size(*free_space); ++ const VDAgentFileXferStatusNotEnoughSpace *err = ++ (VDAgentFileXferStatusNotEnoughSpace*) msg->data; ++ if (!test_agent_cap(channel, VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS) || ++ msg_hdr->size < sizeof(*msg) + sizeof(*err)) { ++ error = ++ g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED, ++ _("File transfer failed due to lack of free space on remote machine")); ++ break; ++ } ++ ++ gchar *free_space_str = g_format_size(err->disk_free_space); + gchar *file_size_str = g_format_size(spice_file_transfer_task_get_total_bytes(xfer_task)); + error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED, + _("File transfer failed due to lack of free space on remote machine " +@@ -2110,7 +2120,7 @@ static void main_agent_handle_msg(SpiceChannel *channel, + break; + } + case VD_AGENT_FILE_XFER_STATUS: +- main_agent_handle_xfer_status(self, payload); ++ main_agent_handle_xfer_status(self, msg, payload); + break; + default: + g_warning("unhandled agent message type: %u (%s), size %u", +-- +2.26.2 + diff --git a/SOURCES/0002-quic-Check-image-size-in-quic_decode_begin.patch b/SOURCES/0002-quic-Check-image-size-in-quic_decode_begin.patch deleted file mode 100644 index ea52954..0000000 --- a/SOURCES/0002-quic-Check-image-size-in-quic_decode_begin.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 19cd6fe85610b424349db2d97e2dd0e2761a4a05 Mon Sep 17 00:00:00 2001 -From: Frediano Ziglio -Date: Wed, 29 Apr 2020 15:10:24 +0100 -Subject: [PATCH spice-common 2/4] quic: Check image size in quic_decode_begin - -Avoid some overflow in code due to images too big or -negative numbers. - -Signed-off-by: Frediano Ziglio -Acked-by: Uri Lublin ---- - common/quic.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/subprojects/spice-common/common/quic.c b/subprojects/spice-common/common/quic.c -index e03f3af..890f128 100644 ---- a/subprojects/spice-common/common/quic.c -+++ b/subprojects/spice-common/common/quic.c -@@ -56,6 +56,9 @@ typedef uint8_t BYTE; - #define MINwminext 1 - #define MAXwminext 100000000 - -+/* Maximum image size in pixels, mainly to avoid possible integer overflows */ -+#define SPICE_MAX_IMAGE_SIZE (512 * 1024 * 1024 - 1) -+ - typedef struct QuicFamily { - unsigned int nGRcodewords[MAXNUMCODES]; /* indexed by code number, contains number of - unmodified GR codewords in the code */ -@@ -1165,6 +1168,16 @@ int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_w - height = encoder->io_word; - decode_eat32bits(encoder); - -+ if (width <= 0 || height <= 0) { -+ encoder->usr->warn(encoder->usr, "invalid size\n"); -+ return QUIC_ERROR; -+ } -+ -+ /* avoid too big images */ -+ if ((uint64_t) width * height > SPICE_MAX_IMAGE_SIZE) { -+ encoder->usr->error(encoder->usr, "image too large\n"); -+ } -+ - quic_image_params(encoder, type, &channels, &bpc); - - if (!encoder_reset_channels(encoder, channels, width, bpc)) { --- -2.25.4 - diff --git a/SOURCES/0003-build-sys-bump-polkit-requirement-to-0.101.patch b/SOURCES/0003-build-sys-bump-polkit-requirement-to-0.101.patch new file mode 100644 index 0000000..991d977 --- /dev/null +++ b/SOURCES/0003-build-sys-bump-polkit-requirement-to-0.101.patch @@ -0,0 +1,94 @@ +From f036039b2ab3945940159342fb3880064f2611c9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Thu, 9 Apr 2020 01:05:00 +0200 +Subject: [PATCH 3/9] build-sys: bump polkit requirement to 0.101 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It was released on 2011-03-03, and is available in old debian stable +and all distros we care about. + +Signed-off-by: Marc-AndrĂ© Lureau +Acked-by: Frediano Ziglio +(cherry picked from commit 94a3375beff5df8c8add33cf2f1b74350185dce2) +--- + meson.build | 7 +----- + src/spice-client-glib-usb-acl-helper.c | 34 -------------------------- + 2 files changed, 1 insertion(+), 40 deletions(-) + +diff --git a/meson.build b/meson.build +index 8098989..c442a0d 100644 +--- a/meson.build ++++ b/meson.build +@@ -204,14 +204,9 @@ endif + + # polkit + spice_gtk_has_polkit = false +-d = dependency('polkit-gobject-1', version : '>= 0.96', required : get_option('polkit')) ++d = dependency('polkit-gobject-1', version : '>= 0.101', required : get_option('polkit')) + if d.found() + spice_gtk_policy_dir = d.get_pkgconfig_variable('policydir') +- foreach func : ['polkit_authority_get_sync', 'polkit_authorization_result_get_dismissed'] +- if compiler.has_function(func, dependencies : d) +- spice_gtk_config_data.set('HAVE_@0@'.format(func.to_upper()), '1') +- endif +- endforeach + + # TODO: With 'auto', we should just disable polkit support if this is missing. + if not compiler.has_function('acl_get_file') +diff --git a/src/spice-client-glib-usb-acl-helper.c b/src/spice-client-glib-usb-acl-helper.c +index f75e0fc..de9e605 100644 +--- a/src/spice-client-glib-usb-acl-helper.c ++++ b/src/spice-client-glib-usb-acl-helper.c +@@ -156,25 +156,6 @@ static void cleanup(void) + g_main_loop_quit(loop); + } + +-/* Not available in polkit < 0.101 */ +-#ifndef HAVE_POLKIT_AUTHORIZATION_RESULT_GET_DISMISSED +-static gboolean +-polkit_authorization_result_get_dismissed(PolkitAuthorizationResult *result) +-{ +- gboolean ret; +- PolkitDetails *details; +- +- g_return_val_if_fail(POLKIT_IS_AUTHORIZATION_RESULT(result), FALSE); +- +- ret = FALSE; +- details = polkit_authorization_result_get_details(result); +- if (details != NULL && polkit_details_lookup(details, "polkit.dismissed")) +- ret = TRUE; +- +- return ret; +-} +-#endif +- + static void check_authorization_cb(PolkitAuthority *authority, + GAsyncResult *res, gpointer data) + { +@@ -291,21 +272,6 @@ static void stdin_read_complete(GObject *src, GAsyncResult *res, gpointer data) + g_free(s); + } + +-/* Fix for polkit 0.97 and later */ +-#ifndef HAVE_POLKIT_AUTHORITY_GET_SYNC +-static PolkitAuthority * +-polkit_authority_get_sync (GCancellable *cancellable, GError **error) +-{ +- PolkitAuthority *authority; +- +- authority = polkit_authority_get (); +- if (!authority) +- g_set_error (error, 0, 0, "failed to get the PolicyKit authority"); +- +- return authority; +-} +-#endif +- + #ifndef HAVE_CLEARENV + extern char **environ; + +-- +2.26.2 + diff --git a/SOURCES/0003-quic-Check-RLE-lengths.patch b/SOURCES/0003-quic-Check-RLE-lengths.patch deleted file mode 100644 index aebad02..0000000 --- a/SOURCES/0003-quic-Check-RLE-lengths.patch +++ /dev/null @@ -1,35 +0,0 @@ -From d45a4954d73b41a255b8b4ec57c01ae87ec2936e Mon Sep 17 00:00:00 2001 -From: Frediano Ziglio -Date: Wed, 29 Apr 2020 15:11:38 +0100 -Subject: [PATCH spice-common 3/4] quic: Check RLE lengths - -Avoid buffer overflows decoding images. On compression we compute -lengths till end of line so it won't cause regressions. -Proved by fuzzing the code. - -Signed-off-by: Frediano Ziglio -Acked-by: Uri Lublin ---- - common/quic_tmpl.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/subprojects/spice-common/common/quic_tmpl.c b/subprojects/spice-common/common/quic_tmpl.c -index f0a4927..11e09f5 100644 ---- a/subprojects/spice-common/common/quic_tmpl.c -+++ b/subprojects/spice-common/common/quic_tmpl.c -@@ -570,7 +570,11 @@ static void FNAME_DECL(uncompress_row_seg)(const PIXEL * const prev_row, - do_run: - state->waitcnt = stopidx - i; - run_index = i; -- run_end = i + decode_state_run(encoder, state); -+ run_end = decode_state_run(encoder, state); -+ if (run_end < 0 || run_end > (end - i)) { -+ encoder->usr->error(encoder->usr, "wrong RLE\n"); -+ } -+ run_end += i; - - for (; i < run_end; i++) { - UNCOMPRESS_PIX_START(&cur_row[i]); --- -2.25.4 - diff --git a/SOURCES/0004-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch b/SOURCES/0004-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch deleted file mode 100644 index 291215f..0000000 --- a/SOURCES/0004-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 57c6e6b00247ad289a27648213d7ad2306fe3931 Mon Sep 17 00:00:00 2001 -From: Frediano Ziglio -Date: Thu, 30 Apr 2020 10:19:09 +0100 -Subject: [PATCH spice-common 4/4] quic: Avoid possible buffer overflow in - find_bucket - -Proved by fuzzing the code. - -Signed-off-by: Frediano Ziglio -Acked-by: Uri Lublin ---- - common/quic_family_tmpl.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/subprojects/spice-common/common/quic_family_tmpl.c b/subprojects/spice-common/common/quic_family_tmpl.c -index 8a5f7d2..6cc051b 100644 ---- a/subprojects/spice-common/common/quic_family_tmpl.c -+++ b/subprojects/spice-common/common/quic_family_tmpl.c -@@ -107,7 +107,12 @@ static s_bucket *FNAME(find_bucket)(Channel *channel, const unsigned int val) - spice_assert(val < (0x1U << BPC)); - } - -- return channel->_buckets_ptrs[val]; -+ /* The and (&) here is to avoid buffer overflows in case of garbage or malicious -+ * attempts. Is much faster then using comparisons and save us from such situations. -+ * Note that on normal build the check above won't be compiled as this code path -+ * is pretty hot and would cause speed regressions. -+ */ -+ return channel->_buckets_ptrs[val & ((1U << BPC) - 1)]; - } - - #undef FNAME --- -2.25.4 diff --git a/SOURCES/0004-spice-channel-Read-all-available-data-from-SASL-buff.patch b/SOURCES/0004-spice-channel-Read-all-available-data-from-SASL-buff.patch new file mode 100644 index 0000000..d703a6b --- /dev/null +++ b/SOURCES/0004-spice-channel-Read-all-available-data-from-SASL-buff.patch @@ -0,0 +1,47 @@ +From 1562755fd60ae79d595dd8be0d1ddf5b784729c2 Mon Sep 17 00:00:00 2001 +From: Gilmar Santos Jr +Date: Wed, 6 May 2020 12:58:51 -0300 +Subject: [PATCH 4/9] spice-channel: Read all available data from SASL buffer + +When SASL is in use, its buffer may contain remaining data from previously +received messages. The spice_channel_iterate_read should use it, even if c->in +socket is not readable. + +Fixes: https://gitlab.freedesktop.org/spice/spice-gtk/-/issues/126 + +Acked-by: Frediano Ziglio +(cherry picked from commit 80e9c852a406d93cad3fc7b845fe181d75356f11) +--- + src/spice-channel.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/src/spice-channel.c b/src/spice-channel.c +index 315e287..5824fdd 100644 +--- a/src/spice-channel.c ++++ b/src/spice-channel.c +@@ -2345,16 +2345,15 @@ static void spice_channel_iterate_read(SpiceChannel *channel) + /* treat all incoming data (block on message completion) */ + while (!c->has_error && + c->state != SPICE_CHANNEL_STATE_MIGRATING && +- g_pollable_input_stream_is_readable(G_POLLABLE_INPUT_STREAM(c->in)) +- ) { do +- spice_channel_recv_msg(channel, +- (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL); ++ (g_pollable_input_stream_is_readable(G_POLLABLE_INPUT_STREAM(c->in)) + #ifdef HAVE_SASL + /* flush the sasl buffer too */ +- while (c->sasl_decoded != NULL); +-#else +- while (FALSE); ++ || c->sasl_decoded != NULL + #endif ++ ) ++ ) { ++ spice_channel_recv_msg(channel, ++ (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL); + } + + } +-- +2.26.2 + diff --git a/SOURCES/0005-meson-add-wayland-protocols.patch b/SOURCES/0005-meson-add-wayland-protocols.patch new file mode 100644 index 0000000..c9e8f73 --- /dev/null +++ b/SOURCES/0005-meson-add-wayland-protocols.patch @@ -0,0 +1,160 @@ +From 8d3731162c2b3aae518f8d8b2d7190c4deec9911 Mon Sep 17 00:00:00 2001 +From: Francesco Giudici +Date: Thu, 27 Feb 2020 11:55:15 +0100 +Subject: [PATCH 5/9] meson: add wayland protocols + +Generate wayland protocols: these will be used later for locking the +mouse pointer and getting relative mouse movements when using the mouse +in server mode in Wayland. +The meson build steps have been freely taken and adapted from Weston +source code: +https://gitlab.freedesktop.org/wayland/weston/-/blob/master/protocol/meson.build + +Signed-off-by: Francesco Giudici +Acked-by: Frediano Ziglio +(cherry picked from commit d70044ab49728524c68796dd371d2bf7d94d27e5) +--- + meson.build | 20 +++++++++++++++ + meson_options.txt | 4 +++ + src/meson.build | 62 +++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 84 insertions(+), 2 deletions(-) + +diff --git a/meson.build b/meson.build +index c442a0d..7426ea5 100644 +--- a/meson.build ++++ b/meson.build +@@ -31,6 +31,7 @@ spice_gtk_config_data = configuration_data() + spice_gtk_include = [include_directories('.')] + spice_glib_deps = [] + spice_gtk_deps = [] ++spice_wayland_deps = [] + spice_acl_deps = [] + + # +@@ -152,6 +153,25 @@ if d.found() + spice_gtk_has_gtk = true + endif + ++# wayland protocols ++spice_gtk_has_wayland_protocols = false ++# Check if gtk is enabled and supports the wayland backend ++if host_machine.system() != 'windows' and spice_gtk_has_gtk and compiler.has_header('gtk-3.0/gdk/gdkwayland.h') ++ d = dependency('wayland-protocols', version: '>= 1.17', required: get_option('wayland-protocols')) ++ if d.found() ++ spice_gtk_config_data.set('HAVE_WAYLAND_PROTOCOLS', '1') ++ dir_wp_base = d.get_pkgconfig_variable('pkgdatadir') ++ dep_scanner = dependency('wayland-scanner', native: true) ++ prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner')) ++ ++ wayland_libs_version_required = '1.17.0' ++ spice_wayland_deps += dependency('wayland-server', version : '>= @0@'.format(wayland_libs_version_required)) ++ spice_wayland_deps += dependency('wayland-cursor', version : '>= @0@'.format(wayland_libs_version_required)) ++ spice_wayland_deps += dependency('wayland-client', version : '>= @0@'.format(wayland_libs_version_required)) ++ spice_gtk_has_wayland_protocols = true ++ endif ++endif ++ + # webdav + spice_gtk_has_phodav = false + d = dependency('libphodav-2.0', required: get_option('webdav')) +diff --git a/meson_options.txt b/meson_options.txt +index 9804217..60b87ca 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -2,6 +2,10 @@ option('gtk', + type : 'feature', + description: 'Enable gtk+') + ++option('wayland-protocols', ++ type : 'feature', ++ description: 'Enable wayland protocols') ++ + option('webdav', + type : 'feature', + description: 'Enable webdav support') +diff --git a/src/meson.build b/src/meson.build +index 654dab5..bdd2239 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -318,6 +318,64 @@ if spice_gtk_has_gtk + command : cmd) + endforeach + ++ # ++ # Wayland protocols ++ # ++ if spice_gtk_has_wayland_protocols ++ ++ generated_protocols = [ ++ [ 'pointer-constraints', 'v1' ], ++ [ 'relative-pointer', 'v1' ], ++ #[ 'input-method', 'v1' ], ++ #[ 'input-timestamps', 'v1' ], ++ #[ 'fullscreen-shell', 'v1' ], ++ #[ 'linux-dmabuf', 'v1' ], ++ #[ 'linux-explicit-synchronization', 'v1' ], ++ #[ 'presentation-time', 'stable' ], ++ #[ 'tablet', 'v2' ], ++ #[ 'text-input', 'v1' ], ++ #[ 'viewporter', 'stable' ], ++ #[ 'xdg-output', 'v1' ], ++ #[ 'xdg-shell', 'v6' ], ++ #[ 'xdg-shell', 'stable' ], ++ ] ++ ++ foreach proto: generated_protocols ++ proto_name = proto[0] ++ if proto[1] == 'internal' ++ base_file = proto_name ++ xml_path = '@0@.xml'.format(proto_name) ++ elif proto[1] == 'stable' ++ base_file = proto_name ++ xml_path = '@0@/stable/@1@/@1@.xml'.format(dir_wp_base, base_file) ++ else ++ base_file = '@0@-unstable-@1@'.format(proto_name, proto[1]) ++ xml_path = '@0@/unstable/@1@/@2@.xml'.format(dir_wp_base, proto_name, base_file) ++ endif ++ ++ foreach output_type: [ 'client-header', 'server-header', 'private-code' ] ++ if output_type == 'client-header' ++ output_file = '@0@-client-protocol.h'.format(base_file) ++ elif output_type == 'server-header' ++ output_file = '@0@-server-protocol.h'.format(base_file) ++ else ++ output_file = '@0@-protocol.c'.format(base_file) ++ if dep_scanner.version().version_compare('< 1.14.91') ++ output_type = 'code' ++ endif ++ endif ++ ++ spice_client_gtk_sources += custom_target( ++ '@0@ @1@'.format(base_file, output_type), ++ command: [ prog_scanner, output_type, '@INPUT@', '@OUTPUT@' ], ++ input: xml_path, ++ output: output_file, ++ ) ++ message('@0@ @1@: @2@ -> @3@'.format(prog_scanner, output_type, xml_path, output_file)) ++ endforeach ++ endforeach ++ endif ++ + # + # libspice-client-gtk.so + # +@@ -343,11 +401,11 @@ if spice_gtk_has_gtk + install : true, + link_args : [spice_gtk_version_script], + link_depends : spice_client_gtk_syms, +- dependencies : [spice_client_glib_dep, spice_gtk_deps]) ++ dependencies : [spice_client_glib_dep, spice_gtk_deps, spice_wayland_deps]) + + spice_client_gtk_dep = declare_dependency(sources : spice_widget_enums[1], + link_with : spice_client_gtk_lib, +- dependencies : [spice_client_glib_dep, spice_gtk_deps]) ++ dependencies : [spice_client_glib_dep, spice_gtk_deps, spice_wayland_deps]) + + if spice_gtk_has_introspection + # +-- +2.26.2 + diff --git a/SOURCES/0006-wayland-add-wayland-extensions-functions.patch b/SOURCES/0006-wayland-add-wayland-extensions-functions.patch new file mode 100644 index 0000000..b195ac2 --- /dev/null +++ b/SOURCES/0006-wayland-add-wayland-extensions-functions.patch @@ -0,0 +1,321 @@ +From 19e0922572cee7bda989a82407208eac8a41b47f Mon Sep 17 00:00:00 2001 +From: Francesco Giudici +Date: Tue, 28 Mar 2017 15:07:16 +0200 +Subject: [PATCH 6/9] wayland: add wayland-extensions functions + +add utilities to lock the pointer to a window and to get relative mouse +movement in Wayland. This is made possible thanks to the wayland +protocols introduced in the previous commit. + +Code freely taken and adapted from Christophe Fergeau branch: +https://gitlab.freedesktop.org/teuf/spice-gtk/-/tree/wayland + +Signed-off-by: Francesco Giudici +Acked-by: Frediano Ziglio +(cherry picked from commit 59d5c92c74da0f452b9104191bb752c33d26ec77) +--- + src/meson.build | 5 + + src/wayland-extensions.c | 235 +++++++++++++++++++++++++++++++++++++++ + src/wayland-extensions.h | 32 ++++++ + 3 files changed, 272 insertions(+) + create mode 100644 src/wayland-extensions.c + create mode 100644 src/wayland-extensions.h + +diff --git a/src/meson.build b/src/meson.build +index bdd2239..16810a7 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -374,6 +374,11 @@ if spice_gtk_has_gtk + message('@0@ @1@: @2@ -> @3@'.format(prog_scanner, output_type, xml_path, output_file)) + endforeach + endforeach ++ ++ spice_client_gtk_sources += [ ++ 'wayland-extensions.c', ++ 'wayland-extensions.h', ++ ] + endif + + # +diff --git a/src/wayland-extensions.c b/src/wayland-extensions.c +new file mode 100644 +index 0000000..64b5139 +--- /dev/null ++++ b/src/wayland-extensions.c +@@ -0,0 +1,235 @@ ++/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* ++ Copyright (C) 2017 Red Hat, Inc. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, see . ++*/ ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++#include "pointer-constraints-unstable-v1-client-protocol.h" ++#include "relative-pointer-unstable-v1-client-protocol.h" ++ ++#include "wayland-extensions.h" ++ ++static void * ++gtk_wl_registry_bind(GtkWidget *widget, ++ uint32_t name, ++ const struct wl_interface *interface, ++ uint32_t version) ++{ ++ GdkDisplay *gdk_display = gtk_widget_get_display(widget); ++ struct wl_display *display; ++ struct wl_registry *registry; ++ ++ if (!GDK_IS_WAYLAND_DISPLAY(gdk_display)) { ++ return NULL; ++ } ++ ++ display = gdk_wayland_display_get_wl_display(gdk_display); ++ registry = wl_display_get_registry(display); ++ ++ return wl_registry_bind(registry, name, interface, version); ++} ++ ++static void ++gtk_wl_registry_add_listener(GtkWidget *widget, const struct wl_registry_listener *listener) ++{ ++ GdkDisplay *gdk_display = gtk_widget_get_display(widget); ++ struct wl_display *display; ++ struct wl_registry *registry; ++ ++ if (!GDK_IS_WAYLAND_DISPLAY(gdk_display)) { ++ return; ++ } ++ ++ display = gdk_wayland_display_get_wl_display(gdk_display); ++ registry = wl_display_get_registry(display); ++ wl_registry_add_listener(registry, listener, widget); ++ wl_display_roundtrip(display); ++} ++ ++ ++static void ++registry_handle_global(void *data, ++ struct wl_registry *registry, ++ uint32_t name, ++ const char *interface, ++ uint32_t version) ++{ ++ GtkWidget *widget = GTK_WIDGET(data); ++ ++ if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { ++ struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; ++ relative_pointer_manager = gtk_wl_registry_bind(widget, name, ++ &zwp_relative_pointer_manager_v1_interface, ++ 1); ++ g_object_set_data_full(G_OBJECT(widget), ++ "zwp_relative_pointer_manager_v1", ++ relative_pointer_manager, ++ (GDestroyNotify)zwp_relative_pointer_manager_v1_destroy); ++ } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { ++ struct zwp_pointer_constraints_v1 *pointer_constraints; ++ pointer_constraints = gtk_wl_registry_bind(widget, name, ++ &zwp_pointer_constraints_v1_interface, ++ 1); ++ g_object_set_data_full(G_OBJECT(widget), ++ "zwp_pointer_constraints_v1", ++ pointer_constraints, ++ (GDestroyNotify)zwp_pointer_constraints_v1_destroy); ++ } ++} ++ ++static void ++registry_handle_global_remove(void *data, ++ struct wl_registry *registry, ++ uint32_t name) ++{ ++} ++ ++static const struct wl_registry_listener registry_listener = { ++ registry_handle_global, ++ registry_handle_global_remove ++}; ++ ++void ++spice_wayland_extensions_init(GtkWidget *widget) ++{ ++ g_return_if_fail(GTK_IS_WIDGET(widget)); ++ ++ gtk_wl_registry_add_listener(widget, ®istry_listener); ++} ++ ++ ++static GdkDevice * ++spice_gdk_window_get_pointing_device(GdkWindow *window) ++{ ++ GdkDisplay *gdk_display = gdk_window_get_display(window); ++ ++ return gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display)); ++} ++ ++static struct zwp_relative_pointer_v1_listener relative_pointer_listener; ++ ++// NOTE this API works only on a single widget per application ++int ++spice_wayland_extensions_enable_relative_pointer(GtkWidget *widget, ++ void (*cb)(void *, ++ struct zwp_relative_pointer_v1 *, ++ uint32_t, uint32_t, ++ wl_fixed_t, wl_fixed_t, wl_fixed_t, wl_fixed_t)) ++{ ++ struct zwp_relative_pointer_v1 *relative_pointer; ++ ++ g_return_val_if_fail(GTK_IS_WIDGET(widget), -1); ++ ++ relative_pointer = g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_v1"); ++ ++ if (relative_pointer == NULL) { ++ struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; ++ GdkWindow *window = gtk_widget_get_window(widget); ++ struct wl_pointer *pointer; ++ ++ relative_pointer_manager = g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_manager_v1"); ++ if (relative_pointer_manager == NULL) ++ return -1; ++ ++ pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window)); ++ relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(relative_pointer_manager, ++ pointer); ++ ++ relative_pointer_listener.relative_motion = cb; ++ zwp_relative_pointer_v1_add_listener(relative_pointer, ++ &relative_pointer_listener, ++ widget); ++ ++ g_object_set_data_full(G_OBJECT(widget), ++ "zwp_relative_pointer_v1", ++ relative_pointer, ++ (GDestroyNotify)zwp_relative_pointer_v1_destroy); ++ } ++ ++ return 0; ++} ++ ++int spice_wayland_extensions_disable_relative_pointer(GtkWidget *widget) ++{ ++ g_return_val_if_fail(GTK_IS_WIDGET(widget), -1); ++ ++ /* This will call zwp_relative_pointer_v1_destroy() and stop relative ++ * movement */ ++ g_object_set_data(G_OBJECT(widget), "zwp_relative_pointer_v1", NULL); ++ ++ return 0; ++} ++ ++static struct zwp_locked_pointer_v1_listener locked_pointer_listener; ++ ++// NOTE this API works only on a single widget per application ++int ++spice_wayland_extensions_lock_pointer(GtkWidget *widget, ++ void (*lock_cb)(void *, struct zwp_locked_pointer_v1 *), ++ void (*unlock_cb)(void *, struct zwp_locked_pointer_v1 *)) ++{ ++ struct zwp_pointer_constraints_v1 *pointer_constraints; ++ struct zwp_locked_pointer_v1 *locked_pointer; ++ GdkWindow *window; ++ struct wl_pointer *pointer; ++ ++ g_return_val_if_fail(GTK_IS_WIDGET(widget), -1); ++ ++ pointer_constraints = g_object_get_data(G_OBJECT(widget), "zwp_pointer_constraints_v1"); ++ locked_pointer = g_object_get_data(G_OBJECT(widget), "zwp_locked_pointer_v1"); ++ if (locked_pointer != NULL) { ++ /* A previous lock already in place */ ++ return 0; ++ } ++ ++ window = gtk_widget_get_window(widget); ++ pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window)); ++ locked_pointer = zwp_pointer_constraints_v1_lock_pointer(pointer_constraints, ++ gdk_wayland_window_get_wl_surface(window), ++ pointer, ++ NULL, ++ ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); ++ if (lock_cb || unlock_cb) { ++ locked_pointer_listener.locked = lock_cb; ++ locked_pointer_listener.unlocked = unlock_cb; ++ zwp_locked_pointer_v1_add_listener(locked_pointer, ++ &locked_pointer_listener, ++ widget); ++ } ++ g_object_set_data_full(G_OBJECT(widget), ++ "zwp_locked_pointer_v1", ++ locked_pointer, ++ (GDestroyNotify)zwp_locked_pointer_v1_destroy); ++ ++ return 0; ++} ++ ++int ++spice_wayland_extensions_unlock_pointer(GtkWidget *widget) ++{ ++ g_return_val_if_fail(GTK_IS_WIDGET(widget), -1); ++ ++ g_object_set_data(G_OBJECT(widget), "zwp_locked_pointer_v1", NULL); ++ ++ return 0; ++} +diff --git a/src/wayland-extensions.h b/src/wayland-extensions.h +new file mode 100644 +index 0000000..bf34044 +--- /dev/null ++++ b/src/wayland-extensions.h +@@ -0,0 +1,32 @@ ++/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* ++ Copyright (C) 2017 Red Hat, Inc. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, see . ++*/ ++#pragma once ++ ++#include ++ ++void spice_wayland_extensions_init(GtkWidget *widget); ++int spice_wayland_extensions_enable_relative_pointer(GtkWidget *widget, ++ void (*cb)(void *, ++ struct zwp_relative_pointer_v1 *, ++ uint32_t, uint32_t, ++ wl_fixed_t, wl_fixed_t, wl_fixed_t, wl_fixed_t)); ++int spice_wayland_extensions_disable_relative_pointer(GtkWidget *widget); ++int spice_wayland_extensions_lock_pointer(GtkWidget *widget, ++ void (*lock_cb)(void *, struct zwp_locked_pointer_v1 *), ++ void (*unlock_cb)(void *, struct zwp_locked_pointer_v1 *)); ++int spice_wayland_extensions_unlock_pointer(GtkWidget *widget); +-- +2.26.2 + diff --git a/SOURCES/0007-spice-gtk-save-mouse-button-state-on-mouse-click.patch b/SOURCES/0007-spice-gtk-save-mouse-button-state-on-mouse-click.patch new file mode 100644 index 0000000..e3618ab --- /dev/null +++ b/SOURCES/0007-spice-gtk-save-mouse-button-state-on-mouse-click.patch @@ -0,0 +1,77 @@ +From 7e0f7bda8d5817c0cbfc56be4cf3727b98f223f4 Mon Sep 17 00:00:00 2001 +From: Francesco Giudici +Date: Mon, 20 Apr 2020 14:37:57 +0200 +Subject: [PATCH 7/9] spice-gtk: save mouse button state on mouse click + +This will be used later to couple it with relative mouse movement under +Wayland: we just get the mouse movement from the Wayland protocols +callback. + +Signed-off-by: Francesco Giudici +Acked-by: Frediano Ziglio +(cherry picked from commit 0fe70950524c28d383f34876c66107117581c72f) +--- + src/spice-widget-priv.h | 1 + + src/spice-widget.c | 22 +++++++++++++++++++++- + 2 files changed, 22 insertions(+), 1 deletion(-) + +diff --git a/src/spice-widget-priv.h b/src/spice-widget-priv.h +index 436c802..0b282f5 100644 +--- a/src/spice-widget-priv.h ++++ b/src/spice-widget-priv.h +@@ -96,6 +96,7 @@ struct _SpiceDisplayPrivate { + SpiceSmartcardChannel *smartcard; + + enum SpiceMouseMode mouse_mode; ++ int mouse_button_mask; + int mouse_grab_active; + bool mouse_have_pointer; + GdkCursor *mouse_cursor; +diff --git a/src/spice-widget.c b/src/spice-widget.c +index 8b91f52..7700f47 100644 +--- a/src/spice-widget.c ++++ b/src/spice-widget.c +@@ -1954,7 +1954,21 @@ static int button_gdk_to_spice(guint gdk) + }; + + if (gdk < SPICE_N_ELEMENTS(map)) { +- return map [ gdk ]; ++ return map[gdk]; ++ } ++ return 0; ++} ++ ++static int button_gdk_to_spice_mask(guint gdk) ++{ ++ static const int map[] = { ++ [1] = SPICE_MOUSE_BUTTON_MASK_LEFT, ++ [2] = SPICE_MOUSE_BUTTON_MASK_MIDDLE, ++ [3] = SPICE_MOUSE_BUTTON_MASK_RIGHT, ++ }; ++ ++ if (gdk < SPICE_N_ELEMENTS(map)) { ++ return map[gdk]; + } + return 0; + } +@@ -2160,11 +2174,17 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button) + spice_inputs_channel_button_press(d->inputs, + button_gdk_to_spice(button->button), + button_mask_gdk_to_spice(button->state)); ++ /* Save the mouse button mask to couple it with Wayland movement */ ++ d->mouse_button_mask = button_mask_gdk_to_spice(button->state); ++ d->mouse_button_mask |= button_gdk_to_spice_mask(button->button); + break; + case GDK_BUTTON_RELEASE: + spice_inputs_channel_button_release(d->inputs, + button_gdk_to_spice(button->button), + button_mask_gdk_to_spice(button->state)); ++ /* Save the mouse button mask to couple it with Wayland movement */ ++ d->mouse_button_mask = button_mask_gdk_to_spice(button->state); ++ d->mouse_button_mask ^= button_gdk_to_spice_mask(button->button); + break; + default: + break; +-- +2.26.2 + diff --git a/SOURCES/0008-wayland-fix-mouse-lock-in-server-mode.patch b/SOURCES/0008-wayland-fix-mouse-lock-in-server-mode.patch new file mode 100644 index 0000000..5c5253e --- /dev/null +++ b/SOURCES/0008-wayland-fix-mouse-lock-in-server-mode.patch @@ -0,0 +1,181 @@ +From 16cdb3a8cdd60da9eef12d1d10a01d4eed21a5a7 Mon Sep 17 00:00:00 2001 +From: Francesco Giudici +Date: Mon, 20 Apr 2020 10:25:03 +0200 +Subject: [PATCH 8/9] wayland: fix mouse lock in server mode + +We can now properly manage mouse pointer lock by using the wayland protocols. +Includes fix from Frediano Ziglio to manage switching from mouse in +server mode to client mode without releasing the pointer +(see https://gitlab.freedesktop.org/fziglio/spice-gtk/-/commit/c14b047e45) + +Signed-off-by: Francesco Giudici +Acked-by: Frediano Ziglio +(cherry picked from commit dd7015d57ca936cc81060b1e2a09d3afc1478d4a) +--- + src/spice-widget.c | 85 +++++++++++++++++++++++++++++++++++----------- + 1 file changed, 66 insertions(+), 19 deletions(-) + +diff --git a/src/spice-widget.c b/src/spice-widget.c +index 7700f47..6cfc72f 100644 +--- a/src/spice-widget.c ++++ b/src/spice-widget.c +@@ -34,7 +34,13 @@ + #endif + #ifdef GDK_WINDOWING_WAYLAND + #include ++#ifdef HAVE_WAYLAND_PROTOCOLS ++#include "pointer-constraints-unstable-v1-client-protocol.h" ++#include "relative-pointer-unstable-v1-client-protocol.h" ++#include "wayland-extensions.h" + #endif ++#endif ++ + #ifdef G_OS_WIN32 + #include + #include +@@ -698,6 +704,11 @@ static void spice_display_init(SpiceDisplay *display) + + d->grabseq = spice_grab_sequence_new_from_string("Control_L+Alt_L"); + d->activeseq = g_new0(gboolean, d->grabseq->nkeysyms); ++ ++#ifdef HAVE_WAYLAND_PROTOCOLS ++ if GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(widget)) ++ spice_wayland_extensions_init(widget); ++#endif + } + + static void +@@ -900,7 +911,7 @@ static void ungrab_keyboard(SpiceDisplay *display) + * We simply issue a gdk_seat_ungrab() followed immediately by another + * gdk_seat_grab() on the pointer if the pointer grab is to be kept. + */ +- if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { ++ if (GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(GTK_WIDGET(display)))) { + SpiceDisplayPrivate *d = display->priv; + + gdk_seat_ungrab(seat); +@@ -1055,15 +1066,49 @@ error: + } + #endif + ++#ifdef HAVE_WAYLAND_PROTOCOLS ++static void ++relative_pointer_handle_relative_motion(void *data, ++ struct zwp_relative_pointer_v1 *pointer, ++ uint32_t time_hi, ++ uint32_t time_lo, ++ wl_fixed_t dx_w, ++ wl_fixed_t dy_w, ++ wl_fixed_t dx_unaccel_w, ++ wl_fixed_t dy_unaccel_w) ++{ ++ SpiceDisplay *display = SPICE_DISPLAY(data); ++ GtkWidget *widget = GTK_WIDGET(display); ++ SpiceDisplayPrivate *d = display->priv; ++ ++ if (!d->inputs) ++ return; ++ if (d->disable_inputs) ++ return; ++ /* mode changed to client in the meantime */ ++ if (d->mouse_mode != SPICE_MOUSE_MODE_SERVER) { ++ spice_wayland_extensions_disable_relative_pointer(widget); ++ spice_wayland_extensions_unlock_pointer(widget); ++ return; ++ } ++ ++ spice_inputs_channel_motion(d->inputs, ++ wl_fixed_to_int(dx_unaccel_w), ++ wl_fixed_to_int(dy_unaccel_w), ++ d->mouse_button_mask); ++} ++#endif ++ + static gboolean do_pointer_grab(SpiceDisplay *display) + { ++ GtkWidget *widget = GTK_WIDGET(display); + SpiceDisplayPrivate *d = display->priv; +- GdkWindow *window = GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(display))); ++ GdkWindow *window = GDK_WINDOW(gtk_widget_get_window(widget)); + GdkGrabStatus status; + GdkCursor *blank = spice_display_get_blank_cursor(display); + gboolean grab_successful = FALSE; + +- if (!gtk_widget_get_realized(GTK_WIDGET(display))) ++ if (!gtk_widget_get_realized(widget)) + goto end; + + #ifdef G_OS_WIN32 +@@ -1080,6 +1125,14 @@ static gboolean do_pointer_grab(SpiceDisplay *display) + NULL, + NULL, + NULL); ++ ++#ifdef HAVE_WAYLAND_PROTOCOLS ++ if (GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(widget))) { ++ spice_wayland_extensions_enable_relative_pointer(widget, relative_pointer_handle_relative_motion); ++ spice_wayland_extensions_lock_pointer(widget, NULL, NULL); ++ } ++#endif ++ + grab_successful = (status == GDK_GRAB_SUCCESS); + if (!grab_successful) { + d->mouse_grab_active = false; +@@ -1204,7 +1257,8 @@ static void ungrab_pointer(SpiceDisplay *display) + * immediately by another gdk_seat_grab() on the keyboard if the + * keyboard grab is to be kept. + */ +- if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { ++ if (GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(GTK_WIDGET(display)))) { ++ GtkWidget *widget = GTK_WIDGET(display); + SpiceDisplayPrivate *d = display->priv; + + gdk_seat_ungrab(seat); +@@ -1213,7 +1267,7 @@ static void ungrab_pointer(SpiceDisplay *display) + GdkGrabStatus status; + + status = gdk_seat_grab(seat, +- gtk_widget_get_window(GTK_WIDGET(display)), ++ gtk_widget_get_window(widget), + GDK_SEAT_CAPABILITY_KEYBOARD, + FALSE, + NULL, +@@ -1224,6 +1278,12 @@ static void ungrab_pointer(SpiceDisplay *display) + g_warning("keyboard grab failed %u", status); + d->keyboard_grab_active = false; + } ++#ifdef HAVE_WAYLAND_PROTOCOLS ++ if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER) { ++ spice_wayland_extensions_disable_relative_pointer(widget); ++ spice_wayland_extensions_unlock_pointer(widget); ++ } ++#endif + } + + return; +@@ -1860,21 +1920,8 @@ static gboolean leave_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC + + DISPLAY_DEBUG(display, "%s", __FUNCTION__); + +- if (d->mouse_grab_active) { +-#ifdef GDK_WINDOWING_WAYLAND +- /* On Wayland, there is no active pointer grab, so once the pointer +- * has left the window, the events are routed to the window with +- * pointer focus instead of ours, in which case we should just +- * ungrab to avoid nasty side effects. */ +- if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { +- GdkWindow *window = gtk_widget_get_window(widget); +- +- if (window == crossing->window) +- try_mouse_ungrab(display); +- } +-#endif ++ if (d->mouse_grab_active) + return true; +- } + + d->mouse_have_pointer = false; + spice_gtk_session_set_mouse_has_pointer(d->gtk_session, false); +-- +2.26.2 + diff --git a/SOURCES/0009-channel-main-Handle-some-detailed-error-for-VD_AGENT.patch b/SOURCES/0009-channel-main-Handle-some-detailed-error-for-VD_AGENT.patch new file mode 100644 index 0000000..9f1255c --- /dev/null +++ b/SOURCES/0009-channel-main-Handle-some-detailed-error-for-VD_AGENT.patch @@ -0,0 +1,54 @@ +From 769465494c5e3e4299a2dfdf6e0c835be4e04ab7 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Thu, 19 Mar 2020 06:12:34 +0000 +Subject: [PATCH 9/9] channel-main: Handle some detailed error for + VD_AGENT_FILE_XFER_STATUS_ERROR + +Detect presence of detail on VD_AGENT_FILE_XFER_STATUS_ERROR and +if the error is supported give more detailed information, otherwise +fallback to the old generic error. + +Signed-off-by: Frediano Ziglio +Acked-by: Victor Toso +(cherry picked from commit d79b05dcff83792c8415cafbb288d614de9d3101) +--- + src/channel-main.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/src/channel-main.c b/src/channel-main.c +index c4fe02b..d845fda 100644 +--- a/src/channel-main.c ++++ b/src/channel-main.c +@@ -1913,10 +1913,26 @@ static void main_agent_handle_xfer_status(SpiceMainChannel *channel, + error = g_error_new_literal(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED, + _("The spice agent cancelled the file transfer")); + break; +- case VD_AGENT_FILE_XFER_STATUS_ERROR: +- error = g_error_new_literal(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED, +- _("The spice agent reported an error during the file transfer")); ++ case VD_AGENT_FILE_XFER_STATUS_ERROR: { ++ const VDAgentFileXferStatusError *err = (VDAgentFileXferStatusError *) msg->data; ++ if (test_agent_cap(channel, VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS) && ++ msg_hdr->size >= sizeof(*msg) + sizeof(*err) && ++ err->error_type == VD_AGENT_FILE_XFER_STATUS_ERROR_GLIB_IO) { ++ ++ switch (err->error_code) { ++ case G_IO_ERROR_INVALID_FILENAME: ++ error = g_error_new_literal(G_IO_ERROR, err->error_code, ++ _("Invalid filename of transferred file")); ++ break; ++ } ++ } ++ if (error == NULL) { ++ error = g_error_new_literal(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED, ++ _("The spice agent reported an error " ++ "during the file transfer")); ++ } + break; ++ } + case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE: { + const VDAgentFileXferStatusNotEnoughSpace *err = + (VDAgentFileXferStatusNotEnoughSpace*) msg->data; +-- +2.26.2 + diff --git a/SOURCES/0010-quic-Check-we-have-some-data-to-start-decoding-quic-.patch b/SOURCES/0010-quic-Check-we-have-some-data-to-start-decoding-quic-.patch new file mode 100644 index 0000000..896bfbc --- /dev/null +++ b/SOURCES/0010-quic-Check-we-have-some-data-to-start-decoding-quic-.patch @@ -0,0 +1,34 @@ +From d9cc2d4659950df230dfe30e5445b91d4c15604e Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Wed, 29 Apr 2020 15:09:13 +0100 +Subject: [PATCH spice-common 1/4] quic: Check we have some data to start + decoding quic image + +All paths already pass some data to quic_decode_begin but for the +test check it, it's not that expensive test. +Checking for not 0 is enough, all other words will potentially be +read calling more_io_words but we need one to avoid a potential +initial buffer overflow or deferencing an invalid pointer. + +Signed-off-by: Frediano Ziglio +Acked-by: Uri Lublin +--- + common/quic.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/subprojects/spice-common/common/quic.c b/subprojects/spice-common/common/quic.c +index 55a5d6c..e03f3af 100644 +--- a/subprojects/spice-common/common/quic.c ++++ b/subprojects/spice-common/common/quic.c +@@ -1136,7 +1136,7 @@ int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_w + int channels; + int bpc; + +- if (!encoder_reset(encoder, io_ptr, io_ptr_end)) { ++ if (!num_io_words || !encoder_reset(encoder, io_ptr, io_ptr_end)) { + return QUIC_ERROR; + } + +-- +2.25.4 + diff --git a/SOURCES/0011-quic-Check-image-size-in-quic_decode_begin.patch b/SOURCES/0011-quic-Check-image-size-in-quic_decode_begin.patch new file mode 100644 index 0000000..ea52954 --- /dev/null +++ b/SOURCES/0011-quic-Check-image-size-in-quic_decode_begin.patch @@ -0,0 +1,48 @@ +From 19cd6fe85610b424349db2d97e2dd0e2761a4a05 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Wed, 29 Apr 2020 15:10:24 +0100 +Subject: [PATCH spice-common 2/4] quic: Check image size in quic_decode_begin + +Avoid some overflow in code due to images too big or +negative numbers. + +Signed-off-by: Frediano Ziglio +Acked-by: Uri Lublin +--- + common/quic.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/subprojects/spice-common/common/quic.c b/subprojects/spice-common/common/quic.c +index e03f3af..890f128 100644 +--- a/subprojects/spice-common/common/quic.c ++++ b/subprojects/spice-common/common/quic.c +@@ -56,6 +56,9 @@ typedef uint8_t BYTE; + #define MINwminext 1 + #define MAXwminext 100000000 + ++/* Maximum image size in pixels, mainly to avoid possible integer overflows */ ++#define SPICE_MAX_IMAGE_SIZE (512 * 1024 * 1024 - 1) ++ + typedef struct QuicFamily { + unsigned int nGRcodewords[MAXNUMCODES]; /* indexed by code number, contains number of + unmodified GR codewords in the code */ +@@ -1165,6 +1168,16 @@ int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_w + height = encoder->io_word; + decode_eat32bits(encoder); + ++ if (width <= 0 || height <= 0) { ++ encoder->usr->warn(encoder->usr, "invalid size\n"); ++ return QUIC_ERROR; ++ } ++ ++ /* avoid too big images */ ++ if ((uint64_t) width * height > SPICE_MAX_IMAGE_SIZE) { ++ encoder->usr->error(encoder->usr, "image too large\n"); ++ } ++ + quic_image_params(encoder, type, &channels, &bpc); + + if (!encoder_reset_channels(encoder, channels, width, bpc)) { +-- +2.25.4 + diff --git a/SOURCES/0012-quic-Check-RLE-lengths.patch b/SOURCES/0012-quic-Check-RLE-lengths.patch new file mode 100644 index 0000000..aebad02 --- /dev/null +++ b/SOURCES/0012-quic-Check-RLE-lengths.patch @@ -0,0 +1,35 @@ +From d45a4954d73b41a255b8b4ec57c01ae87ec2936e Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Wed, 29 Apr 2020 15:11:38 +0100 +Subject: [PATCH spice-common 3/4] quic: Check RLE lengths + +Avoid buffer overflows decoding images. On compression we compute +lengths till end of line so it won't cause regressions. +Proved by fuzzing the code. + +Signed-off-by: Frediano Ziglio +Acked-by: Uri Lublin +--- + common/quic_tmpl.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/subprojects/spice-common/common/quic_tmpl.c b/subprojects/spice-common/common/quic_tmpl.c +index f0a4927..11e09f5 100644 +--- a/subprojects/spice-common/common/quic_tmpl.c ++++ b/subprojects/spice-common/common/quic_tmpl.c +@@ -570,7 +570,11 @@ static void FNAME_DECL(uncompress_row_seg)(const PIXEL * const prev_row, + do_run: + state->waitcnt = stopidx - i; + run_index = i; +- run_end = i + decode_state_run(encoder, state); ++ run_end = decode_state_run(encoder, state); ++ if (run_end < 0 || run_end > (end - i)) { ++ encoder->usr->error(encoder->usr, "wrong RLE\n"); ++ } ++ run_end += i; + + for (; i < run_end; i++) { + UNCOMPRESS_PIX_START(&cur_row[i]); +-- +2.25.4 + diff --git a/SOURCES/0013-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch b/SOURCES/0013-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch new file mode 100644 index 0000000..ca9f80f --- /dev/null +++ b/SOURCES/0013-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch @@ -0,0 +1,35 @@ +From 57c6e6b00247ad289a27648213d7ad2306fe3931 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Thu, 30 Apr 2020 10:19:09 +0100 +Subject: [PATCH spice-common 4/4] quic: Avoid possible buffer overflow in + find_bucket + +Proved by fuzzing the code. + +Signed-off-by: Frediano Ziglio +Acked-by: Uri Lublin +--- + common/quic_family_tmpl.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/subprojects/spice-common/common/quic_family_tmpl.c b/subprojects/spice-common/common/quic_family_tmpl.c +index 8a5f7d2..6cc051b 100644 +--- a/subprojects/spice-common/common/quic_family_tmpl.c ++++ b/subprojects/spice-common/common/quic_family_tmpl.c +@@ -103,7 +103,12 @@ static s_bucket *FNAME(find_bucket)(Channel *channel, const unsigned int val) + { + spice_extra_assert(val < (0x1U << BPC)); + +- return channel->_buckets_ptrs[val]; ++ /* The and (&) here is to avoid buffer overflows in case of garbage or malicious ++ * attempts. Is much faster then using comparisons and save us from such situations. ++ * Note that on normal build the check above won't be compiled as this code path ++ * is pretty hot and would cause speed regressions. ++ */ ++ return channel->_buckets_ptrs[val & ((1U << BPC) - 1)]; + } + + #undef FNAME +-- +2.25.4 + diff --git a/SOURCES/spice-gtk-0.37.tar.bz2.sig b/SOURCES/spice-gtk-0.37.tar.bz2.sig deleted file mode 100644 index bb32b06..0000000 Binary files a/SOURCES/spice-gtk-0.37.tar.bz2.sig and /dev/null differ diff --git a/SOURCES/spice-gtk-0.38.tar.xz.sig b/SOURCES/spice-gtk-0.38.tar.xz.sig new file mode 100644 index 0000000..eab14d2 Binary files /dev/null and b/SOURCES/spice-gtk-0.38.tar.xz.sig differ diff --git a/SPECS/spice-gtk.spec b/SPECS/spice-gtk.spec index 273e812..4716615 100644 --- a/SPECS/spice-gtk.spec +++ b/SPECS/spice-gtk.spec @@ -1,28 +1,37 @@ #define _version_suffix Name: spice-gtk -Version: 0.37 -Release: 1%{?dist}.2 +Version: 0.38 +Release: 3%{?dist} Summary: A GTK+ widget for SPICE clients Group: System Environment/Libraries License: LGPLv2+ URL: http://spice-space.org/page/Spice-Gtk #VCS: git:git://anongit.freedesktop.org/spice/spice-gtk -Source0: https://www.spice-space.org/download/gtk/%{name}-%{version}%{?_version_suffix}.tar.bz2 -Source1: https://www.spice-space.org/download/gtk/%{name}-%{version}%{?_version_suffix}.tar.bz2.sig +Source0: https://www.spice-space.org/download/gtk/%{name}-%{version}%{?_version_suffix}.tar.xz +Source1: https://www.spice-space.org/download/gtk/%{name}-%{version}%{?_version_suffix}.tar.xz.sig Source2: victortoso-E37A484F.keyring - -Patch0001: 0001-quic-Check-we-have-some-data-to-start-decoding-quic-.patch -Patch0002: 0002-quic-Check-image-size-in-quic_decode_begin.patch -Patch0003: 0003-quic-Check-RLE-lengths.patch -Patch0004: 0004-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch - +Patch0001: 0001-channel-main-Avoid-macro-side-effects.patch +Patch0002: 0002-channel-main-Check-proper-size-and-caps-handling-VD_.patch +Patch0003: 0003-build-sys-bump-polkit-requirement-to-0.101.patch +Patch0004: 0004-spice-channel-Read-all-available-data-from-SASL-buff.patch +Patch0005: 0005-meson-add-wayland-protocols.patch +Patch0006: 0006-wayland-add-wayland-extensions-functions.patch +Patch0007: 0007-spice-gtk-save-mouse-button-state-on-mouse-click.patch +Patch0008: 0008-wayland-fix-mouse-lock-in-server-mode.patch +Patch0009: 0009-channel-main-Handle-some-detailed-error-for-VD_AGENT.patch +Patch0010: 0010-quic-Check-we-have-some-data-to-start-decoding-quic-.patch +Patch0011: 0011-quic-Check-image-size-in-quic_decode_begin.patch +Patch0012: 0012-quic-Check-RLE-lengths.patch +Patch0013: 0013-quic-Avoid-possible-buffer-overflow-in-find_bucket.patch + +BuildRequires: meson BuildRequires: git-core BuildRequires: gnupg2 BuildRequires: intltool BuildRequires: usbredir-devel >= 0.7.1 -BuildRequires: libusb1-devel >= 1.0.16 +BuildRequires: libusb1-devel >= 1.0.21 BuildRequires: libgudev1-devel BuildRequires: pixman-devel libjpeg-turbo-devel BuildRequires: celt051-devel pulseaudio-libs-devel opus-devel @@ -39,10 +48,11 @@ BuildRequires: libsoup-devel >= 2.49.91 BuildRequires: lz4-devel BuildRequires: gtk3-devel BuildRequires: json-glib-devel -BuildRequires: spice-protocol >= 0.14.0 +BuildRequires: spice-protocol >= 0.14.2 BuildRequires: gstreamer1-devel >= 1.10.0 gstreamer1-plugins-base-devel >= 1.10.0 -BuildRequires: python3-devel -BuildRequires: libdrm-devel +BuildRequires: python3-devel python3-six python3-pyparsing +BuildRequires: wayland-protocols-devel >= 1.17 +BuildRequires: libwayland-server libwayland-cursor libwayland-client Obsoletes: spice-gtk-python < 0.32 Requires: spice-glib%{?_isa} = %{version}-%{release} @@ -119,23 +129,19 @@ gpgv2 --quiet --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0} %autosetup -S git_am %build -%configure \ - --enable-celt051 \ - --enable-gtk-doc \ - --with-usb-acl-helper-dir=%{_libexecdir}/spice-gtk-%{_arch}/ \ - --enable-lz4 \ - --enable-vala \ - --disable-webdav \ - --disable-werror -make %{?_smp_mflags} V=1 +%meson \ + -Dgtk_doc=enabled \ + -Dusb-acl-helper-dir=%{_libexecdir}/spice-gtk-%{_arch}/ \ + -Dlz4=enabled \ + -Dvapi=enabled \ + -Dcelt051=disabled \ + -Dwayland-protocols=enabled \ + -Dwebdav=disabled +%meson_build %install -make install DESTDIR=%{buildroot} - -rm -f %{buildroot}%{_libdir}/*.a -rm -f %{buildroot}%{_libdir}/*.la - +%meson_install %find_lang %{name} @@ -187,9 +193,23 @@ rm -f %{buildroot}%{_libdir}/*.la %{_bindir}/spicy-stats %changelog -* Wed Sep 2 2020 Frediano Ziglio - 0.37-1.2 +* Mon Jun 1 2020 Frediano Ziglio - 0.38-3 - Fix multiple buffer overflows in QUIC decoding code - Resolves: CVE-2020-14355 + Resolves: rhbz#1842472 + +* Tue May 20 2020 Victor Toso - 0.38-2 +- Brings some post releases fixes and disables celt051 that is + deprecated in spice-protocol 0.14.2 +- Possibly related to rhbz#1688737 rhbz#1746239 + Related: rhbz#1817471 +- Fix mouse pointer on relative mouse mode + Resolves: rhbz#1674311 +- Handle detailed errors from guest agent + Related: rhbz#1520393 + +* Tue May 05 2020 Victor Toso - 0.38-1 +- Update to 0.38 + Resolves: rhbz#1817471 * Fri May 17 2019 Victor Toso - 0.37-1 - Update to 0.37