diff options
Diffstat (limited to 'libusb/os/windows_common.c')
-rw-r--r-- | libusb/os/windows_common.c | 155 |
1 files changed, 92 insertions, 63 deletions
diff --git a/libusb/os/windows_common.c b/libusb/os/windows_common.c index 119ed49..24ac095 100644 --- a/libusb/os/windows_common.c +++ b/libusb/os/windows_common.c @@ -24,7 +24,6 @@ #include <config.h> -#include <process.h> #include <stdio.h> #include "libusbi.h" @@ -153,7 +152,7 @@ static bool htab_create(struct libusb_context *ctx) // Create a mutex usbi_mutex_init(&htab_mutex); - usbi_dbg("using %lu entries hash table", HTAB_SIZE); + usbi_dbg(ctx, "using %lu entries hash table", HTAB_SIZE); htab_filled = 0; // allocate memory and zero out. @@ -222,7 +221,7 @@ unsigned long htab_hash(const char *str) if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0)) goto out_unlock; // existing hash - usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str); + usbi_dbg(NULL, "hash collision ('%s' vs '%s')", str, htab_table[idx].str); // Second hash function, as suggested in [Knuth] hval2 = 1UL + hval % (HTAB_SIZE - 2); @@ -284,7 +283,7 @@ enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS st case USBD_STATUS_DEVICE_GONE: return LIBUSB_TRANSFER_NO_DEVICE; default: - usbi_dbg("USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status)); + usbi_dbg(NULL, "USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status)); return LIBUSB_TRANSFER_ERROR; } } @@ -294,15 +293,18 @@ enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS st */ void windows_force_sync_completion(struct usbi_transfer *itransfer, ULONG size) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_context_priv *priv = usbi_get_context_priv(TRANSFER_CTX(transfer)); struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer); OVERLAPPED *overlapped = &transfer_priv->overlapped; - usbi_dbg("transfer %p, length %lu", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(size)); + usbi_dbg(TRANSFER_CTX(transfer), "transfer %p, length %lu", transfer, ULONG_CAST(size)); overlapped->Internal = (ULONG_PTR)STATUS_SUCCESS; overlapped->InternalHigh = (ULONG_PTR)size; - usbi_signal_transfer_completion(itransfer); + if (!PostQueuedCompletionStatus(priv->completion_port, (DWORD)size, (ULONG_PTR)transfer->dev_handle, overlapped)) + usbi_err(TRANSFER_CTX(transfer), "failed to post I/O completion: %s", windows_error_str(0)); } /* Windows version detection */ @@ -344,6 +346,8 @@ static enum windows_version get_windows_version(void) if ((vi.dwMajorVersion > 6) || ((vi.dwMajorVersion == 6) && (vi.dwMinorVersion >= 2))) { // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx + // And starting with Windows 10 Preview 2, Windows enforces the use of the application/supportedOS + // manifest in order for VerSetConditionMask() to report the ACTUAL OS major and minor... major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); for (major = vi.dwMajorVersion; major <= 9; major++) { @@ -379,6 +383,7 @@ static enum windows_version get_windows_version(void) ws = (vi.wProductType <= VER_NT_WORKSTATION); version = vi.dwMajorVersion << 4 | vi.dwMinorVersion; + switch (version) { case 0x50: winver = WINDOWS_2000; w = "2000"; break; case 0x51: winver = WINDOWS_XP; w = "XP"; break; @@ -388,22 +393,30 @@ static enum windows_version get_windows_version(void) case 0x62: winver = WINDOWS_8; w = (ws ? "8" : "2012"); break; case 0x63: winver = WINDOWS_8_1; w = (ws ? "8.1" : "2012_R2"); break; case 0x64: // Early Windows 10 Insider Previews and Windows Server 2017 Technical Preview 1 used version 6.4 - case 0xA0: winver = WINDOWS_10; w = (ws ? "10" : "2016"); break; + case 0xA0: winver = WINDOWS_10; w = (ws ? "10" : "2016"); + if (vi.dwBuildNumber < 20000) + break; + // fallthrough + case 0xB0: winver = WINDOWS_11; w = (ws ? "11" : "2022"); break; default: if (version < 0x50) return WINDOWS_UNDEFINED; - winver = WINDOWS_11_OR_LATER; - w = "11 or later"; + winver = WINDOWS_12_OR_LATER; + w = "12 or later"; } + // We cannot tell if we are on 8, 10, or 11 without "app manifest" + if (version == 0x62 && vi.dwBuildNumber == 9200) + w = "8 (or later)"; + arch = is_x64() ? "64-bit" : "32-bit"; if (vi.wServicePackMinor) - usbi_dbg("Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch); + usbi_dbg(NULL, "Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch); else if (vi.wServicePackMajor) - usbi_dbg("Windows %s SP%u %s", w, vi.wServicePackMajor, arch); + usbi_dbg(NULL, "Windows %s SP%u %s", w, vi.wServicePackMajor, arch); else - usbi_dbg("Windows %s %s", w, arch); + usbi_dbg(NULL, "Windows %s %s", w, arch); return winver; } @@ -416,10 +429,14 @@ static unsigned __stdcall windows_iocp_thread(void *arg) DWORD num_bytes; ULONG_PTR completion_key; OVERLAPPED *overlapped; + struct libusb_device_handle *dev_handle; + struct libusb_device_handle *opened_device_handle; + struct windows_device_handle_priv *handle_priv; struct windows_transfer_priv *transfer_priv; struct usbi_transfer *itransfer; + bool found; - usbi_dbg("I/O completion thread started"); + usbi_dbg(ctx, "I/O completion thread started"); while (true) { overlapped = NULL; @@ -435,14 +452,48 @@ static unsigned __stdcall windows_iocp_thread(void *arg) break; } - transfer_priv = container_of(overlapped, struct windows_transfer_priv, overlapped); + // Find the transfer associated with the OVERLAPPED that just completed. + // If we cannot find a match, the I/O operation originated from outside of libusb + // (e.g. within libusbK) and we need to ignore it. + dev_handle = (struct libusb_device_handle *)completion_key; + + found = false; + transfer_priv = NULL; + + // Issue 912: lock opened device handles in context to search the current device handle + // to avoid accessing unallocated memory after device has been closed + usbi_mutex_lock(&ctx->open_devs_lock); + for_each_open_device(ctx, opened_device_handle) { + if (dev_handle == opened_device_handle) { + handle_priv = usbi_get_device_handle_priv(dev_handle); + + usbi_mutex_lock(&dev_handle->lock); + list_for_each_entry(transfer_priv, &handle_priv->active_transfers, list, struct windows_transfer_priv) { + if (overlapped == &transfer_priv->overlapped) { + // This OVERLAPPED belongs to us, remove the transfer from the device handle's list + list_del(&transfer_priv->list); + found = true; + break; + } + } + usbi_mutex_unlock(&dev_handle->lock); + } + } + usbi_mutex_unlock(&ctx->open_devs_lock); + + if (!found) { + usbi_dbg(ctx, "ignoring overlapped %p for handle %p (device %u.%u)", + overlapped, dev_handle, dev_handle->dev->bus_number, dev_handle->dev->device_address); + continue; + } + itransfer = (struct usbi_transfer *)((unsigned char *)transfer_priv + PTR_ALIGN(sizeof(*transfer_priv))); - usbi_dbg("transfer %p completed, length %lu", + usbi_dbg(ctx, "transfer %p completed, length %lu", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(num_bytes)); usbi_signal_transfer_completion(itransfer); } - usbi_dbg("I/O completion thread exiting"); + usbi_dbg(ctx, "I/O completion thread exiting"); return 0; } @@ -450,26 +501,9 @@ static unsigned __stdcall windows_iocp_thread(void *arg) static int windows_init(struct libusb_context *ctx) { struct windows_context_priv *priv = usbi_get_context_priv(ctx); - char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0' - HANDLE mutex; bool winusb_backend_init = false; int r; - sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU)); - mutex = CreateMutexA(NULL, FALSE, mutex_name); - if (mutex == NULL) { - usbi_err(ctx, "could not create mutex: %s", windows_error_str(0)); - return LIBUSB_ERROR_NO_MEM; - } - - // A successful wait gives this thread ownership of the mutex - // => any concurrent wait stalls until the mutex is released - if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) { - usbi_err(ctx, "failure to access mutex: %s", windows_error_str(0)); - CloseHandle(mutex); - return LIBUSB_ERROR_NO_MEM; - } - // NB: concurrent usage supposes that init calls are equally balanced with // exit calls. If init is called more than exit, we will not exit properly if (++init_count == 1) { // First init? @@ -496,7 +530,7 @@ static int windows_init(struct libusb_context *ctx) r = usbdk_backend.init(ctx); if (r == LIBUSB_SUCCESS) { - usbi_dbg("UsbDk backend is available"); + usbi_dbg(ctx, "UsbDk backend is available"); usbdk_available = true; } else { usbi_info(ctx, "UsbDk backend is not available"); @@ -538,29 +572,12 @@ init_exit: // Holds semaphore here --init_count; } - ReleaseMutex(mutex); - CloseHandle(mutex); return r; } static void windows_exit(struct libusb_context *ctx) { struct windows_context_priv *priv = usbi_get_context_priv(ctx); - char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0' - HANDLE mutex; - - sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU)); - mutex = CreateMutexA(NULL, FALSE, mutex_name); - if (mutex == NULL) - return; - - // A successful wait gives this thread ownership of the mutex - // => any concurrent wait stalls until the mutex is released - if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) { - usbi_err(ctx, "failed to access mutex: %s", windows_error_str(0)); - CloseHandle(mutex); - return; - } // A NULL completion status will indicate to the thread that it is time to exit if (!PostQueuedCompletionStatus(priv->completion_port, 0, (ULONG_PTR)ctx, NULL)) @@ -581,9 +598,6 @@ static void windows_exit(struct libusb_context *ctx) winusb_backend.exit(ctx); htab_destroy(); } - - ReleaseMutex(mutex); - CloseHandle(mutex); } static int windows_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap) @@ -597,7 +611,7 @@ static int windows_set_option(struct libusb_context *ctx, enum libusb_option opt usbi_err(ctx, "UsbDk backend not available"); return LIBUSB_ERROR_NOT_FOUND; } - usbi_dbg("switching context %p to use UsbDk backend", ctx); + usbi_dbg(ctx, "switching context %p to use UsbDk backend", ctx); priv->backend = &usbdk_backend; return LIBUSB_SUCCESS; } @@ -614,6 +628,9 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered static int windows_open(struct libusb_device_handle *dev_handle) { struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); + struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle); + + list_init(&handle_priv->active_transfers); return priv->backend->open(dev_handle); } @@ -698,8 +715,10 @@ static void windows_destroy_device(struct libusb_device *dev) static int windows_submit_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct libusb_context *ctx = TRANSFER_CTX(transfer); + struct libusb_device_handle *dev_handle = transfer->dev_handle; + struct libusb_context *ctx = HANDLE_CTX(dev_handle); struct windows_context_priv *priv = usbi_get_context_priv(ctx); + struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle); struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer); int r; @@ -722,8 +741,18 @@ static int windows_submit_transfer(struct usbi_transfer *itransfer) transfer_priv->handle = NULL; } + // Add transfer to the device handle's list + usbi_mutex_lock(&dev_handle->lock); + list_add_tail(&transfer_priv->list, &handle_priv->active_transfers); + usbi_mutex_unlock(&dev_handle->lock); + r = priv->backend->submit_transfer(itransfer); if (r != LIBUSB_SUCCESS) { + // Remove the unsuccessful transfer from the device handle's list + usbi_mutex_lock(&dev_handle->lock); + list_del(&transfer_priv->list); + usbi_mutex_unlock(&dev_handle->lock); + // Always call the backend's clear_transfer_priv() function on failure priv->backend->clear_transfer_priv(itransfer); transfer_priv->handle = NULL; @@ -772,7 +801,7 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer) else result = GetLastError(); - usbi_dbg("handling transfer %p completion with errcode %lu, length %lu", + usbi_dbg(ctx, "handling transfer %p completion with errcode %lu, length %lu", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(result), ULONG_CAST(bytes_transferred)); switch (result) { @@ -780,25 +809,25 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer) status = backend->copy_transfer_data(itransfer, bytes_transferred); break; case ERROR_GEN_FAILURE: - usbi_dbg("detected endpoint stall"); + usbi_dbg(ctx, "detected endpoint stall"); status = LIBUSB_TRANSFER_STALL; break; case ERROR_SEM_TIMEOUT: - usbi_dbg("detected semaphore timeout"); + usbi_dbg(ctx, "detected semaphore timeout"); status = LIBUSB_TRANSFER_TIMED_OUT; break; case ERROR_OPERATION_ABORTED: istatus = backend->copy_transfer_data(itransfer, bytes_transferred); if (istatus != LIBUSB_TRANSFER_COMPLETED) - usbi_dbg("failed to copy partial data in aborted operation: %d", (int)istatus); + usbi_dbg(ctx, "failed to copy partial data in aborted operation: %d", (int)istatus); - usbi_dbg("detected operation aborted"); + usbi_dbg(ctx, "detected operation aborted"); status = LIBUSB_TRANSFER_CANCELLED; break; case ERROR_FILE_NOT_FOUND: case ERROR_DEVICE_NOT_CONNECTED: case ERROR_NO_SUCH_DEVICE: - usbi_dbg("detected device removed"); + usbi_dbg(ctx, "detected device removed"); status = LIBUSB_TRANSFER_NO_DEVICE; break; default: @@ -881,6 +910,6 @@ const struct usbi_os_backend usbi_backend = { windows_handle_transfer_completion, sizeof(struct windows_context_priv), sizeof(union windows_device_priv), - sizeof(union windows_device_handle_priv), + sizeof(struct windows_device_handle_priv), sizeof(struct windows_transfer_priv), }; |