core: Move parameter validation from backend to core

Some functions (e.g. libusb_set_interface_alt_setting()) do not perform
sufficient parameter validation, leaving the burden on the backend to
catch invalid user input. Much of this validation is common across all
backends, yet not every backend implemented it. Fix this by moving
parameter validation to the core library functions.

This is also a good opportunity to remove the redundant
'num_configurations' field from the libusb_device structure. The value
of this field is already contained in the 'device_descriptor' member.

Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
diff --git a/libusb/core.c b/libusb/core.c
index 85d62e4..7ffec1f 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -746,10 +746,10 @@
 	if (num_configurations > USB_MAXCONFIG) {
 		usbi_err(DEVICE_CTX(dev), "too many configurations");
 		return LIBUSB_ERROR_IO;
-	} else if (0 == num_configurations)
+	} else if (0 == num_configurations) {
 		usbi_dbg("zero configurations, maybe an unauthorized device");
+	}
 
-	dev->num_configurations = num_configurations;
 	return 0;
 }
 
@@ -1636,6 +1636,8 @@
 	int configuration)
 {
 	usbi_dbg("configuration %d", configuration);
+	if (configuration < -1 || configuration > UINT8_MAX)
+		return LIBUSB_ERROR_INVALID_PARAM;
 	return usbi_backend.set_configuration(dev_handle, configuration);
 }
 
@@ -1673,7 +1675,7 @@
 	int r = 0;
 
 	usbi_dbg("interface %d", interface_number);
-	if (interface_number >= USB_MAXINTERFACES)
+	if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
 		return LIBUSB_ERROR_INVALID_PARAM;
 
 	if (!dev_handle->dev->attached)
@@ -1717,7 +1719,7 @@
 	int r;
 
 	usbi_dbg("interface %d", interface_number);
-	if (interface_number >= USB_MAXINTERFACES)
+	if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
 		return LIBUSB_ERROR_INVALID_PARAM;
 
 	usbi_mutex_lock(&dev_handle->lock);
