diff options
author | Andrew Thompson <thompsa@FreeBSD.org> | 2009-11-08 20:03:52 +0000 |
---|---|---|
committer | Andrew Thompson <thompsa@FreeBSD.org> | 2009-11-08 20:03:52 +0000 |
commit | ccef4ddf40fee777575fe227405159a2b6d11dee (patch) | |
tree | 977c6deb1e9004ec847ecd42fbc6b89e7dc790cc /lib | |
parent | f5a034f95a3eb1e4c66310499d671929d89b3b43 (diff) | |
download | src-ccef4ddf40fee777575fe227405159a2b6d11dee.tar.gz src-ccef4ddf40fee777575fe227405159a2b6d11dee.zip |
- fix refcounting error during data transfer
- fix a memory leak on the USB backend
- fix invalid pointer computations (in one case memory outside the allocated
area was written in LibUSB v1.0)
- make sure memory is always initialised, also in failing cases
- add missing functions from v1.0.4
PR: usb/140325
Reported by: Robert Jenssen
Submitted by: Hans Petter Selasky
MFC After: 3 days
Notes
Notes:
svn path=/head/; revision=199055
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libusb/libusb.h | 37 | ||||
-rw-r--r-- | lib/libusb/libusb10.c | 26 | ||||
-rw-r--r-- | lib/libusb/libusb10_desc.c | 34 | ||||
-rw-r--r-- | lib/libusb/libusb10_io.c | 172 | ||||
-rw-r--r-- | lib/libusb/libusb20.c | 8 | ||||
-rw-r--r-- | lib/libusb/libusb20_desc.c | 3 | ||||
-rw-r--r-- | lib/libusb/libusb20_ugen20.c | 6 |
7 files changed, 251 insertions, 35 deletions
diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h index f65b57a50394..bdf281265ddf 100644 --- a/lib/libusb/libusb.h +++ b/lib/libusb/libusb.h @@ -271,9 +271,11 @@ typedef struct libusb_control_setup { uint16_t wLength; } libusb_control_setup; +#define LIBUSB_CONTROL_SETUP_SIZE 8 /* bytes */ + typedef struct libusb_iso_packet_descriptor { - unsigned int length; - unsigned int actual_length; + uint32_t length; + uint32_t actual_length; enum libusb_transfer_status status; } libusb_iso_packet_descriptor __aligned(sizeof(void *)); @@ -282,9 +284,9 @@ typedef void (*libusb_transfer_cb_fn) (struct libusb_transfer *transfer); typedef struct libusb_transfer { libusb_device_handle *dev_handle; uint8_t flags; - unsigned int endpoint; + uint32_t endpoint; uint8_t type; - unsigned int timeout; + uint32_t timeout; enum libusb_transfer_status status; int length; int actual_length; @@ -320,7 +322,7 @@ int libusb_get_configuration(libusb_device_handle * devh, int *config); int libusb_set_configuration(libusb_device_handle * devh, int configuration); int libusb_claim_interface(libusb_device_handle * devh, int interface_number); int libusb_release_interface(libusb_device_handle * devh, int interface_number); -int libusb_reset_device(libusb_device_handle * dev); +int libusb_reset_device(libusb_device_handle * devh); int libusb_kernel_driver_active(libusb_device_handle * devh, int interface); int libusb_detach_kernel_driver(libusb_device_handle * devh, int interface); int libusb_attach_kernel_driver(libusb_device_handle * devh, int interface); @@ -333,7 +335,8 @@ int libusb_get_active_config_descriptor(libusb_device * dev, struct libusb_confi int libusb_get_config_descriptor(libusb_device * dev, uint8_t config_index, struct libusb_config_descriptor **config); int libusb_get_config_descriptor_by_value(libusb_device * dev, uint8_t bConfigurationValue, struct libusb_config_descriptor **config); void libusb_free_config_descriptor(struct libusb_config_descriptor *config); -int libusb_get_string_descriptor_ascii(libusb_device_handle * dev, uint8_t desc_index, uint8_t *data, int length); +int libusb_get_string_descriptor_ascii(libusb_device_handle * devh, uint8_t desc_index, uint8_t *data, int length); +int libusb_get_descriptor(libusb_device_handle * devh, uint8_t desc_type, uint8_t desc_index, uint8_t *data, int length); /* Asynchronous device I/O */ @@ -341,7 +344,16 @@ struct libusb_transfer *libusb_alloc_transfer(int iso_packets); void libusb_free_transfer(struct libusb_transfer *transfer); int libusb_submit_transfer(struct libusb_transfer *transfer); int libusb_cancel_transfer(struct libusb_transfer *transfer); -uint8_t *libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, unsigned int packet); +uint8_t *libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t index); +uint8_t *libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t index); +void libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, uint32_t length); +uint8_t *libusb_control_transfer_get_data(struct libusb_transfer *transfer); +struct libusb_control_setup *libusb_control_transfer_get_setup(struct libusb_transfer *transfer); +void libusb_fill_control_setup(uint8_t *buf, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength); +void libusb_fill_control_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t *buf, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); +void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); +void libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); +void libusb_fill_iso_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, int npacket, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); /* Polling and timing */ @@ -362,9 +374,14 @@ struct libusb_pollfd **libusb_get_pollfds(libusb_context * ctx); /* Synchronous device I/O */ -int libusb_control_transfer(libusb_device_handle * devh, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, unsigned int timeout); -int libusb_bulk_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout); -int libusb_interrupt_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout); +int libusb_control_transfer(libusb_device_handle * devh, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, uint32_t timeout); +int libusb_bulk_transfer(libusb_device_handle * devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, uint32_t timeout); +int libusb_interrupt_transfer(libusb_device_handle * devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, uint32_t timeout); + +/* Byte-order */ + +uint16_t libusb_cpu_to_le16(uint16_t x); +uint16_t libusb_le16_to_cpu(uint16_t x); #if 0 { /* indent fix */ diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index fa9130d7e53f..55ee1c509e76 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -35,6 +35,7 @@ #include <sys/ioctl.h> #include <sys/filio.h> #include <sys/queue.h> +#include <sys/endian.h> #include "libusb20.h" #include "libusb20_desc.h" @@ -185,8 +186,6 @@ libusb_get_device_list(libusb_context *ctx, libusb_device ***list) /* create libusb v1.0 compliant devices */ i = 0; while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { - /* get device into libUSB v1.0 list */ - libusb20_be_dequeue_device(usb_backend, pdev); dev = malloc(sizeof(*dev)); if (dev == NULL) { @@ -199,6 +198,10 @@ libusb_get_device_list(libusb_context *ctx, libusb_device ***list) libusb20_be_free(usb_backend); return (LIBUSB_ERROR_NO_MEM); } + + /* get device into libUSB v1.0 list */ + libusb20_be_dequeue_device(usb_backend, pdev); + memset(dev, 0, sizeof(*dev)); /* init transfer queues */ @@ -416,6 +419,8 @@ libusb_close(struct libusb20_device *pdev) libusb10_remove_pollfd(ctx, &dev->dev_poll); libusb20_dev_close(pdev); + + /* unref will free the "pdev" when the refcount reaches zero */ libusb_unref_device(dev); /* make sure our event loop detects the closed device */ @@ -1195,7 +1200,7 @@ libusb_submit_transfer(struct libusb_transfer *uxfer) struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_device *dev; - unsigned int endpoint; + uint32_t endpoint; int err; if (uxfer == NULL) @@ -1252,7 +1257,7 @@ libusb_cancel_transfer(struct libusb_transfer *uxfer) struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_device *dev; - unsigned int endpoint; + uint32_t endpoint; if (uxfer == NULL) return (LIBUSB_ERROR_INVALID_PARAM); @@ -1312,3 +1317,16 @@ libusb10_cancel_all_transfer(libusb_device *dev) { /* TODO */ } + +uint16_t +libusb_cpu_to_le16(uint16_t x) +{ + return (htole16(x)); +} + +uint16_t +libusb_le16_to_cpu(uint16_t x) +{ + return (le16toh(x)); +} + diff --git a/lib/libusb/libusb10_desc.c b/lib/libusb/libusb10_desc.c index c43443af939b..0215bceef006 100644 --- a/lib/libusb/libusb10_desc.c +++ b/lib/libusb/libusb10_desc.c @@ -35,6 +35,8 @@ #include "libusb.h" #include "libusb10.h" +#define N_ALIGN(n) (-((-(n)) & (-8UL))) + /* USB descriptors */ int @@ -114,17 +116,17 @@ libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, nalt = nif = pconf->num_interface; nep = 0; - nextra = pconf->extra.len; + nextra = N_ALIGN(pconf->extra.len); for (i = 0; i < nif; i++) { pinf = pconf->interface + i; - nextra += pinf->extra.len; + nextra += N_ALIGN(pinf->extra.len); nep += pinf->num_endpoints; k = pinf->num_endpoints; pend = pinf->endpoints; while (k--) { - nextra += pend->extra.len; + nextra += N_ALIGN(pend->extra.len); pend++; } @@ -132,12 +134,12 @@ libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, nalt += pinf->num_altsetting; pinf = pinf->altsetting; while (j--) { - nextra += pinf->extra.len; + nextra += N_ALIGN(pinf->extra.len); nep += pinf->num_endpoints; k = pinf->num_endpoints; pend = pinf->endpoints; while (k--) { - nextra += pend->extra.len; + nextra += N_ALIGN(pend->extra.len); pend++; } pinf++; @@ -150,17 +152,18 @@ libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, (nalt * sizeof(libusb_interface_descriptor)) + (nep * sizeof(libusb_endpoint_descriptor)); + nextra = N_ALIGN(nextra); + pconfd = malloc(nextra); if (pconfd == NULL) { free(pconf); return (LIBUSB_ERROR_NO_MEM); } - /* make sure memory is clean */ + /* make sure memory is initialised */ memset(pconfd, 0, nextra); - pconfd->interface = (libusb_interface *) (pconfd + - sizeof(libusb_config_descriptor)); + pconfd->interface = (libusb_interface *) (pconfd + 1); ifd = (libusb_interface_descriptor *) (pconfd->interface + nif); endd = (libusb_endpoint_descriptor *) (ifd + nalt); @@ -181,7 +184,7 @@ libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, pconfd->extra_length = pconf->extra.len; pconfd->extra = pextra; memcpy(pextra, pconf->extra.ptr, pconfd->extra_length); - pextra += pconfd->extra_length; + pextra += N_ALIGN(pconfd->extra_length); } /* setup all interface and endpoint pointers */ @@ -221,7 +224,7 @@ libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, ifd->extra_length = pinf->extra.len; ifd->extra = pextra; memcpy(pextra, pinf->extra.ptr, pinf->extra.len); - pextra += pinf->extra.len; + pextra += N_ALIGN(pinf->extra.len); } for (k = 0; k < pinf->num_endpoints; k++) { pend = &pinf->endpoints[k]; @@ -238,7 +241,7 @@ libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, endd->extra_length = pend->extra.len; endd->extra = pextra; memcpy(pextra, pend->extra.ptr, pend->extra.len); - pextra += pend->extra.len; + pextra += N_ALIGN(pend->extra.len); } } } @@ -304,3 +307,12 @@ libusb_get_string_descriptor_ascii(libusb_device_handle *pdev, return (LIBUSB_ERROR_OTHER); } + +int +libusb_get_descriptor(libusb_device_handle * devh, uint8_t desc_type, + uint8_t desc_index, uint8_t *data, int length) +{ + return (libusb_control_transfer(devh, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, + length, 1000)); +} diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c index a5bb85fc5285..3d6daefbc241 100644 --- a/lib/libusb/libusb10_io.c +++ b/lib/libusb/libusb10_io.c @@ -32,6 +32,7 @@ #include <time.h> #include <errno.h> #include <sys/queue.h> +#include <sys/endian.h> #include "libusb20.h" #include "libusb20_desc.h" @@ -148,19 +149,19 @@ libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) goto do_done; } for (i = 0; i != nfds; i++) { - if (fds[i].revents == 0) - continue; if (ppdev[i] != NULL) { dev = libusb_get_device(ppdev[i]); - err = libusb20_dev_process(ppdev[i]); + if (fds[i].revents == 0) + err = 0; /* nothing to do */ + else + err = libusb20_dev_process(ppdev[i]); + if (err) { /* cancel all transfers - device is gone */ libusb10_cancel_all_transfer(dev); - /* - * make sure we don't go into an infinite - * loop - */ + + /* remove USB device from polling loop */ libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); } CTX_UNLOCK(ctx); @@ -573,3 +574,160 @@ libusb_interrupt_transfer(libusb_device_handle *devh, DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer leave"); return (ret); } + +uint8_t * +libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t index) +{ + uint8_t *ptr; + uint32_t n; + + if (transfer->num_iso_packets < 0) + return (NULL); + + if (index >= (uint32_t)transfer->num_iso_packets) + return (NULL); + + ptr = transfer->buffer; + if (ptr == NULL) + return (NULL); + + for (n = 0; n != index; n++) { + ptr += transfer->iso_packet_desc[n].length; + } + return (ptr); +} + +uint8_t * +libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t index) +{ + uint8_t *ptr; + + if (transfer->num_iso_packets < 0) + return (NULL); + + if (index >= (uint32_t)transfer->num_iso_packets) + return (NULL); + + ptr = transfer->buffer; + if (ptr == NULL) + return (NULL); + + ptr += transfer->iso_packet_desc[0].length * index; + + return (ptr); +} + +void +libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, uint32_t length) +{ + int n; + + if (transfer->num_iso_packets < 0) + return; + + for (n = 0; n != transfer->num_iso_packets; n++) + transfer->iso_packet_desc[n].length = length; +} + +uint8_t * +libusb_control_transfer_get_data(struct libusb_transfer *transfer) +{ + if (transfer->buffer == NULL) + return (NULL); + + return (transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE); +} + +struct libusb_control_setup * +libusb_control_transfer_get_setup(struct libusb_transfer *transfer) +{ + return ((struct libusb_control_setup *)transfer->buffer); +} + +void +libusb_fill_control_setup(uint8_t *buf, uint8_t bmRequestType, + uint8_t bRequest, uint16_t wValue, + uint16_t wIndex, uint16_t wLength) +{ + struct libusb_control_setup *req = (struct libusb_control_setup *)buf; + + /* The alignment is OK for all fields below. */ + req->bmRequestType = bmRequestType; + req->bRequest = bRequest; + req->wValue = htole16(wValue); + req->wIndex = htole16(wIndex); + req->wLength = htole16(wLength); +} + +void +libusb_fill_control_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t *buf, + libusb_transfer_cb_fn callback, void *user_data, + uint32_t timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)buf; + + transfer->dev_handle = devh; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buf; + if (setup != NULL) + transfer->length = LIBUSB_CONTROL_SETUP_SIZE + + le16toh(setup->wLength); + else + transfer->length = 0; + transfer->user_data = user_data; + transfer->callback = callback; + +} + +void +libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, + int length, libusb_transfer_cb_fn callback, void *user_data, + uint32_t timeout) +{ + transfer->dev_handle = devh; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buf; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +void +libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, + int length, libusb_transfer_cb_fn callback, void *user_data, + uint32_t timeout) +{ + transfer->dev_handle = devh; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buf; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +void +libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, + int length, int npacket, libusb_transfer_cb_fn callback, + void *user_data, uint32_t timeout) +{ + transfer->dev_handle = devh; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buf; + transfer->length = length; + transfer->num_iso_packets = npacket; + transfer->user_data = user_data; + transfer->callback = callback; +} + diff --git a/lib/libusb/libusb20.c b/lib/libusb/libusb20.c index 6e0ec476f3d3..1a338052c939 100644 --- a/lib/libusb/libusb20.c +++ b/lib/libusb/libusb20.c @@ -630,6 +630,9 @@ libusb20_dev_req_string_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED req; int error; + /* make sure memory is initialised */ + memset(ptr, 0, len); + if (len < 4) { /* invalid length */ return (LIBUSB20_ERROR_INVALID_PARAM); @@ -1093,7 +1096,8 @@ libusb20_be_free(struct libusb20_backend *pbe) if (pbe->methods->exit_backend) { pbe->methods->exit_backend(pbe); } - return; + /* free backend */ + free(pbe); } void @@ -1101,7 +1105,6 @@ libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device { pdev->beMethods = pbe->methods; /* copy backend methods */ TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry); - return; } void @@ -1109,5 +1112,4 @@ libusb20_be_dequeue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) { TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry); - return; } diff --git a/lib/libusb/libusb20_desc.c b/lib/libusb/libusb20_desc.c index e0d2c54b9080..5206cf457570 100644 --- a/lib/libusb/libusb20_desc.c +++ b/lib/libusb/libusb20_desc.c @@ -118,6 +118,9 @@ libusb20_parse_config_desc(const void *config_desc) if (lub_config == NULL) { return (NULL); /* out of memory */ } + /* make sure memory is initialised */ + memset(lub_config, 0, size); + lub_interface = (void *)(lub_config + 1); lub_alt_interface = (void *)(lub_interface + niface_no_alt); lub_endpoint = (void *)(lub_interface + niface); diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c index f9f36891a034..efcca63a2095 100644 --- a/lib/libusb/libusb20_ugen20.c +++ b/lib/libusb/libusb20_ugen20.c @@ -449,6 +449,8 @@ ugen20_get_config_desc_full(struct libusb20_device *pdev, uint16_t len; int error; + /* make sure memory is initialised */ + memset(&cdesc, 0, sizeof(cdesc)); memset(&gen_desc, 0, sizeof(gen_desc)); gen_desc.ugd_data = &cdesc; @@ -468,6 +470,10 @@ ugen20_get_config_desc_full(struct libusb20_device *pdev, if (!ptr) { return (LIBUSB20_ERROR_NO_MEM); } + + /* make sure memory is initialised */ + memset(ptr, 0, len); + gen_desc.ugd_data = ptr; gen_desc.ugd_maxlen = len; |