import usbredir-0.12.0-4.el8

c8 imports/c8/usbredir-0.12.0-4.el8
CentOS Sources 2 years ago committed by MSVSphere Packaging Team
commit bdbd151ca0

1
.gitignore vendored

@ -0,0 +1 @@
SOURCES/usbredir-0.12.0.tar.xz

@ -0,0 +1 @@
70940f6dc409b3bdb9ee98f24690c438f1ae999e SOURCES/usbredir-0.12.0.tar.xz

@ -0,0 +1,193 @@
From 6bf41a231b445ac5190c32e281b698b1ee5379b4 Mon Sep 17 00:00:00 2001
From: Victor Toso <victortoso@redhat.com>
Date: Fri, 24 Jun 2022 23:29:08 +0200
Subject: [PATCH 1/2] usbredirparser: Fix unserialize on pristine check
Content-type: text/plain
As mentioned in the bug below, the user is trying to migrate QEMU and
it is failing on the unserialization of usbredirparser at the target
host. The user does not have USB attached to the VM at all.
I've added a test that shows that serialization is currently broken.
It fails at the 'pristine' check in usbredirparser_unserialize().
This check was added with e37d86c "Skip empty write buffers when
unserializing parser" and restricted further with 186c4c7 "Avoid
memory leak from ill-formatted serialization data"
The issue here is that usbredirparser's initialization sets some
fields and thus it isn't guaranteed to be pristine.
The parser's basic data is:
| write_buf_count ... : 1
| write_buf ........ : 0xbc03e0
| write_buf_total_size: 80
| data ............. : (nil)
| header_read: ...... : 0
| type_header_read .. : 0
| data_read: ........ : 0
The current fix is to to ignore write_buf checks as, again, they are
not guaranteed to be pristine. usbredirparser library should properly
overwrite them when unserializing the data and if there were pending
buffers, they should be freed.
Related: https://bugzilla.redhat.com/show_bug.cgi?id=2096008
Signed-off-by: Victor Toso <victortoso@redhat.com>
---
tests/meson.build | 1 +
tests/serializer.c | 113 ++++++++++++++++++++++++++++++++
usbredirparser/usbredirparser.c | 4 +-
3 files changed, 115 insertions(+), 3 deletions(-)
create mode 100644 tests/serializer.c
diff --git a/tests/meson.build b/tests/meson.build
index 0d4397b..2a179c9 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,5 +1,6 @@
tests = [
'filter',
+ 'serializer',
]
deps = dependency('glib-2.0')
diff --git a/tests/serializer.c b/tests/serializer.c
new file mode 100644
index 0000000..4bd669e
--- /dev/null
+++ b/tests/serializer.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2022 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 <http://www.gnu.org/licenses/>.
+*/
+#include "config.h"
+
+#define G_LOG_DOMAIN "serializer"
+#define G_LOG_USE_STRUCTURED
+
+#include "usbredirparser.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <glib.h>
+#include <stdlib.h>
+
+
+static void
+log_cb(void *priv, int level, const char *msg)
+{
+ GLogLevelFlags glog_level;
+
+ switch(level) {
+ case usbredirparser_error:
+ glog_level = G_LOG_LEVEL_ERROR;
+ break;
+ case usbredirparser_warning:
+ glog_level = G_LOG_LEVEL_WARNING;
+ break;
+ case usbredirparser_info:
+ glog_level = G_LOG_LEVEL_INFO;
+ break;
+ case usbredirparser_debug:
+ case usbredirparser_debug_data:
+ glog_level = G_LOG_LEVEL_DEBUG;
+ break;
+ default:
+ g_warn_if_reached();
+ return;
+ }
+ g_log_structured(G_LOG_DOMAIN, glog_level, "MESSAGE", msg);
+}
+
+static struct usbredirparser *
+get_usbredirparser(void)
+{
+ struct usbredirparser *parser = usbredirparser_create();
+ g_assert_nonnull(parser);
+
+ uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
+ /* Typical caps set by usbredirhost */
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_device_disconnect_ack);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
+#if LIBUSBX_API_VERSION >= 0x01000103
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+#endif
+ int parser_flags = usbredirparser_fl_usb_host;
+
+ parser->log_func = log_cb;
+ usbredirparser_init(parser,
+ PACKAGE_STRING,
+ caps,
+ USB_REDIR_CAPS_SIZE,
+ parser_flags);
+ return parser;
+}
+
+static void
+simple (gconstpointer user_data)
+{
+ uint8_t *state = NULL;
+ int ret, len = -1;
+
+ struct usbredirparser *source = get_usbredirparser();
+ ret = usbredirparser_serialize(source, &state, &len);
+ g_assert_cmpint(ret, ==, 0);
+
+ struct usbredirparser *target = get_usbredirparser();
+ ret = usbredirparser_unserialize(target, state, len);
+ g_assert_cmpint(ret, ==, 0);
+
+ g_clear_pointer(&state, free);
+ usbredirparser_destroy(source);
+ usbredirparser_destroy(target);
+}
+
+int
+main(int argc, char **argv)
+{
+ setlocale(LC_ALL, "");
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_data_func("/serializer/serialize-and-unserialize", NULL, simple);
+
+ return g_test_run();
+}
diff --git a/usbredirparser/usbredirparser.c b/usbredirparser/usbredirparser.c
index cd1136b..a5dd0e7 100644
--- a/usbredirparser/usbredirparser.c
+++ b/usbredirparser/usbredirparser.c
@@ -1816,9 +1816,7 @@ int usbredirparser_unserialize(struct usbredirparser *parser_pub,
return -1;
}
- if (!(parser->write_buf_count == 0 && parser->write_buf == NULL &&
- parser->write_buf_total_size == 0 &&
- parser->data == NULL && parser->header_read == 0 &&
+ if (!(parser->data == NULL && parser->header_read == 0 &&
parser->type_header_read == 0 && parser->data_read == 0)) {
ERROR("unserialization must use a pristine parser");
usbredirparser_assert_invariants(parser);
--
2.37.1

@ -0,0 +1,63 @@
From b93c4cae1aebda786a478677d6364308e4579ade Mon Sep 17 00:00:00 2001
From: Victor Toso <victortoso@redhat.com>
Date: Sat, 25 Jun 2022 00:29:12 +0200
Subject: [PATCH 2/2] usbredirparser: reset parser's fields on unserialize
Content-type: text/plain
This is a followup from previous commit and fixes the following leak.
| 104 (24 direct, 80 indirect) bytes in 1 blocks are definitely lost in loss record 15 of 19
| at 0x484A464: calloc (vg_replace_malloc.c:1328)
| by 0x485A238: usbredirparser_queue (usbredirparser.c:1235)
| by 0x485A571: usbredirparser_init (usbredirparser.c:227)
| by 0x40130B: get_usbredirparser (serializer.c:77)
| by 0x401379: simple (serializer.c:95)
| by 0x48FA3DD: ??? (in /usr/lib64/libglib-2.0.so.0.7200.2)
| by 0x48FA144: ??? (in /usr/lib64/libglib-2.0.so.0.7200.2)
| by 0x48FA8E1: g_test_run_suite (in /usr/lib64/libglib-2.0.so.0.7200.2)
| by 0x48FA94C: g_test_run (in /usr/lib64/libglib-2.0.so.0.7200.2)
| by 0x401161: main (serializer.c:112)
|
| LEAK SUMMARY:
| definitely lost: 24 bytes in 1 blocks
| indirectly lost: 80 bytes in 1 blocks
| possibly lost: 0 bytes in 0 blocks
| still reachable: 25,500 bytes in 17 blocks
| suppressed: 0 bytes in 0 blocks
| Reachable blocks (those to which a pointer was found) are not shown.
| To see them, rerun with: --leak-check=full --show-leak-kinds=all
Signed-off-by: Victor Toso <victortoso@redhat.com>
---
usbredirparser/usbredirparser.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/usbredirparser/usbredirparser.c b/usbredirparser/usbredirparser.c
index a5dd0e7..9bfc27c 100644
--- a/usbredirparser/usbredirparser.c
+++ b/usbredirparser/usbredirparser.c
@@ -1823,6 +1823,21 @@ int usbredirparser_unserialize(struct usbredirparser *parser_pub,
return -1;
}
+ {
+ /* We need to reset parser's state to receive unserialized
+ * data. */
+ struct usbredirparser_buf *wbuf = parser->write_buf;
+ while (wbuf) {
+ struct usbredirparser_buf *next_wbuf = wbuf->next;
+ free(wbuf->buf);
+ free(wbuf);
+ wbuf = next_wbuf;
+ }
+ parser->write_buf = NULL;
+ parser->write_buf_count = 0;
+ parser->write_buf_total_size = 0;
+ }
+
if (unserialize_int(parser, &state, &remain, &i, "length")) {
usbredirparser_assert_invariants(parser);
return -1;
--
2.37.1

@ -0,0 +1,135 @@
From e30b0ce5c46c0e2a75b6e38759876ba1369b2168 Mon Sep 17 00:00:00 2001
From: Frediano Ziglio <freddy77@gmail.com>
Date: Fri, 16 Sep 2022 20:14:28 +0100
Subject: [PATCH 3/8] Use typedef on redirect structure to simplify some
statements
Signed-off-by: Frediano Ziglio <freddy77@gmail.com>
---
tools/usbredirect.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/tools/usbredirect.c b/tools/usbredirect.c
index 98e5a8c..89a42a6 100644
--- a/tools/usbredirect.c
+++ b/tools/usbredirect.c
@@ -22,7 +22,7 @@
#include <gio/gwin32outputstream.h>
#endif
-struct redirect {
+typedef struct redirect {
struct {
int vendor;
int product;
@@ -40,7 +40,7 @@ struct redirect {
int watch_server_id;
GMainLoop *main_loop;
-};
+} redirect;
static bool
parse_opt_device(const char *device, int *vendor, int *product)
@@ -124,7 +124,7 @@ parse_opt_uri(const char *uri, char **adr, int *port)
return true;
}
-static struct redirect *
+static redirect *
parse_opts(int *argc, char ***argv)
{
char *device = NULL;
@@ -132,7 +132,7 @@ parse_opts(int *argc, char ***argv)
char *localaddr = NULL;
gboolean keepalive = FALSE;
gint verbosity = 0; /* none */
- struct redirect *self = NULL;
+ redirect *self = NULL;
GOptionEntry entries[] = {
{ "device", 0, 0, G_OPTION_ARG_STRING, &device, "Local USB device to be redirected", NULL },
@@ -161,7 +161,7 @@ parse_opts(int *argc, char ***argv)
goto end;
}
- self = g_new0(struct redirect, 1);
+ self = g_new0(redirect, 1);
if (!parse_opt_device(device, &self->device.vendor, &self->device.product)) {
g_printerr("Failed to parse device: '%s' - expected: vendor:product or busnum-devnum\n", device);
g_clear_pointer(&self, g_free);
@@ -201,7 +201,7 @@ end:
static gpointer
thread_handle_libusb_events(gpointer user_data)
{
- struct redirect *self = (struct redirect *) user_data;
+ redirect *self = (redirect *) user_data;
int res = 0;
const char *desc = "";
@@ -279,7 +279,7 @@ usbredir_log_cb(void *priv, int level, const char *msg)
static int
usbredir_read_cb(void *priv, uint8_t *data, int count)
{
- struct redirect *self = (struct redirect *) priv;
+ redirect *self = (redirect *) priv;
GIOStream *iostream = G_IO_STREAM(self->connection);
GError *err = NULL;
@@ -307,7 +307,7 @@ usbredir_read_cb(void *priv, uint8_t *data, int count)
static int
usbredir_write_cb(void *priv, uint8_t *data, int count)
{
- struct redirect *self = (struct redirect *) priv;
+ redirect *self = (redirect *) priv;
GIOStream *iostream = G_IO_STREAM(self->connection);
GError *err = NULL;
@@ -335,7 +335,7 @@ usbredir_write_cb(void *priv, uint8_t *data, int count)
static void
usbredir_write_flush_cb(void *user_data)
{
- struct redirect *self = (struct redirect *) user_data;
+ redirect *self = (redirect *) user_data;
if (!self || !self->usbredirhost) {
return;
}
@@ -386,7 +386,7 @@ usbredir_unlock_lock(void *user_data)
static gboolean
connection_handle_io_cb(GIOChannel *source, GIOCondition condition, gpointer user_data)
{
- struct redirect *self = (struct redirect *) user_data;
+ redirect *self = (redirect *) user_data;
if (condition & G_IO_ERR || condition & G_IO_HUP) {
g_warning("Connection: err=%d, hup=%d - exiting", (condition & G_IO_ERR), (condition & G_IO_HUP));
@@ -418,7 +418,7 @@ end:
static gboolean
signal_handler(gpointer user_data)
{
- struct redirect *self = (struct redirect *) user_data;
+ redirect *self = (redirect *) user_data;
g_main_loop_quit(self->main_loop);
return G_SOURCE_REMOVE;
}
@@ -430,7 +430,7 @@ connection_incoming_cb(GSocketService *service,
GObject *source_object,
gpointer user_data)
{
- struct redirect *self = (struct redirect *) user_data;
+ redirect *self = (redirect *) user_data;
self->connection = g_object_ref(client_connection);
/* Add a GSource watch to handle polling for us and handle IO in the callback */
@@ -455,7 +455,7 @@ main(int argc, char *argv[])
goto err_init;
}
- struct redirect *self = parse_opts(&argc, &argv);
+ redirect *self = parse_opts(&argc, &argv);
if (!self) {
/* specific issues logged in parse_opts() */
return 1;
--
2.39.0

@ -0,0 +1,74 @@
From 5399d3d5ce420891adfd4beedb023515a28ceab1 Mon Sep 17 00:00:00 2001
From: Frediano Ziglio <freddy77@gmail.com>
Date: Fri, 16 Sep 2022 20:14:28 +0100
Subject: [PATCH 4/8] Factor out a function to create watches
---
tools/usbredirect.c | 37 ++++++++++++++++++++-----------------
1 file changed, 20 insertions(+), 17 deletions(-)
diff --git a/tools/usbredirect.c b/tools/usbredirect.c
index 89a42a6..6a8b68b 100644
--- a/tools/usbredirect.c
+++ b/tools/usbredirect.c
@@ -414,6 +414,24 @@ end:
return G_SOURCE_REMOVE;
}
+static void
+create_watch(redirect *self)
+{
+ GSocket *socket = g_socket_connection_get_socket(self->connection);
+ int socket_fd = g_socket_get_fd(socket);
+ GIOChannel *io_channel =
+#ifdef G_OS_UNIX
+ g_io_channel_unix_new(socket_fd);
+#else
+ g_io_channel_win32_new_socket(socket_fd);
+#endif
+
+ self->watch_server_id = g_io_add_watch(io_channel,
+ G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
+ connection_handle_io_cb,
+ self);
+}
+
#ifdef G_OS_UNIX
static gboolean
signal_handler(gpointer user_data)
@@ -436,12 +454,7 @@ connection_incoming_cb(GSocketService *service,
/* Add a GSource watch to handle polling for us and handle IO in the callback */
GSocket *connection_socket = g_socket_connection_get_socket(self->connection);
g_socket_set_keepalive(connection_socket, self->keepalive);
- int socket_fd = g_socket_get_fd(connection_socket);
- GIOChannel *io_channel = g_io_channel_unix_new(socket_fd);
- self->watch_server_id = g_io_add_watch(io_channel,
- G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
- connection_handle_io_cb,
- self);
+ create_watch(self);
return G_SOURCE_REMOVE;
}
@@ -551,17 +564,7 @@ main(int argc, char *argv[])
GSocket *connection_socket = g_socket_connection_get_socket(self->connection);
g_socket_set_keepalive(connection_socket, self->keepalive);
- int socket_fd = g_socket_get_fd(connection_socket);
- GIOChannel *io_channel =
-#ifdef G_OS_UNIX
- g_io_channel_unix_new(socket_fd);
-#else
- g_io_channel_win32_new_socket(socket_fd);
-#endif
- self->watch_server_id = g_io_add_watch(io_channel,
- G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
- connection_handle_io_cb,
- self);
+ create_watch(self);
} else {
GSocketService *socket_service;
--
2.39.0

@ -0,0 +1,106 @@
From f5e24c5cf77b92ab2737eb230db7ae0b2fb102cc Mon Sep 17 00:00:00 2001
From: Frediano Ziglio <freddy77@gmail.com>
Date: Sat, 17 Sep 2022 09:28:08 +0100
Subject: [PATCH 5/8] Recreate watch if needed
Do not always watch for output buffer.
Watching for output buffer if we don't have nothing to write
(which is the usual case) is consuming a lot of CPU.
This fixes https://gitlab.freedesktop.org/spice/usbredir/-/issues/24.
Signed-off-by: Frediano Ziglio <freddy77@gmail.com>
---
tools/usbredirect.c | 28 ++++++++++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/tools/usbredirect.c b/tools/usbredirect.c
index 6a8b68b..b6a30ad 100644
--- a/tools/usbredirect.c
+++ b/tools/usbredirect.c
@@ -29,6 +29,7 @@ typedef struct redirect {
} device;
bool is_client;
bool keepalive;
+ bool watch_inout;
char *addr;
int port;
int verbosity;
@@ -42,6 +43,8 @@ typedef struct redirect {
GMainLoop *main_loop;
} redirect;
+static void create_watch(redirect *self);
+
static bool
parse_opt_device(const char *device, int *vendor, int *product)
{
@@ -162,6 +165,7 @@ parse_opts(int *argc, char ***argv)
}
self = g_new0(redirect, 1);
+ self->watch_inout = true;
if (!parse_opt_device(device, &self->device.vendor, &self->device.product)) {
g_printerr("Failed to parse device: '%s' - expected: vendor:product or busnum-devnum\n", device);
g_clear_pointer(&self, g_free);
@@ -276,6 +280,20 @@ usbredir_log_cb(void *priv, int level, const char *msg)
g_log_structured(G_LOG_DOMAIN, glog_level, "MESSAGE", msg);
}
+static void
+update_watch(redirect *self)
+{
+ const bool watch_inout = usbredirhost_has_data_to_write(self->usbredirhost) != 0;
+ if (watch_inout == self->watch_inout) {
+ return;
+ }
+ g_source_remove(self->watch_server_id);
+ self->watch_server_id = 0;
+ self->watch_inout = watch_inout;
+
+ create_watch(self);
+}
+
static int
usbredir_read_cb(void *priv, uint8_t *data, int count)
{
@@ -321,6 +339,7 @@ usbredir_write_cb(void *priv, uint8_t *data, int count)
if (g_error_matches(err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
/* Try again later */
nbytes = 0;
+ update_watch(self);
} else {
if (err != NULL) {
g_warning("Failure at %s: %s", __func__, err->message);
@@ -400,13 +419,18 @@ connection_handle_io_cb(GIOChannel *source, GIOCondition condition, gpointer use
goto end;
}
}
- if (condition & G_IO_OUT) {
+ // try to write data in any case, to avoid having another iteration and
+ // creation of another watch if there is space in output buffer
+ if (usbredirhost_has_data_to_write(self->usbredirhost) != 0) {
int ret = usbredirhost_write_guest_data(self->usbredirhost);
if (ret < 0) {
g_critical("%s: Failed to write to guest", __func__);
goto end;
}
}
+
+ // update the watch if needed
+ update_watch(self);
return G_SOURCE_CONTINUE;
end:
@@ -427,7 +451,7 @@ create_watch(redirect *self)
#endif
self->watch_server_id = g_io_add_watch(io_channel,
- G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | (self->watch_inout ? G_IO_OUT : 0),
connection_handle_io_cb,
self);
}
--
2.39.0

@ -0,0 +1,51 @@
From b3482e8d8f8970d481b9bfdb6662a8b67a973537 Mon Sep 17 00:00:00 2001
From: John Call <johnsimcall@gmail.com>
Date: Mon, 19 Dec 2022 19:01:50 +0000
Subject: [PATCH 6/8] Add documentation examples for using bus-device
identification
---
tools/usbredirect.1 | 6 ++++++
tools/usbredirect.c | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/tools/usbredirect.1 b/tools/usbredirect.1
index d6b2a63..2be8be0 100644
--- a/tools/usbredirect.1
+++ b/tools/usbredirect.1
@@ -4,6 +4,9 @@ usbredirect \- exporting an USB device for use from another (virtual) machine
.SH SYNOPSIS
.B usbredirect
[\fI--device vendor:product\fR] [\fI--to addr:port\fR] [\fI--as addr:port\fR]
+.br
+.B usbredirect
+[\fI--device bus-device\fR] [\fI--to addr:port\fR] [\fI--as addr:port\fR]
.SH DESCRIPTION
usbredirect is an usbredir client for exporting an USB device either as TCP
client or server, for use from another (virtual) machine through the usbredir
@@ -11,6 +14,9 @@ protocol.
.PP
You can specify the USB device to export by USB id in the form of
\fI<vendorid>:<prodid>\fR.
+.br
+Or you can specify the USB device to export in the form of
+\fI<busnum>-<devnum>\fR.
.PP
Notice that an instance of usbredirect can only be used to export a single USB
device and it will close once the other side closes the connection. If you
diff --git a/tools/usbredirect.c b/tools/usbredirect.c
index b6a30ad..95f3580 100644
--- a/tools/usbredirect.c
+++ b/tools/usbredirect.c
@@ -138,7 +138,7 @@ parse_opts(int *argc, char ***argv)
redirect *self = NULL;
GOptionEntry entries[] = {
- { "device", 0, 0, G_OPTION_ARG_STRING, &device, "Local USB device to be redirected", NULL },
+ { "device", 0, 0, G_OPTION_ARG_STRING, &device, "Local USB device to be redirected identified as either VENDOR:PRODUCT \"0123:4567\" or BUS-DEVICE \"5-2\"", NULL },
{ "to", 0, 0, G_OPTION_ARG_STRING, &remoteaddr, "Client URI to connect to", NULL },
{ "as", 0, 0, G_OPTION_ARG_STRING, &localaddr, "Server URI to be run", NULL },
{ "keepalive", 'k', 0, G_OPTION_ARG_NONE, &keepalive, "If we should set SO_KEEPALIVE flag on underlying socket", NULL },
--
2.39.0

@ -0,0 +1,136 @@
From 813afe206c8ff10cbbe715bf94980bf8ba6be70f Mon Sep 17 00:00:00 2001
From: Victor Toso <victortoso@redhat.com>
Date: Thu, 22 Dec 2022 16:13:08 +0100
Subject: [PATCH 7/8] usbredirect: allow multiple devices by vendor:product
Currently, if an user tries to redirect two devices with the same
vendor:product info, the second instance of usbredirect will not
succeed, leading to a segmentation fault.
The core of the problem is that usbredirect is using
libusb_open_device_with_vid_pid() which always returns the first
instance of a given vendor:product, leading the second instance of
usbredirect to give an usb device that is in use to usbredirhost.
This patch fixes the situation, making it possible to run usbredirect
with --device $vendor:$product multiple times. We do a early check
that we can claim the usb device, prior to handle it over to
usbredirhost.
Related: https://gitlab.freedesktop.org/spice/usbredir/-/issues/29
Signed-off-by: Victor Toso <victortoso@redhat.com>
---
tools/usbredirect.c | 90 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 85 insertions(+), 5 deletions(-)
diff --git a/tools/usbredirect.c b/tools/usbredirect.c
index 95f3580..0b04418 100644
--- a/tools/usbredirect.c
+++ b/tools/usbredirect.c
@@ -466,6 +466,90 @@ signal_handler(gpointer user_data)
}
#endif
+static bool
+can_claim_usb_device(libusb_device *dev, libusb_device_handle **handle)
+{
+ int ret = libusb_open(dev, handle);
+ if (ret != 0) {
+ g_debug("Failed to open device");
+ return false;
+ }
+
+ /* Opening is not enough. We need to check if device can be claimed
+ * for I/O operations */
+ struct libusb_config_descriptor *config = NULL;
+ ret = libusb_get_active_config_descriptor(dev, &config);
+ if (ret != 0 || config == NULL) {
+ g_debug("Failed to get active descriptor");
+ *handle = NULL;
+ return false;
+ }
+
+#if LIBUSBX_API_VERSION >= 0x01000102
+ libusb_set_auto_detach_kernel_driver(*handle, 1);
+#endif
+
+ int i;
+ for (i = 0; i < config->bNumInterfaces; i++) {
+ int interface_num = config->interface[i].altsetting[0].bInterfaceNumber;
+#if LIBUSBX_API_VERSION < 0x01000102
+ ret = libusb_detach_kernel_driver(handle, interface_num);
+ if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND
+ && ret != LIBUSB_ERROR_NOT_SUPPORTED) {
+ g_error("failed to detach driver from interface %d: %s",
+ interface_num, libusb_error_name(ret));
+ *handle = NULL;
+ break
+ }
+#endif
+ ret = libusb_claim_interface(*handle, interface_num);
+ if (ret != 0) {
+ g_debug("Could not claim interface");
+ *handle = NULL;
+ break;
+ }
+ ret = libusb_release_interface(*handle, interface_num);
+ if (ret != 0) {
+ g_debug("Could not release interface");
+ *handle = NULL;
+ break;
+ }
+ }
+
+ libusb_free_config_descriptor(config);
+ return *handle != NULL;
+}
+
+static libusb_device_handle *
+open_usb_device(redirect *self)
+{
+ struct libusb_device **devs;
+ struct libusb_device_handle *dev_handle = NULL;
+ size_t i, ndevices;
+
+ ndevices = libusb_get_device_list(NULL, &devs);
+ for (i = 0; i < ndevices; i++) {
+ struct libusb_device_descriptor desc;
+ if (libusb_get_device_descriptor(devs[i], &desc) != 0) {
+ g_warning("Failed to get descriptor");
+ continue;
+ }
+
+ if (self->device.vendor != desc.idVendor ||
+ self->device.product != desc.idProduct) {
+ continue;
+ }
+
+ if (can_claim_usb_device(devs[i], &dev_handle)) {
+ break;
+ }
+ }
+
+ libusb_free_device_list(devs, 1);
+ return dev_handle;
+}
+
+
static gboolean
connection_incoming_cb(GSocketService *service,
GSocketConnection *client_connection,
@@ -515,11 +599,7 @@ main(int argc, char *argv[])
g_unix_signal_add(SIGTERM, signal_handler, self);
#endif
- /* This is binary is not meant to support plugins so it is safe to pass
- * NULL as libusb_context here and all subsequent calls */
- libusb_device_handle *device_handle = libusb_open_device_with_vid_pid(NULL,
- self->device.vendor,
- self->device.product);
+ libusb_device_handle *device_handle = open_usb_device(self);
if (!device_handle) {
g_printerr("Failed to open device!\n");
goto err_init;
--
2.39.0

@ -0,0 +1,153 @@
From c32774c8a46aa04bf8f882ea552df8cb2d9f3a59 Mon Sep 17 00:00:00 2001
From: Victor Toso <victortoso@redhat.com>
Date: Thu, 22 Dec 2022 16:58:43 +0100
Subject: [PATCH 8/8] usbredirect: use the correct bus-device
This patch is a continuation from:
"usbredirect: allow multiple devices by vendor:product"
As we were using libusb_open_device_with_vid_pid(), if an user
requested that device on 2-3 was redirected, we would instead get the
vendor and product info of device on 2-3 and use that info to pick a
usb device. This is wrong when multiple devices that shared
vendor:product are plugged as libbusb_open_device_with_vid_pid()
always return the same (first) device.
This commit now stores bus-device info and uses that to pick the usb
device to redirect.
Related: https://gitlab.freedesktop.org/spice/usbredir/-/issues/29
Signed-off-by: Victor Toso <victortoso@redhat.com>
---
tools/usbredirect.c | 63 ++++++++++++++++++++-------------------------
1 file changed, 28 insertions(+), 35 deletions(-)
diff --git a/tools/usbredirect.c b/tools/usbredirect.c
index 0b04418..cad9d23 100644
--- a/tools/usbredirect.c
+++ b/tools/usbredirect.c
@@ -24,9 +24,14 @@
typedef struct redirect {
struct {
+ /* vendor:product */
int vendor;
int product;
+ /* bus-device */
+ int bus;
+ int device_number;
} device;
+ bool by_bus;
bool is_client;
bool keepalive;
bool watch_inout;
@@ -46,7 +51,7 @@ typedef struct redirect {
static void create_watch(redirect *self);
static bool
-parse_opt_device(const char *device, int *vendor, int *product)
+parse_opt_device(redirect *self, const char *device)
{
if (!device) {
g_warning("No device to redirect. For testing only\n");
@@ -54,37 +59,15 @@ parse_opt_device(const char *device, int *vendor, int *product)
}
if (g_strrstr(device, "-") != NULL) {
- /* Get vendor and product by bus and address number */
+ self->by_bus = true;
char **usbid = g_strsplit(device, "-", 2);
if (usbid == NULL || usbid[0] == NULL || usbid[1] == NULL || usbid[2] != NULL) {
g_strfreev(usbid);
return false;
}
- gint64 bus = g_ascii_strtoll(usbid[0], NULL, 10);
- gint64 addr = g_ascii_strtoll(usbid[1], NULL, 10);
-
- libusb_device **list = NULL;
- ssize_t i, n;
-
- n = libusb_get_device_list(NULL, &list);
- for (i = 0; i < n; i++) {
- if (libusb_get_bus_number(list[i]) == bus &&
- libusb_get_device_address(list[i]) == addr) {
- break;
- }
- }
-
- if (i == n) {
- libusb_free_device_list(list, true);
- return false;
- }
-
- struct libusb_device_descriptor desc;
- libusb_get_device_descriptor(list[i], &desc);
- *vendor = desc.idVendor;
- *product = desc.idProduct;
-
- libusb_free_device_list(list, true);
+ self->device.bus = g_ascii_strtoll(usbid[0], NULL, 10);
+ self->device.device_number = g_ascii_strtoll(usbid[1], NULL, 10);
+ g_strfreev(usbid);
return true;
}
@@ -94,12 +77,14 @@ parse_opt_device(const char *device, int *vendor, int *product)
return false;
}
- *vendor = g_ascii_strtoll(usbid[0], NULL, 16);
- *product = g_ascii_strtoll(usbid[1], NULL, 16);
+ self->device.vendor = g_ascii_strtoll(usbid[0], NULL, 16);
+ self->device.product = g_ascii_strtoll(usbid[1], NULL, 16);
g_strfreev(usbid);
- if (*vendor <= 0 || *vendor > 0xffff || *product < 0 || *product > 0xffff) {
- g_printerr("Bad vendor:product values %04x:%04x", *vendor, *product);
+ if (self->device.vendor <= 0 || self->device.vendor > 0xffff ||
+ self->device.product < 0 || self->device.product > 0xffff) {
+ g_printerr("Bad vendor:product values %04x:%04x",
+ self->device.vendor, self->device.product);
return false;
}
@@ -166,7 +151,7 @@ parse_opts(int *argc, char ***argv)
self = g_new0(redirect, 1);
self->watch_inout = true;
- if (!parse_opt_device(device, &self->device.vendor, &self->device.product)) {
+ if (!parse_opt_device(self, device)) {
g_printerr("Failed to parse device: '%s' - expected: vendor:product or busnum-devnum\n", device);
g_clear_pointer(&self, g_free);
goto end;
@@ -535,9 +520,16 @@ open_usb_device(redirect *self)
continue;
}
- if (self->device.vendor != desc.idVendor ||
- self->device.product != desc.idProduct) {
- continue;
+ if (self->by_bus &&
+ (self->device.bus != libusb_get_bus_number(devs[i]) ||
+ self->device.device_number != libusb_get_device_address(devs[i]))) {
+ continue;
+ }
+
+ if (!self->by_bus &&
+ (self->device.vendor != desc.idVendor ||
+ self->device.product != desc.idProduct)) {
+ continue;
}
if (can_claim_usb_device(devs[i], &dev_handle)) {
@@ -674,6 +666,7 @@ main(int argc, char *argv[])
socket_service = g_socket_service_new ();
GInetAddress *iaddr = g_inet_address_new_loopback(G_SOCKET_FAMILY_IPV4);
+
GSocketAddress *saddr = g_inet_socket_address_new(iaddr, self->port);
g_object_unref(iaddr);
--
2.39.0

@ -0,0 +1,207 @@
Name: usbredir
Version: 0.12.0
Release: 4%{?dist}
Summary: USB network redirection protocol libraries
Group: System Environment/Libraries
License: LGPLv2+
URL: https://www.spice-space.org/usbredir.html
Source0: http://spice-space.org/download/%{name}/%{name}-%{version}.tar.xz
Patch0001: 0001-usbredirparser-Fix-unserialize-on-pristine-check.patch
Patch0002: 0002-usbredirparser-reset-parser-s-fields-on-unserialize.patch
Patch0003: 0003-Use-typedef-on-redirect-structure-to-simplify-some-s.patch
Patch0004: 0004-Factor-out-a-function-to-create-watches.patch
Patch0005: 0005-Recreate-watch-if-needed.patch
Patch0006: 0006-Add-documentation-examples-for-using-bus-device-iden.patch
Patch0007: 0007-usbredirect-allow-multiple-devices-by-vendor-product.patch
Patch0008: 0008-usbredirect-use-the-correct-bus-device.patch
BuildRequires: glib2-devel
BuildRequires: libusb1-devel >= 1.0.9
BuildRequires: git-core
BuildRequires: meson
%description
The usbredir libraries allow USB devices to be used on remote and/or virtual
hosts over TCP. The following libraries are provided:
usbredirparser:
A library containing the parser for the usbredir protocol
usbredirhost:
A library implementing the USB host side of a usbredir connection.
All that an application wishing to implement a USB host needs to do is:
* Provide a libusb device handle for the device
* Provide write and read callbacks for the actual transport of usbredir data
* Monitor for usbredir and libusb read/write events and call their handlers
%package devel
Summary: Development files for %{name}
Group: Development/Libraries
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
The %{name}-devel package contains libraries and header files for
developing applications that use %{name}.
%package server
Summary: Simple USB host TCP server
Group: System Environment/Daemons
License: GPLv2+
Requires: %{name}%{?_isa} = %{version}-%{release}
%description server
A simple USB host TCP server, using libusbredirhost.
%prep
%autosetup -S git_am
%build
%meson \
-Dgit_werror=disabled \
-Dtools=enabled \
-Dfuzzing=disabled
%meson_build
%install
%meson_install
%ldconfig_scriptlets
%files
%{!?_licensedir:%global license %%doc}
%license COPYING.LIB
%{_libdir}/libusbredir*.so.*
%files devel
%doc docs/usb-redirection-protocol.md docs/multi-thread.md ChangeLog.md TODO
%{_includedir}/usbredir*.h
%{_libdir}/libusbredir*.so
%{_libdir}/pkgconfig/libusbredir*.pc
%files server
%{!?_licensedir:%global license %%doc}
%license COPYING
%{_bindir}/usbredirect
%{_sbindir}/usbredirserver
%{_mandir}/man1/usbredirect.1*
%{_mandir}/man1/usbredirserver.1*
%changelog
* Wed Jan 25 2023 Victor Toso <victortoso@redhat.com> - 0.12.0-4
- Rebuild to fix lack of elf sections
Related: rhbz#2157521
* Thu Jan 05 2023 Victor Toso <victortoso@redhat.com> - 0.12.0-3
- Fixes 100% CPU usage when usbredirect used as TCP server.
Related: rhbz#2157521
- Fixes USB redirection of identical devices.
Resolves: rhbz#2157521
* Wed Jul 27 2022 Victor Toso <victortoso@redhat.com> - 0.12.0-2
- Fixes unserialization on migration
Resolves: rhbz#2111351
* Fri Nov 12 2021 Victor Toso <victortoso@redhat.com> - 0.12.0-1
- Update to 0.12.0 release
- Resolves: rhbz#2022751
* Thu Aug 23 2018 Victor Toso <victortoso@redhat.com> - 0.8.0-1
- Update to 0.8.0 release
- Resolves: rhbz#1620098
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.7.1-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Mon Feb 05 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 0.7.1-6
- Switch to %%ldconfig_scriptlets
* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.7.1-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.7.1-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.7.1-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
* Fri Feb 05 2016 Fedora Release Engineering <releng@fedoraproject.org> - 0.7.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
* Mon Nov 02 2015 Fabiano Fidêncio <fidencio@redhat.com> 0.7.1-1
- Update to upstream 0.7.1 release
* Tue Jun 16 2015 Peter Robinson <pbrobinson@fedoraproject.org> 0.7-4
- Use %%license
* Mon Aug 18 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.7-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
* Sun Jun 08 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
* Wed May 21 2014 Hans de Goede <hdegoede@redhat.com> - 0.7-1
- Update to upstream 0.7 release
* Tue Sep 10 2013 Hans de Goede <hdegoede@redhat.com> - 0.6-5
- Use the new libusb autodetach kernel driver functionality
- Fix a usbredirparser bug which causes tcp/ip redir to not work (rhbz#1005015)
* Sun Aug 04 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
* Mon May 13 2013 Hans de Goede <hdegoede@redhat.com> - 0.6-3
- Fix usbredirserver not listening for ipv6 connections (rhbz#957470)
- Fix a few (harmless) coverity warnings
* Fri Feb 15 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
* Thu Dec 13 2012 Hans de Goede <hdegoede@redhat.com> - 0.6-1
- Update to upstream 0.6 release
* Tue Sep 25 2012 Hans de Goede <hdegoede@redhat.com> - 0.5.2-1
- Update to upstream 0.5.2 release
* Wed Sep 19 2012 Hans de Goede <hdegoede@redhat.com> - 0.5.1-1
- Update to upstream 0.5.1 release
* Fri Sep 7 2012 Hans de Goede <hdegoede@redhat.com> - 0.5-1
- Update to upstream 0.5 release
* Mon Jul 30 2012 Hans de Goede <hdegoede@redhat.com> - 0.4.3-3
- Add 2 fixes from upstream fixing issues with some bulk devices (rhbz#842358)
* Sun Jul 22 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.4.3-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
* Mon Apr 2 2012 Hans de Goede <hdegoede@redhat.com> - 0.4.3-1
- Update to upstream 0.4.3 release
* Tue Mar 6 2012 Hans de Goede <hdegoede@redhat.com> - 0.4.2-1
- Update to upstream 0.4.2 release
* Sat Feb 25 2012 Hans de Goede <hdegoede@redhat.com> - 0.4.1-1
- Update to upstream 0.4.1 release
* Thu Feb 23 2012 Hans de Goede <hdegoede@redhat.com> - 0.4-1
- Update to upstream 0.4 release
* Thu Jan 12 2012 Hans de Goede <hdegoede@redhat.com> - 0.3.3-1
- Update to upstream 0.3.3 release
* Tue Jan 3 2012 Hans de Goede <hdegoede@redhat.com> 0.3.2-1
- Update to upstream 0.3.2 release
* Wed Aug 24 2011 Hans de Goede <hdegoede@redhat.com> 0.3.1-1
- Update to upstream 0.3.1 release
* Thu Jul 14 2011 Hans de Goede <hdegoede@redhat.com> 0.3-1
- Initial Fedora package
Loading…
Cancel
Save