From 261c00aec299611e2b884d630468dbc71745e148 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 12:06:52 +0100 Subject: [PATCH] Work around API misuse in gutenprint https://bugzilla.redhat.com/show_bug.cgi?id=2056326 --- ...ll-first-context-as-implicit-default.patch | 175 ++++++++++++++++++ libusb1.spec | 4 + 2 files changed, 179 insertions(+) create mode 100644 0001-core-Install-first-context-as-implicit-default.patch diff --git a/0001-core-Install-first-context-as-implicit-default.patch b/0001-core-Install-first-context-as-implicit-default.patch new file mode 100644 index 0000000..b04207b --- /dev/null +++ b/0001-core-Install-first-context-as-implicit-default.patch @@ -0,0 +1,175 @@ +From f74f1e21b6201ec803347ddaf2737cdaf9a1180a 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 | 24 ++++++++++++++++-------- + libusb/libusbi.h | 8 +++++++- + tests/umockdev.c | 26 ++++++++++++++++++++++++++ + 3 files changed, 49 insertions(+), 9 deletions(-) + +diff --git a/libusb/core.c b/libusb/core.c +index 1c1ada1..c56df72 100644 +--- a/libusb/core.c ++++ b/libusb/core.c +@@ -41,7 +41,7 @@ static libusb_log_cb log_handler; + #endif + + struct libusb_context *usbi_default_context; +-static int default_context_refcnt; ++int usbi_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,9 +2284,9 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + + usbi_mutex_static_lock(&default_context_lock); + +- if (!ctx && usbi_default_context) { ++ if (!ctx && usbi_default_context_refcnt > 0) { + usbi_dbg(usbi_default_context, "reusing default context"); +- default_context_refcnt++; ++ usbi_default_context_refcnt++; + usbi_mutex_static_unlock(&default_context_lock); + return 0; + } +@@ -2331,7 +2331,7 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + /* default context must be initialized before calling usbi_dbg */ + if (!ctx) { + usbi_default_context = _ctx; +- default_context_refcnt = 1; ++ usbi_default_context_refcnt = 1; + usbi_dbg(usbi_default_context, "created default context"); + } + +@@ -2357,6 +2357,12 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + if (ctx) + *ctx = _ctx; + ++ if (usbi_default_context == NULL) { ++ usbi_default_context = _ctx; ++ usbi_default_context_refcnt = 0; ++ usbi_dbg(usbi_default_context, "installing new context as implicit default"); ++ } ++ + usbi_mutex_static_unlock(&default_context_lock); + + return 0; +@@ -2373,7 +2379,7 @@ err_free_ctx: + if (!ctx) { + /* clear default context that was not fully initialized */ + usbi_default_context = NULL; +- default_context_refcnt = 0; ++ usbi_default_context_refcnt = 0; + } + + usbi_mutex_destroy(&_ctx->open_devs_lock); +@@ -2407,7 +2413,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + return; + } + +- if (--default_context_refcnt > 0) { ++ if (--usbi_default_context_refcnt > 0) { + usbi_dbg(ctx, "not destroying default context"); + usbi_mutex_static_unlock(&default_context_lock); + return; +@@ -2427,8 +2433,10 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + if (usbi_backend.exit) + usbi_backend.exit(_ctx); + +- if (!ctx) ++ if (!ctx || ctx == usbi_default_context) { + usbi_default_context = NULL; ++ assert(usbi_default_context_refcnt == 0); ++ } + + usbi_mutex_static_unlock(&default_context_lock); + +@@ -2575,7 +2583,7 @@ 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; + if (ctx) + ctx_level = ctx->debug; + else +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 5f0d5c2..a404cff 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -436,13 +436,19 @@ struct libusb_context { + }; + + extern struct libusb_context *usbi_default_context; ++extern int usbi_default_context_refcnt; + + 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; ++ if (!ctx && usbi_default_context) { ++ if (usbi_default_context_refcnt == 0) ++ usbi_err(usbi_default_context, "API misuse! Using non-default context as implicit default."); ++ ctx = usbi_default_context; ++ } ++ return ctx; + } + + enum usbi_event_flags { +diff --git a/tests/umockdev.c b/tests/umockdev.c +index b2af512..7de0aae 100644 +--- a/tests/umockdev.c ++++ b/tests/umockdev.c +@@ -551,6 +551,27 @@ 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"); ++ ++ 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 +953,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/libusb1.spec b/libusb1.spec index b35bb98..a97ff41 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -21,6 +21,10 @@ 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.