@@ -1761,7 +1763,9 @@
 {
 	usbi_dbg("interface %d altsetting %d",
 		interface_number, alternate_setting);
-	if (interface_number >= USB_MAXINTERFACES)
+	if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+		return LIBUSB_ERROR_INVALID_PARAM;
+	if (alternate_setting < 0 || alternate_setting > UINT8_MAX)
 		return LIBUSB_ERROR_INVALID_PARAM;
 
 	usbi_mutex_lock(&dev_handle->lock);
@@ -1858,7 +1862,10 @@
 int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
 	uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
 {
-	usbi_dbg("streams %u eps %d", (unsigned) num_streams, num_endpoints);
+	usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints);
+
+	if (!num_streams || !endpoints || num_endpoints <= 0)
+		return LIBUSB_ERROR_INVALID_PARAM;
 
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
@@ -1887,6 +1894,9 @@
 {
 	usbi_dbg("eps %d", num_endpoints);
 
+	if (!endpoints || num_endpoints <= 0)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
@@ -1973,6 +1983,9 @@
 {
 	usbi_dbg("interface %d", interface_number);
 
+	if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
@@ -2008,6 +2021,9 @@
 {
 	usbi_dbg("interface %d", interface_number);
 
+	if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
@@ -2042,6 +2058,9 @@
 {
 	usbi_dbg("interface %d", interface_number);
 
+	if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index 4c7733d..44237dd 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -617,7 +617,7 @@
 	int r;
 
 	usbi_dbg("index %d", config_index);
-	if (config_index >= dev->num_configurations)
+	if (config_index >= dev->device_descriptor.bNumConfigurations)
 		return LIBUSB_ERROR_NOT_FOUND;
 
 	r = usbi_backend.get_config_descriptor(dev, config_index, tmp,
@@ -655,7 +655,7 @@
 	uint8_t i;
 
 	usbi_dbg("value %d", bConfigurationValue);
-	for (i = 0; i < dev->num_configurations; i++) {
+	for (i = 0; i < dev->device_descriptor.bNumConfigurations; i++) {
 		unsigned char tmp[6];
 		int host_endian;
 		int r = usbi_backend.get_config_descriptor(dev, i, tmp, sizeof(tmp),
diff --git a/libusb/io.c b/libusb/io.c
index b843d4a..09ccc83 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -1261,6 +1261,8 @@
 	struct libusb_transfer *transfer;
 
 	assert(iso_packets >= 0);
+	if (iso_packets < 0)
+		return NULL;
 
 	priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
 	alloc_size = priv_size
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index faaeff3..1fdfc84 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -429,12 +429,11 @@
 	int refcnt;
 
 	struct libusb_context *ctx;
+	struct libusb_device *parent_dev;
 
 	uint8_t bus_number;
 	uint8_t port_number;
-	struct libusb_device* parent_dev;
 	uint8_t device_address;
-	uint8_t num_configurations;
 	enum libusb_speed speed;
 
 	struct list_head list;
diff --git a/libusb/os/haiku_usb_raw.cpp b/libusb/os/haiku_usb_raw.cpp
index 3162371..63efc17 100644
--- a/libusb/os/haiku_usb_raw.cpp
+++ b/libusb/os/haiku_usb_raw.cpp
@@ -97,7 +97,7 @@
 	const usb_configuration_descriptor *config = dev->ConfigurationDescriptor(config_index);
 	if (config == NULL) {
 		usbi_err(DEVICE_CTX(device), "failed getting configuration descriptor");
-		return LIBUSB_ERROR_INVALID_PARAM;
+		return LIBUSB_ERROR_IO;
 	}
 	if (len > config->total_length) {
 		len = config->total_length;
diff --git a/libusb/os/sunos_usb.c b/libusb/os/sunos_usb.c
index 8d0c812..cd32f8b 100644
--- a/libusb/os/sunos_usb.c
+++ b/libusb/os/sunos_usb.c
@@ -1109,8 +1109,8 @@
 	if (dpriv->ugenpath == NULL)
 		return (LIBUSB_ERROR_NOT_SUPPORTED);
 
-	if (config < 1 || config > dpriv->dev_descr.bNumConfigurations)
-		return (LIBUSB_ERROR_INVALID_PARAM);
+	if (config < 1)
+		return (LIBUSB_ERROR_NOT_SUPPORTED);
 
 	dpriv->cfgvalue = config;
 	hpriv->config_index = config - 1;
@@ -1122,9 +1122,6 @@
 sunos_claim_interface(struct libusb_device_handle *handle, int iface)
 {
 	usbi_dbg("iface %d", iface);
-	if (iface < 0) {
-		return (LIBUSB_ERROR_INVALID_PARAM);
-	}
 
 	return (LIBUSB_SUCCESS);
 }
@@ -1135,9 +1132,6 @@
 	sunos_dev_handle_priv_t *hpriv = usbi_get_device_handle_priv(handle);
 
 	usbi_dbg("iface %d", iface);
-	if (iface < 0) {
-		return (LIBUSB_ERROR_INVALID_PARAM);
-	}
 
 	/* XXX: can we release it? */
 	hpriv->altsetting[iface] = 0;
@@ -1154,9 +1148,6 @@
 
 	usbi_dbg("iface %d, setting %d", iface, altsetting);
 
-	if (iface < 0 || altsetting < 0) {
-		return (LIBUSB_ERROR_INVALID_PARAM);
-	}
 	if (dpriv->ugenpath == NULL)
 		return (LIBUSB_ERROR_NOT_FOUND);
 
diff --git a/libusb/os/windows_usbdk.c b/libusb/os/windows_usbdk.c
index 9c3e8fd..bb370ca 100644
--- a/libusb/os/windows_usbdk.c
+++ b/libusb/os/windows_usbdk.c
@@ -286,7 +286,6 @@
 	// Addresses in libusb are 1-based
 	dev->device_address = (uint8_t)(info->Port + 1);
 
-	dev->num_configurations = info->DeviceDescriptor.bNumConfigurations;
 	memcpy(&dev->device_descriptor, &info->DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE);
 
 	switch (info->Speed) {
@@ -373,9 +372,6 @@
 	PUSB_CONFIGURATION_DESCRIPTOR config_header;
 	size_t size;
 
-	if (config_index >= dev->num_configurations)
-		return LIBUSB_ERROR_INVALID_PARAM;
-
 	config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptors[config_index];
 
 	size = min(config_header->wTotalLength, len);
@@ -390,7 +386,7 @@
 	PUSB_CONFIGURATION_DESCRIPTOR config_header;
 	uint8_t index;
 
-	for (index = 0; index < dev->num_configurations; index++) {
+	for (index = 0; index < dev->device_descriptor.bNumConfigurations; index++) {
 		config_header = priv->config_descriptors[index];
 		if (config_header->bConfigurationValue == bConfigurationValue) {
 			*buffer = (unsigned char *)priv->config_descriptors[index];
diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c
index ef6fbd2..245df90 100644
--- a/libusb/os/windows_winusb.c
+++ b/libusb/os/windows_winusb.c
@@ -681,27 +681,28 @@
 	struct libusb_context *ctx = DEVICE_CTX(dev);
 	struct winusb_device_priv *priv = usbi_get_device_priv(dev);
 	DWORD size, ret_size;
-	uint8_t i;
+	uint8_t i, num_configurations;
 
 	USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short; // dummy request
 	PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL;    // actual request
 	PUSB_CONFIGURATION_DESCRIPTOR cd_data;
 
-	if (dev->num_configurations == 0)
+	num_configurations = priv->dev_descriptor.bNumConfigurations;
+	if (num_configurations == 0)
 		return;
 
 	assert(sizeof(USB_DESCRIPTOR_REQUEST) == USB_DESCRIPTOR_REQUEST_SIZE);
 
-	priv->config_descriptor = calloc(dev->num_configurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR));
+	priv->config_descriptor = calloc(num_configurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR));
 	if (priv->config_descriptor == NULL) {
 		usbi_err(ctx, "could not allocate configuration descriptor array for '%s'", priv->dev_id);
 		return;
 	}
 
-	for (i = 0; i <= dev->num_configurations; i++) {
+	for (i = 0; i <= num_configurations; i++) {
 		safe_free(cd_buf_actual);
 
-		if (i == dev->num_configurations)
+		if (i == num_configurations)
 			break;
 
 		size = sizeof(cd_buf_short);
@@ -860,13 +861,12 @@
 			}
 
 			memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR));
-			dev->num_configurations = conn_info.DeviceDescriptor.bNumConfigurations;
 			priv->active_config = conn_info.CurrentConfigurationValue;
 			if (priv->active_config == 0) {
 				usbi_dbg("0x%x:0x%x found %u configurations (not configured)",
 					priv->dev_descriptor.idVendor,
 					priv->dev_descriptor.idProduct,
-					dev->num_configurations);
+					priv->dev_descriptor.bNumConfigurations);
 				SleepEx(50, TRUE);
 			}
 		} while (priv->active_config == 0 && --ginfotimeout >= 0);
@@ -876,10 +876,10 @@
 				"forcing current configuration to 1",
 				priv->dev_descriptor.idVendor,
 				priv->dev_descriptor.idProduct,
-				dev->num_configurations);
+				priv->dev_descriptor.bNumConfigurations);
 			priv->active_config = 1;
 		} else {
-			usbi_dbg("found %u configurations (current config: %u)", dev->num_configurations, priv->active_config);
+			usbi_dbg("found %u configurations (current config: %u)", priv->dev_descriptor.bNumConfigurations, priv->active_config);
 		}
 
 		// Cache as many config descriptors as we can
@@ -957,7 +957,6 @@
 		usbi_dbg("assigning HCD '%s' bus number %u", dev_id, bus_number);
 		priv = usbi_get_device_priv(dev);
 		dev->bus_number = bus_number;
-		dev->num_configurations = 1;
 		priv->dev_descriptor.bLength = LIBUSB_DT_DEVICE_SIZE;
 		priv->dev_descriptor.bDescriptorType = LIBUSB_DT_DEVICE;
 		priv->dev_descriptor.bDeviceClass = LIBUSB_CLASS_HUB;
@@ -1543,10 +1542,6 @@
 	PUSB_CONFIGURATION_DESCRIPTOR config_header;
 	size_t size;
 
-	// config index is zero based
-	if (config_index >= dev->num_configurations)
-		return LIBUSB_ERROR_INVALID_PARAM;
-
 	if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL))
 		return LIBUSB_ERROR_NOT_FOUND;
 
@@ -1567,7 +1562,7 @@
 	if (priv->config_descriptor == NULL)
 		return LIBUSB_ERROR_NOT_FOUND;
 
-	for (index = 0; index < dev->num_configurations; index++) {
+	for (index = 0; index < dev->device_descriptor.bNumConfigurations; index++) {
 		config_header = priv->config_descriptor[index];
 		if (config_header == NULL)
 			continue;
@@ -1641,9 +1636,6 @@
 	struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
 	int r = LIBUSB_SUCCESS;
 
-	if (config >= USB_MAXCONFIG)
-		return LIBUSB_ERROR_INVALID_PARAM;
-
 	r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT |
 		LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
 		LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config,
@@ -2478,9 +2470,6 @@
 
 	CHECK_WINUSBX_AVAILABLE(sub_api);
 
-	if (altsetting > 255)
-		return LIBUSB_ERROR_INVALID_PARAM;
-
 	winusb_handle = handle_priv->interface_handle[iface].api_handle;
 	if (!HANDLE_VALID(winusb_handle)) {
 		usbi_err(HANDLE_CTX(dev_handle), "interface must be claimed first");
@@ -3592,9 +3581,6 @@
 
 	CHECK_HID_AVAILABLE;
 
-	if (altsetting > 255)
-		return LIBUSB_ERROR_INVALID_PARAM;
-
 	if (altsetting != 0) {
 		usbi_err(HANDLE_CTX(dev_handle), "set interface altsetting not supported for altsetting >0");
 		return LIBUSB_ERROR_NOT_SUPPORTED;
@@ -3615,7 +3601,7 @@
 	OVERLAPPED *overlapped;
 	int current_interface, config;
 	size_t size;
-	int r = LIBUSB_ERROR_INVALID_PARAM;
+	int r;
 
 	UNUSED(sub_api);
 	CHECK_HID_AVAILABLE;
diff --git a/libusb/os/windows_winusb.h b/libusb/os/windows_winusb.h
index c8d3f15..0340fe7 100644
--- a/libusb/os/windows_winusb.h
+++ b/libusb/os/windows_winusb.h
@@ -220,8 +220,8 @@
 
 	free(priv->dev_id);
 	free(priv->path);
-	if ((dev->num_configurations > 0) && (priv->config_descriptor != NULL)) {
-		for (i = 0; i < dev->num_configurations; i++) {
+	if ((priv->dev_descriptor.bNumConfigurations > 0) && (priv->config_descriptor != NULL)) {
+		for (i = 0; i < priv->dev_descriptor.bNumConfigurations; i++) {
 			if (priv->config_descriptor[i] == NULL)
 				continue;
 			free((UCHAR *)priv->config_descriptor[i] - USB_DESCRIPTOR_REQUEST_SIZE);
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 3804599..a744f2f 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11464
+#define LIBUSB_NANO 11465