Blob Blame History Raw
From d46cbbac4851ce6e49d8dacb0daa328453eb8a84 Mon Sep 17 00:00:00 2001
From: Benjamin Berg <bberg@redhat.com>
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