diff --git a/.gitignore b/.gitignore index 3a9ae78..255da81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /libusb-1.0.24.tar.bz2 /libusb-1.0.25.tar.bz2 +/libusb-1.0.26.tar.bz2 diff --git a/0001-core-Install-first-context-as-implicit-default.patch b/0001-core-Install-first-context-as-implicit-default.patch deleted file mode 100644 index b64a29e..0000000 --- a/0001-core-Install-first-context-as-implicit-default.patch +++ /dev/null @@ -1,158 +0,0 @@ -From d46cbbac4851ce6e49d8dacb0daa328453eb8a84 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Tue, 22 Feb 2022 11:45:38 +0100 -Subject: [PATCH] core: Install first context as implicit default - -There was a behaviour change in libusb, which triggers issues when the -API is misused. This caused gutenprint to crash, see -https://bugzilla.redhat.com/show_bug.cgi?id=2055504 - -For now, work around this by installing an implicit default. But, change -the code to log an error in case this "feature" is being used. ---- - libusb/core.c | 16 +++++++++++++--- - libusb/libusbi.h | 15 ++++++++++++++- - tests/umockdev.c | 31 +++++++++++++++++++++++++++++++ - 3 files changed, 58 insertions(+), 4 deletions(-) - -diff --git a/libusb/core.c b/libusb/core.c -index 1c1ada1..c75ddae 100644 ---- a/libusb/core.c -+++ b/libusb/core.c -@@ -41,6 +41,7 @@ static libusb_log_cb log_handler; - #endif - - struct libusb_context *usbi_default_context; -+struct libusb_context *usbi_fallback_context; - static int default_context_refcnt; - static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; - static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; -@@ -2284,7 +2285,7 @@ int API_EXPORTED libusb_init(libusb_context **ctx) - - usbi_mutex_static_lock(&default_context_lock); - -- if (!ctx && usbi_default_context) { -+ if (!ctx && default_context_refcnt > 0) { - usbi_dbg(usbi_default_context, "reusing default context"); - default_context_refcnt++; - usbi_mutex_static_unlock(&default_context_lock); -@@ -2354,9 +2355,15 @@ int API_EXPORTED libusb_init(libusb_context **ctx) - goto err_io_exit; - } - -- if (ctx) -+ if (ctx) { - *ctx = _ctx; - -+ if (!usbi_fallback_context) { -+ usbi_fallback_context = _ctx; -+ usbi_warn(usbi_fallback_context, "installing new context as implicit default"); -+ } -+ } -+ - usbi_mutex_static_unlock(&default_context_lock); - - return 0; -@@ -2429,6 +2436,8 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) - - if (!ctx) - usbi_default_context = NULL; -+ if (ctx == usbi_fallback_context) -+ usbi_fallback_context = NULL; - - usbi_mutex_static_unlock(&default_context_lock); - -@@ -2575,7 +2584,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, - #else - enum libusb_log_level ctx_level; - -- ctx = usbi_get_context(ctx); -+ ctx = ctx ? ctx : usbi_default_context; -+ ctx = ctx ? ctx : usbi_fallback_context; - if (ctx) - ctx_level = ctx->debug; - else -diff --git a/libusb/libusbi.h b/libusb/libusbi.h -index 5f0d5c2..580add8 100644 ---- a/libusb/libusbi.h -+++ b/libusb/libusbi.h -@@ -436,13 +436,26 @@ struct libusb_context { - }; - - extern struct libusb_context *usbi_default_context; -+extern struct libusb_context *usbi_fallback_context; - - extern struct list_head active_contexts_list; - extern usbi_mutex_static_t active_contexts_lock; - - static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) - { -- return ctx ? ctx : usbi_default_context; -+ static int warned = 0; -+ -+ if (!ctx) { -+ ctx = usbi_default_context; -+ } -+ if (!ctx) { -+ ctx = usbi_fallback_context; -+ if (ctx && warned == 0) { -+ usbi_err(ctx, "API misuse! Using non-default context as implicit default."); -+ warned = 1; -+ } -+ } -+ return ctx; - } - - enum usbi_event_flags { -diff --git a/tests/umockdev.c b/tests/umockdev.c -index b2af512..0e73f94 100644 ---- a/tests/umockdev.c -+++ b/tests/umockdev.c -@@ -551,6 +551,32 @@ test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) - libusb_close(handle); - } - -+static void -+test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ libusb_device **devs = NULL; -+ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); -+ libusb_free_device_list(devs, TRUE); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[usbi_get_context\\].*implicit default"); -+ -+ /* Only warns once */ -+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); -+ libusb_free_device_list(devs, TRUE); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ -+ libusb_init(NULL); -+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); -+ libusb_exit(NULL); -+ -+ /* We free late, causing a warning from libusb_exit. However, -+ * we never see this warning (i.e. test success) because it is on a -+ * different context. -+ */ -+ libusb_free_device_list(devs, TRUE); -+} -+ - static void - test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) - { -@@ -932,6 +958,11 @@ main(int argc, char **argv) - test_open_close, - test_fixture_teardown); - -+ g_test_add("/libusb/implicit-default", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_implicit_default, -+ test_fixture_teardown); -+ - g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, - test_fixture_setup_with_canon, - test_close_flying, --- -2.35.1 - diff --git a/0001-tests-Add-some-umockdev-based-tests.patch b/0001-tests-Add-some-umockdev-based-tests.patch deleted file mode 100644 index 8aba02d..0000000 --- a/0001-tests-Add-some-umockdev-based-tests.patch +++ /dev/null @@ -1,1038 +0,0 @@ -From 87976d513fbb35e13cbcd8cb00d2abc1cac0dd54 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Fri, 18 Feb 2022 16:26:29 +0100 -Subject: [PATCH 1/2] tests: Add some umockdev based tests - -Add some basic umockdev tests. The setup in this case is all in one -process, but umockdev in principle could also work between processes. - -This is just a start, more tests would make sense. ---- - configure.ac | 7 + - tests/Makefile.am | 13 +- - tests/umockdev.c | 965 ++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 984 insertions(+), 1 deletion(-) - create mode 100644 tests/umockdev.c - -diff --git a/configure.ac b/configure.ac -index f6cf2f9..5336463 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -177,6 +177,12 @@ linux) - dnl system has udev. use it or fail! - AC_CHECK_HEADER([libudev.h], [], [AC_MSG_ERROR([udev support requested but libudev header not installed])]) - AC_CHECK_LIB([udev], [udev_new], [], [AC_MSG_ERROR([udev support requested but libudev not installed])]) -+ -+ # We can build umockdev tests (if available) -+ PKG_PROG_PKG_CONFIG -+ PKG_CHECK_MODULES(UMOCKDEV, umockdev-1.0 >= 0.16.0, ac_have_umockdev=yes, ac_have_umockdev=no) -+ AC_SUBST(UMOCKDEV_CFLAGS) -+ AC_SUBST(UMOCKDEV_LIBS) - else - AC_CHECK_HEADERS([asm/types.h]) - AC_CHECK_HEADER([linux/netlink.h], [], [AC_MSG_ERROR([Linux netlink header not found])]) -@@ -356,6 +362,7 @@ AC_ARG_ENABLE([tests-build], - - AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != xno]) - AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != xno]) -+AM_CONDITIONAL([BUILD_UMOCKDEV_TEST], [test "x$ac_have_umockdev" = xyes -a "x$log_enabled" != xno]) - AM_CONDITIONAL([CREATE_IMPORT_LIB], [test "x$create_import_lib" = xyes]) - AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = xdarwin]) - AM_CONDITIONAL([OS_HAIKU], [test "x$backend" = xhaiku]) -diff --git a/tests/Makefile.am b/tests/Makefile.am -index cb8fad9..cf6237e 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -2,6 +2,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb - LDADD = ../libusb/libusb-1.0.la - LIBS = - -+stress_SOURCES = stress.c libusb_testlib.h testlib.c -+ - noinst_PROGRAMS = stress - --stress_SOURCES = stress.c libusb_testlib.h testlib.c -+if BUILD_UMOCKDEV_TEST -+# NOTE: We add libumockdev-preload.so so that we can run tests in-process -+# We also use -Wl,-lxxx as the compiler doesn't need it and libtool -+# would reorder the flags otherwise. -+umockdev_CPPFLAGS = ${UMOCKDEV_CFLAGS} -I$(top_srcdir)/libusb -+umockdev_LDFLAGS = -Wl,--push-state,--no-as-needed -Wl,-lumockdev-preload -Wl,--pop-state ${UMOCKDEV_LIBS} -+umockdev_SOURCES = umockdev.c -+ -+noinst_PROGRAMS += umockdev -+endif -diff --git a/tests/umockdev.c b/tests/umockdev.c -new file mode 100644 -index 0000000..c026b51 ---- /dev/null -+++ b/tests/umockdev.c -@@ -0,0 +1,965 @@ -+/* -+ * libusb umockdev based tests -+ * -+ * Copyright (C) 2022 Benjamin Berg -+ * -+ * This program 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 program 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 program; If not, see . -+ */ -+ -+#define _GNU_SOURCE -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "libusb.h" -+ -+#include "umockdev.h" -+ -+#define UNUSED_DATA __attribute__ ((unused)) gconstpointer unused_data -+ -+/* avoid leak reports inside assertions; leaking stuff on assertion failures does not matter in tests */ -+#if !defined(__clang__) -+#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" -+#pragma GCC diagnostic ignored "-Wanalyzer-file-leak" -+#endif -+ -+typedef struct { -+ pid_t thread; -+ libusb_context *ctx; -+ enum libusb_log_level level; -+ char *str; -+} LogMessage; -+ -+static void -+log_message_free(LogMessage *msg) -+{ -+ g_free(msg->str); -+ g_free(msg); -+} -+G_DEFINE_AUTOPTR_CLEANUP_FUNC(LogMessage, log_message_free) -+ -+typedef struct _UsbChat UsbChat; -+ -+struct _UsbChat { -+ gboolean submit; -+ gboolean reap; -+ UsbChat *reaps; -+ UsbChat *next; -+ -+ /* struct usbdevfs_urb */ -+ unsigned char type; -+ unsigned char endpoint; -+ int status; -+ unsigned int flags; -+ const unsigned char *buffer; -+ int buffer_length; -+ int actual_length; -+ -+ /* */ -+ UMockdevIoctlData *submit_urb; -+}; -+ -+typedef struct { -+ UMockdevTestbed *testbed; -+ UMockdevIoctlBase *handler; -+ struct libusb_context *ctx; -+ -+ gchar *root_dir; -+ gchar *sys_dir; -+ -+ gboolean libusb_log_silence; -+ GList *libusb_log; -+ -+ UsbChat *chat; -+ GList *flying_urbs; -+ GList *discarded_urbs; -+ -+ /* GMutex confuses tsan unecessarily */ -+ pthread_mutex_t mutex; -+} UMockdevTestbedFixture; -+ -+/* Global for log handler */ -+static UMockdevTestbedFixture *cur_fixture = NULL; -+ -+static void -+log_handler(libusb_context *ctx, enum libusb_log_level level, const char *str) -+{ -+ /* May be called from different threads without synchronization! */ -+ LogMessage *msg; -+ pid_t tid = gettid(); -+ -+ g_assert (cur_fixture != NULL); -+ g_assert(pthread_mutex_lock(&cur_fixture->mutex) == 0); -+ -+ msg = g_new0(LogMessage, 1); -+ msg->ctx = ctx; -+ msg->level = level; -+ msg->str = g_strchomp (g_strdup(str)); -+ msg->thread = tid; -+ -+ if (!cur_fixture->libusb_log_silence) -+ g_printerr("%s\n", msg->str); -+ -+ cur_fixture->libusb_log = g_list_append(cur_fixture->libusb_log, msg); -+ pthread_mutex_unlock(&cur_fixture->mutex); -+} -+ -+static void -+log_handler_null(libusb_context *ctx, enum libusb_log_level level, const char *str) -+{ -+ (void) ctx; -+ (void) level; -+ (void) str; -+} -+ -+static void -+clear_libusb_log(UMockdevTestbedFixture * fixture, enum libusb_log_level level) -+{ -+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); -+ -+ while (fixture->libusb_log) { -+ LogMessage *msg = fixture->libusb_log->data; -+ -+ g_assert(msg->ctx == fixture->ctx); -+ -+ if (msg->level < level) { -+ pthread_mutex_unlock(&fixture->mutex); -+ return; -+ } -+ -+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); -+ log_message_free(msg); -+ } -+ pthread_mutex_unlock(&fixture->mutex); -+} -+ -+static void -+assert_libusb_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) -+{ -+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); -+ -+ while (fixture->libusb_log) { -+ g_autoptr(LogMessage) msg = NULL; -+ -+ if (fixture->libusb_log == NULL) -+ g_error ("No level %d message found searching for %s", level, re); -+ -+ msg = fixture->libusb_log->data; -+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); -+ -+ if (msg->ctx != fixture->ctx) -+ g_error ("Saw unexpected message \"%s\" from context %p while %p was expected", -+ msg->str, msg->ctx, fixture->ctx); -+ -+ if (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)) { -+ pthread_mutex_unlock(&fixture->mutex); -+ return; -+ } -+ -+ /* Allow skipping INFO and DEBUG messages */ -+ if (msg->level >= LIBUSB_LOG_LEVEL_INFO) -+ continue; -+ -+ g_error ("Searched for \"%s\" (%d) but found \"%s\" (%d)", re, level, msg->str, msg->level); -+ } -+ -+ pthread_mutex_unlock(&fixture->mutex); -+ g_error ("Searched for \"%s\" (%d) but no message matched", re, level); -+} -+ -+#if 0 -+static void -+assert_libusb_no_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) -+{ -+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); -+ -+ while (fixture->libusb_log) { -+ g_autoptr(LogMessage) msg = NULL; -+ gboolean matching; -+ -+ msg = fixture->libusb_log->data; -+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); -+ -+ g_assert(msg->ctx == fixture->ctx); -+ -+ matching = (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)); -+ -+ /* Allow skipping INFO and DEBUG messages */ -+ if (!matching && msg->level >= LIBUSB_LOG_LEVEL_INFO) -+ continue; -+ -+ g_error ("Asserting \"%s\" (%d) not logged and found \"%s\" (%d)", re, level, msg->str, msg->level); -+ } -+ -+ pthread_mutex_unlock(&fixture->mutex); -+} -+#endif -+ -+static void -+dump_buffer(const unsigned char *buffer, int len) -+{ -+ g_autoptr(GString) line = NULL; -+ -+ line = g_string_new (""); -+ for (gint i = 0; i < len; i++) { -+ g_string_append_printf(line, "%02x ", buffer[i]); -+ if ((i + 1) % 16 == 0) { -+ g_printerr(" %s\n", line->str); -+ g_string_set_size(line, 0); -+ } -+ } -+ -+ if (line->len) -+ g_printerr(" %s\n", line->str); -+} -+ -+static gint -+cmp_ioctl_data_addr(const void *data, const void *addr) -+{ -+ return ((const UMockdevIoctlData*) data)->client_addr != (gulong) addr; -+} -+ -+static gboolean -+handle_ioctl_cb (UMockdevIoctlBase *handler, UMockdevIoctlClient *client, UMockdevTestbedFixture *fixture) -+{ -+ UMockdevIoctlData *ioctl_arg; -+ long int request; -+ struct usbdevfs_urb *urb; -+ -+ (void) handler; -+ -+ request = umockdev_ioctl_client_get_request (client); -+ ioctl_arg = umockdev_ioctl_client_get_arg (client); -+ -+ /* NOTE: We share the address space, dereferencing pointers *will* work. -+ * However, to make tsan work, we still stick to the API that resolves -+ * the data into a local copy! */ -+ -+ switch (request) { -+ case USBDEVFS_GET_CAPABILITIES: { -+ g_autoptr(UMockdevIoctlData) d = NULL; -+ d = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(guint32), NULL); -+ -+ *(guint32*) d->data = USBDEVFS_CAP_BULK_SCATTER_GATHER | -+ USBDEVFS_CAP_BULK_CONTINUATION | -+ USBDEVFS_CAP_NO_PACKET_SIZE_LIM | -+ USBDEVFS_CAP_REAP_AFTER_DISCONNECT | -+ USBDEVFS_CAP_ZERO_PACKET; -+ -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ } -+ -+ case USBDEVFS_CLAIMINTERFACE: -+ case USBDEVFS_RELEASEINTERFACE: -+ case USBDEVFS_CLEAR_HALT: -+ case USBDEVFS_RESET: -+ case USBDEVFS_RESETEP: -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ -+ case USBDEVFS_SUBMITURB: { -+ g_autoptr(UMockdevIoctlData) urb_buffer = NULL; -+ g_autoptr(UMockdevIoctlData) urb_data = NULL; -+ gsize buflen; -+ -+ if (!fixture->chat || !fixture->chat->submit) -+ return FALSE; -+ -+ buflen = fixture->chat->buffer_length; -+ if (fixture->chat->type == USBDEVFS_URB_TYPE_CONTROL) -+ buflen = 8; -+ -+ urb_data = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(struct usbdevfs_urb), NULL); -+ urb = (struct usbdevfs_urb*) urb_data->data; -+ urb_buffer = umockdev_ioctl_data_resolve(urb_data, G_STRUCT_OFFSET(struct usbdevfs_urb, buffer), urb->buffer_length, NULL); -+ -+ if (fixture->chat->type == urb->type && -+ fixture->chat->endpoint == urb->endpoint && -+ fixture->chat->buffer_length == urb->buffer_length && -+ (fixture->chat->buffer == NULL || memcmp (fixture->chat->buffer, urb_buffer->data, buflen) == 0)) { -+ fixture->flying_urbs = g_list_append (fixture->flying_urbs, umockdev_ioctl_data_ref(urb_data)); -+ -+ if (fixture->chat->reaps) -+ fixture->chat->reaps->submit_urb = urb_data; -+ -+ if (fixture->chat->status) -+ umockdev_ioctl_client_complete(client, -1, -fixture->chat->status); -+ else -+ umockdev_ioctl_client_complete(client, 0, 0); -+ -+ if (fixture->chat->next) -+ fixture->chat = fixture->chat->next; -+ else -+ fixture->chat += 1; -+ return TRUE; -+ } -+ -+ /* chat message didn't match, don't accept it */ -+ g_printerr("Could not process submit urb:\n"); -+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", -+ urb->type, urb->endpoint, urb->actual_length, urb->buffer_length); -+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL || urb->endpoint & LIBUSB_ENDPOINT_IN) -+ dump_buffer(urb->buffer, urb->buffer_length); -+ g_printerr("Looking for:\n"); -+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", -+ fixture->chat->type, fixture->chat->endpoint, -+ fixture->chat->actual_length, fixture->chat->buffer_length); -+ if (fixture->chat->buffer) -+ dump_buffer(fixture->chat->buffer, buflen); -+ -+ return FALSE; -+ } -+ -+ case USBDEVFS_REAPURB: -+ case USBDEVFS_REAPURBNDELAY: { -+ g_autoptr(UMockdevIoctlData) urb_ptr = NULL; -+ g_autoptr(UMockdevIoctlData) urb_data = NULL; -+ -+ if (fixture->discarded_urbs) { -+ urb_data = fixture->discarded_urbs->data; -+ urb = (struct usbdevfs_urb*) urb_data->data; -+ fixture->discarded_urbs = g_list_delete_link(fixture->discarded_urbs, fixture->discarded_urbs); -+ urb->status = -ENOENT; -+ -+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); -+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); -+ -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ } -+ -+ if (fixture->chat && fixture->chat->reap) { -+ GList *l = g_list_find(fixture->flying_urbs, fixture->chat->submit_urb); -+ -+ if (l) { -+ fixture->flying_urbs = g_list_remove_link(fixture->flying_urbs, fixture->flying_urbs); -+ -+ urb_data = fixture->chat->submit_urb; -+ urb = (struct usbdevfs_urb*) urb_data->data; -+ urb->actual_length = fixture->chat->actual_length; -+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL && urb->actual_length) -+ urb->actual_length -= 8; -+ if (fixture->chat->buffer) -+ memcpy(urb->buffer, fixture->chat->buffer, fixture->chat->actual_length); -+ urb->status = fixture->chat->status; -+ -+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); -+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); -+ if (fixture->chat->next) -+ fixture->chat = fixture->chat->next; -+ else -+ fixture->chat += 1; -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ } -+ } -+ -+ /* Nothing to reap */ -+ umockdev_ioctl_client_complete(client, -1, EAGAIN); -+ return TRUE; -+ } -+ -+ case USBDEVFS_DISCARDURB: { -+ GList *l = g_list_find_custom(fixture->flying_urbs, *(void**) ioctl_arg->data, cmp_ioctl_data_addr); -+ -+ if (l) { -+ fixture->discarded_urbs = g_list_append(fixture->discarded_urbs, l->data); -+ fixture->flying_urbs = g_list_delete_link(fixture->flying_urbs, l); -+ umockdev_ioctl_client_complete(client, 0, 0); -+ } else { -+ umockdev_ioctl_client_complete(client, -1, EINVAL); -+ } -+ -+ return TRUE; -+ } -+ -+ default: -+ return FALSE; -+ } -+} -+ -+static void -+test_fixture_add_canon(UMockdevTestbedFixture * fixture) -+{ -+ /* Setup first, so we can be sure libusb_open works when the add uevent -+ * happens. -+ */ -+ g_assert_cmpint(umockdev_testbed_attach_ioctl(fixture->testbed, "/dev/bus/usb/001/001", fixture->handler, NULL), ==, 1); -+ -+ /* NOTE: add_device would not create a file, needed for device emulation */ -+ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ -+ umockdev_testbed_add_from_string(fixture->testbed, -+ "P: /devices/usb1\n" -+ "N: bus/usb/001/001\n" -+ "E: SUBSYSTEM=usb\n" -+ "E: DRIVER=usb\n" -+ "E: BUSNUM=001\n" -+ "E: DEVNUM=001\n" -+ "E: DEVNAME=/dev/bus/usb/001/001\n" -+ "E: DEVTYPE=usb_device\n" -+ "A: bConfigurationValue=1\\n\n" -+ "A: busnum=1\\n\n" -+ "A: devnum=1\\n\n" -+ "A: bConfigurationValue=1\\n\n" -+ "A: speed=480\\n\n" -+ /* descriptor from a Canon PowerShot SX200; VID 04a9 PID 31c0 */ -+ "H: descriptors=" -+ "1201000200000040a904c03102000102" -+ "030109022700010100c0010904000003" -+ "06010100070581020002000705020200" -+ "020007058303080009\n", -+ NULL); -+} -+ -+static void -+test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) -+{ -+ libusb_device **devs = NULL; -+ -+ libusb_init (&fixture->ctx); -+ -+ /* Supress global log messages completely -+ * (though, in some tests it might be interesting to check there are no real ones). -+ */ -+ libusb_set_log_cb (NULL, log_handler_null, LIBUSB_LOG_CB_GLOBAL); -+ libusb_set_option (fixture->ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG); -+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount); -+ libusb_free_device_list(devs, TRUE); -+ libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT); -+} -+ -+static void -+test_fixture_setup_common(UMockdevTestbedFixture * fixture) -+{ -+ g_assert(cur_fixture == NULL); -+ cur_fixture = fixture; -+ -+ pthread_mutex_init(&fixture->mutex, NULL); -+ -+ fixture->testbed = umockdev_testbed_new(); -+ g_assert(fixture->testbed != NULL); -+ fixture->root_dir = umockdev_testbed_get_root_dir(fixture->testbed); -+ fixture->sys_dir = umockdev_testbed_get_sys_dir(fixture->testbed); -+ -+ fixture->handler = umockdev_ioctl_base_new(); -+ g_object_connect(fixture->handler, "signal-after::handle-ioctl", handle_ioctl_cb, fixture, NULL); -+} -+ -+#if 0 -+static void -+test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ -+ test_fixture_setup_common(fixture); -+ -+ test_fixture_setup_libusb(fixture, 0); -+} -+#endif -+ -+static void -+test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ test_fixture_setup_common(fixture); -+ -+ test_fixture_add_canon(fixture); -+ -+ test_fixture_setup_libusb(fixture, 1); -+} -+ -+static void -+test_fixture_teardown(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ g_assert(cur_fixture == fixture); -+ -+ /* libusb_exit logs */ -+ if (fixture->ctx) -+ libusb_exit (fixture->ctx); -+ libusb_set_log_cb (NULL, NULL, LIBUSB_LOG_CB_GLOBAL); -+ cur_fixture = NULL; -+ -+ /* Abort if there are any warnings/errors in the log */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ fixture->ctx = NULL; -+ g_assert_null(fixture->libusb_log); -+ -+ g_clear_object(&fixture->handler); -+ g_clear_object(&fixture->testbed); -+ -+ /* verify that temp dir gets cleaned up properly */ -+ g_assert(!g_file_test(fixture->root_dir, G_FILE_TEST_EXISTS)); -+ g_free(fixture->root_dir); -+ g_free(fixture->sys_dir); -+ -+ while (fixture->flying_urbs) { -+ umockdev_ioctl_data_unref (fixture->flying_urbs->data); -+ fixture->flying_urbs = g_list_delete_link (fixture->flying_urbs, fixture->flying_urbs); -+ } -+ -+ pthread_mutex_destroy(&fixture->mutex); -+} -+ -+static void -+test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ libusb_device **devs = NULL; -+ struct libusb_device_descriptor desc; -+ libusb_device_handle *handle = NULL; -+ -+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1); -+ /* The linux_enumerate_device may happen from a different thread */ -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_list"); -+ /* We have exactly one device */ -+ g_assert_cmpint(libusb_get_bus_number(devs[0]), ==, 1); -+ g_assert_cmpint(libusb_get_device_address(devs[0]), ==, 1); -+ -+ /* Get/Check descriptor */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ libusb_get_device_descriptor (devs[0], &desc); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_descriptor"); -+ g_assert_cmpint(desc.idVendor, ==, 0x04a9); -+ g_assert_cmpint(desc.idProduct, ==, 0x31c0); -+ -+ /* Open and close */ -+ g_assert_cmpint(libusb_open(devs[0], &handle), ==, 0); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_add_event_source"); -+ g_assert_nonnull(handle); -+ libusb_close(handle); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_remove_event_source"); -+ -+ libusb_free_device_list(devs, TRUE); -+ -+ /* Open and close using vid/pid */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ libusb_close(handle); -+} -+ -+static void -+test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { .submit = FALSE } -+ }; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ /* Open */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ NULL, -+ NULL, -+ 1); -+ -+ /* Submit */ -+ libusb_submit_transfer(transfer); -+ -+ /* Closing logs fat error (two lines) */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ libusb_close(handle); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*cancellation hasn't even been scheduled"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); -+ -+ /* Free'ing the transfer works, and logs to the right context */ -+ libusb_free_transfer(transfer); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[libusb_free_transfer\\]"); -+} -+ -+static void -+test_close_cancelled(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { .submit = FALSE } -+ }; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ /* Open */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ NULL, -+ NULL, -+ 1); -+ -+ /* Submit */ -+ libusb_submit_transfer(transfer); -+ libusb_cancel_transfer(transfer); -+ -+ /* Closing logs fat error (two lines) */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ libusb_close(handle); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[do_close\\] .*cancellation.*hasn't completed"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); -+ -+ libusb_free_transfer(transfer); -+} -+ -+static void -+test_ctx_destroy(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { .submit = FALSE } -+ }; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ /* Open */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ NULL, -+ NULL, -+ 1); -+ -+ /* Submit */ -+ libusb_submit_transfer(transfer); -+ -+ /* Now we are evil and destroy the ctx! */ -+ libusb_exit(fixture->ctx); -+ -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] device.*still referenced"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] application left some devices open"); -+ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ fixture->ctx = NULL; -+ -+ /* XXX: Closing crashes the application as it unref's the NULL pointer */ -+ /* libusb_close(handle); */ -+ -+ libusb_free_transfer(transfer); -+} -+ -+static void -+test_get_string_descriptor(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ unsigned char data[255] = { 0, }; -+ libusb_device_handle *handle = NULL; -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .reaps = &chat[1], -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 12, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", -+ }, { -+ /* String with content 0x0409 (en_US) */ -+ .reap = TRUE, -+ .actual_length = 12, -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00\x04\x03\x09\x04", -+ }, { -+ .submit = TRUE, -+ .reaps = &chat[3], -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 263, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00", -+ }, { -+ /* 4 byte string, "ab" */ -+ .reap = TRUE, -+ .actual_length = 14, -+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00\x06\x03\x61\x00\x62\x00", -+ }, { -+ .submit = TRUE, -+ .reaps = &chat[5], -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 12, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", -+ }, { -+ .reap = TRUE, -+ .status = -ENOENT, -+ }, { -+ .submit = TRUE, -+ .status = -ENOENT, -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 12, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", -+ }, { -+ .submit = FALSE, -+ } -+ }; -+ -+ fixture->chat = chat; -+ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ /* The chat allows us to fetch the descriptor */ -+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, 2); -+ g_assert_cmpint(memcmp(data, "ab", 2), ==, 0); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ -+ /* Again, but the URB fails with ENOENT when reaping */ -+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ -+ /* Again, but the URB fails to submit with ENOENT */ -+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[submit_control_transfer\\] submiturb failed, errno=2"); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ -+ libusb_close(handle); -+} -+ -+static void -+transfer_cb_inc_user_data(struct libusb_transfer *transfer) -+{ -+ *(int*)transfer->user_data += 1; -+} -+ -+static void -+test_timeout(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { -+ .submit = FALSE, -+ } -+ }; -+ int completed = 0; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ transfer_cb_inc_user_data, -+ &completed, -+ 10); -+ -+ libusb_submit_transfer(transfer); -+ while (!completed) { -+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &completed), ==, 0); -+ /* Silence after one iteration. */ -+ fixture->libusb_log_silence = TRUE; -+ } -+ fixture->libusb_log_silence = FALSE; -+ -+ g_assert_cmpint(transfer->status, ==, LIBUSB_TRANSFER_TIMED_OUT); -+ libusb_free_transfer(transfer); -+ -+ libusb_close(handle); -+} -+ -+#define THREADED_SUBMIT_URB_SETS 64 -+#define THREADED_SUBMIT_URB_IN_FLIGHT 64 -+typedef struct { -+ struct libusb_transfer *transfers[THREADED_SUBMIT_URB_IN_FLIGHT * THREADED_SUBMIT_URB_SETS]; -+ int submitted; -+ int completed; -+ int done; -+ UMockdevTestbedFixture *fixture; -+} TestThreadedSubmit; -+ -+static gpointer -+transfer_submit_all_retry(TestThreadedSubmit *data) -+{ -+ for (guint i = 0; i < G_N_ELEMENTS(data->transfers); i++) { -+ while (libusb_submit_transfer(data->transfers[i]) < 0) { -+ assert_libusb_log_msg(data->fixture, LIBUSB_LOG_LEVEL_ERROR, "submit_bulk_transfer"); -+ continue; -+ } -+ -+ data->submitted += 1; -+ } -+ -+ return NULL; -+} -+ -+static void -+test_threaded_submit_transfer_cb(struct libusb_transfer *transfer) -+{ -+ TestThreadedSubmit *data = transfer->user_data; -+ -+ /* We should only be receiving packets in the main thread */ -+ g_assert_cmpint (getpid(), ==, gettid()); -+ -+ /* Check that the transfer buffer has the expected value */ -+ g_assert_cmpint (*(int*)transfer->buffer, ==, data->completed); -+ data->completed += 1; -+ -+ if (data->completed == G_N_ELEMENTS(data->transfers)) -+ data->done = TRUE; -+} -+ -+static void -+test_threaded_submit(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ GThread *thread = NULL; -+ TestThreadedSubmit data = { .fixture = fixture }; -+ UsbChat out_msg = { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_IN, -+ .buffer_length = sizeof(int), -+ }; -+ UsbChat in_msg = { -+ .reap = TRUE, -+ .actual_length = 4, -+ }; -+ UsbChat *c; -+ libusb_device_handle *handle = NULL; -+ int urb; -+ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ fixture->libusb_log_silence = TRUE; -+ -+ c = fixture->chat = g_new0(UsbChat, G_N_ELEMENTS(data.transfers) * 2 + 1); -+ urb = 0; -+ for (int i = 0; i < THREADED_SUBMIT_URB_SETS; i++) { -+ for (int j = 0; j < THREADED_SUBMIT_URB_IN_FLIGHT; j++) { -+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j] = out_msg; -+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j].reaps = &c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j]; -+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j] = in_msg; -+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = (unsigned char*) g_new0(int, 1); -+ *(int*) c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = urb; -+ -+ data.transfers[urb] = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(data.transfers[urb], -+ handle, -+ LIBUSB_ENDPOINT_IN, -+ g_malloc(out_msg.buffer_length), -+ out_msg.buffer_length, -+ test_threaded_submit_transfer_cb, -+ &data, -+ G_MAXUINT); -+ data.transfers[urb]->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; -+ urb++; -+ } -+ } -+ -+ thread = g_thread_new("transfer all", (GThreadFunc) transfer_submit_all_retry, &data); -+ -+ while (!data.done) -+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &data.done), ==, 0); -+ -+ g_thread_join(thread); -+ -+ fixture->libusb_log_silence = FALSE; -+ libusb_close(handle); -+ -+ for (int i = 0; i < 2 * THREADED_SUBMIT_URB_SETS * THREADED_SUBMIT_URB_SETS; i++) -+ g_clear_pointer ((void**) &c->buffer, g_free); -+ g_free (c); -+} -+ -+int -+main(int argc, char **argv) -+{ -+ g_test_init(&argc, &argv, NULL); -+ -+ g_test_add("/libusb/open-close", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_open_close, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_close_flying, -+ test_fixture_teardown); -+ g_test_add("/libusb/close-cancelled", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_close_cancelled, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/ctx-destroy", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_ctx_destroy, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/string-descriptor", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_get_string_descriptor, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/timeout", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_timeout, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/threaded-submit", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_threaded_submit, -+ test_fixture_teardown); -+ -+ return g_test_run(); -+} --- -2.35.1 - diff --git a/1058.patch b/1058.patch deleted file mode 100644 index d504455..0000000 --- a/1058.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 2529a3fc4f987f93e0774af865ac7cb6557bd0c2 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Fri, 4 Feb 2022 22:50:28 +0100 -Subject: [PATCH] core: Unset device ctx if it has been destroyed - -Devices can outlive their context in some cases (in particular with -python garbage collection). Guard against this happening by clearing the -ctx pointer so that it is not pointing to uninitialized memory. ---- - libusb/core.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/libusb/core.c b/libusb/core.c -index 7893ac23..1c1ada14 100644 ---- a/libusb/core.c -+++ b/libusb/core.c -@@ -2441,6 +2441,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) - for_each_device(_ctx, dev) { - usbi_warn(_ctx, "device %d.%d still referenced", - dev->bus_number, dev->device_address); -+ DEVICE_CTX(dev) = NULL; - } - - if (!list_empty(&_ctx->open_devs)) diff --git a/1073.patch b/1073.patch deleted file mode 100644 index 7ecce5b..0000000 --- a/1073.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 1cb11c5ba0d1d1266fe4ddd70f29d081f9d16802 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Tue, 15 Feb 2022 11:13:41 +0100 -Subject: [PATCH] io: Track device in usbi_transfer - -transfer->dev_handle currently has the behaviour that it will be unset -if the device is closed. The sync API uses this fact to catch an error -case. - -In other cases, transfer->dev_handle will keep its value, which means -that if the transfer lives longer than the device handle, the pointer -becomes invalid. - -The transfer does however keep a reference to the device, which owns the -pointer to the context. As such, we can track this reference internal to -the transfer, and it is set while the transfer is in-flight. - -With this, switch the logging infrastructure to use itransfer->dev->ctx -while checking that itransfer->dev is non-NULL. - -Note that this was a regression caused by 6cae9c6dbd74 ("core: update -usbi_dbg to take the context as an argument"), specifically when -resolving the context while freeing a transfer after closing a device. - -Note that the transfer will now keep a reference to the device until it -is free'ed. This allows it to use the correct context for logging even -in libusb_free_transfer. - -The alternative to all this would be to just explicitly pass NULL to the -log handler in libusb_free_transfer. ---- - libusb/io.c | 20 ++++++++++++-------- - libusb/libusbi.h | 10 +++++++--- - 2 files changed, 19 insertions(+), 11 deletions(-) - -diff --git a/libusb/io.c b/libusb/io.c -index 0d2ac9ea..b919e9d9 100644 ---- a/libusb/io.c -+++ b/libusb/io.c -@@ -1344,6 +1344,8 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) - - itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); - usbi_mutex_destroy(&itransfer->lock); -+ if (itransfer->dev) -+ libusb_unref_device(itransfer->dev); - - priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); - ptr = (unsigned char *)itransfer - priv_size; -@@ -1489,9 +1491,15 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) - { - struct usbi_transfer *itransfer = - LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); -- struct libusb_context *ctx = TRANSFER_CTX(transfer); -+ struct libusb_context *ctx; - int r; - -+ assert(transfer->dev_handle); -+ if (itransfer->dev) -+ libusb_unref_device(itransfer->dev); -+ itransfer->dev = libusb_ref_device(transfer->dev_handle->dev); -+ -+ ctx = HANDLE_CTX(transfer->dev_handle); - usbi_dbg(ctx, "transfer %p", transfer); - - /* -@@ -1551,8 +1559,6 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) - r = usbi_backend.submit_transfer(itransfer); - if (r == LIBUSB_SUCCESS) { - itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT; -- /* keep a reference to this device */ -- libusb_ref_device(transfer->dev_handle->dev); - } - usbi_mutex_unlock(&itransfer->lock); - -@@ -1659,7 +1665,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, - { - struct libusb_transfer *transfer = - USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); -- struct libusb_device_handle *dev_handle = transfer->dev_handle; - struct libusb_context *ctx = ITRANSFER_CTX(itransfer); - uint8_t flags; - int r; -@@ -1693,7 +1698,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, - * this point. */ - if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) - libusb_free_transfer(transfer); -- libusb_unref_device(dev_handle->dev); - return r; - } - -@@ -1727,10 +1731,10 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) - * function will be called the next time an event handler runs. */ - void usbi_signal_transfer_completion(struct usbi_transfer *itransfer) - { -- libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle; -+ struct libusb_device *dev = itransfer->dev; - -- if (dev_handle) { -- struct libusb_context *ctx = HANDLE_CTX(dev_handle); -+ if (dev) { -+ struct libusb_context *ctx = DEVICE_CTX(dev); - unsigned int event_flags; - - usbi_mutex_lock(&ctx->event_data_lock); -diff --git a/libusb/libusbi.h b/libusb/libusbi.h -index 158a9af5..5f0d5c2e 100644 ---- a/libusb/libusbi.h -+++ b/libusb/libusbi.h -@@ -329,10 +329,11 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, - #endif /* ENABLE_LOGGING */ - - #define DEVICE_CTX(dev) ((dev)->ctx) --#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) --#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) -+#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL) - #define ITRANSFER_CTX(itransfer) \ -- (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) -+ ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL) -+#define TRANSFER_CTX(transfer) \ -+ (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer))) - - #define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) - #define IS_EPOUT(ep) (!IS_EPIN(ep)) -@@ -562,6 +563,9 @@ struct usbi_transfer { - uint32_t state_flags; /* Protected by usbi_transfer->lock */ - uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ - -+ /* This is used for logging mostly. As long as it is set, the */ -+ struct libusb_device *dev; -+ - /* this lock is held during libusb_submit_transfer() and - * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate - * cancellation, submission-during-cancellation, etc). the OS backend diff --git a/changelog b/changelog index 5df34fe..c8df9bf 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ +* Fri Sep 30 2022 Kate Hsuan 1.0.26-1 +- Update to 1.0.26 + * Wed Feb 02 2022 Benjamin Berg 1.0.25-1 - Update to 1.0.25 diff --git a/libusb1.spec b/libusb1.spec index e7c8582..7c112f7 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,6 +1,6 @@ Summary: Library for accessing USB devices Name: libusb1 -Version: 1.0.25 +Version: 1.0.26 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPLv2+ @@ -13,18 +13,6 @@ BuildRequires: gcc Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} -# Fix a crash after libusb_exit API has been misused -# https://bugzilla.redhat.com/show_bug.cgi?id=2050638 -Patch0001: https://github.com/libusb/libusb/pull/1058.patch -# Fix a crash if a transfer outlives closing the device -Patch0002: https://github.com/libusb/libusb/pull/1073.patch -# Add umockdev based tests from https://github.com/libusb/libusb/pull/1078 -Patch0003: 0001-tests-Add-some-umockdev-based-tests.patch - -# Work around API misuse in gutenprint -# https://bugzilla.redhat.com/show_bug.cgi?id=2056326 -Patch9999: 0001-core-Install-first-context-as-implicit-default.patch - %description This package provides a way for applications to access USB devices. diff --git a/sources b/sources index 75982ca..3b509af 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (libusb-1.0.25.tar.bz2) = f1e6e5577d4bd1ff136927dc66c615014a06ac332ddd797b1d1ad5f7b68e2405e66068dcb210e2f0ae3e31681603ef72efbd88bf7fbe0eb41ce700fdc3f92f9d +SHA512 (libusb-1.0.26.tar.bz2) = fcdb85c98f21639668693c2fd522814d440972d65883984c4ae53d0555bdbdb7e8c7a32199cd4b01113556a1eb5be7841b750cc73c9f6bda79bfe1af80914e71