[BUG] UBSAN: shift-out-of-bounds in dln2_spi_probe

Farhad Alemi posted 1 patch 2 weeks, 1 day ago
[BUG] UBSAN: shift-out-of-bounds in dln2_spi_probe
Posted by Farhad Alemi 2 weeks, 1 day ago
Hello,

We are reporting the following crash (reproducer and a proposed patch attached):

Linux version 7.3.0-rc2 50d05c7c76c96b90462f24debacca971d2e86713
Build Config: UpstreamAppArmorKASAN (KASAN + UBSAN, panic_on_warn=1)

[   50.563179][  T987] usb 3-1: new high-speed USB device number 2
using dummy_hcd
[   50.724668][  T987] usb 3-1: New USB device found, idVendor=a257,
idProduct=2013, bcdDevice= 1.00
[   50.728082][  T987] usb 3-1: New USB device strings: Mfr=0,
Product=0, SerialNumber=0
[   50.757406][  T987] dln2 3-1:1.0: Diolan DLN2 serial 0
[   50.763502][  T987] gpiochip_add_data_with_key: GPIOs 0..-1 (dln2)
failed to register, -22
[   50.763517][  T987] dln2-gpio dln2-gpio.2.auto: failed to add gpio chip: -22
[   50.763525][  T987] dln2-gpio dln2-gpio.2.auto: probe with driver
dln2-gpio failed with error -22
[   50.789064][  T987] ------------[ cut here ]------------
[   50.789071][  T987] UBSAN: shift-out-of-bounds in
drivers/spi/spi-dln2.c:179:15
[   50.789083][  T987] shift exponent 18446744073709543487 is too
large for 64-bit type 'typeof(unsigned long)' (aka 'unsigned long')
[   50.789098][  T987] CPU: 1 UID: 0 PID: 987 Comm: kworker/1:3 Not
tainted 7.3.0-rc2-00099-g50d05c7c76c9 #1 PREEMPT(full)
[   50.789108][  T987] Hardware name: QEMU Standard PC (Q35 + ICH9,
2009), BIOS 1.17.0-debian-1.17.0-1ubuntu1 04/01/2014
[   50.789114][  T987] Workqueue: usb_hub_wq hub_event
[   50.791092][  T987] Call Trace:
[   50.791098][  T987]  <TASK>
[   50.791106][  T987]  dump_stack_lvl+0xe8/0x150
[   50.791118][  T987]  ubsan_epilogue+0xa/0x30
[   50.791129][  T987]  __ubsan_handle_shift_out_of_bounds+0x385/0x410
[
** replaying previous printk message **
[   50.791149][  T987]  dln2_spi_probe+0x104d/0x13c0
[   50.791165][  T987]  ? __pfx_dln2_spi_probe+0x10/0x10
[   50.791178][  T987]  ? up_write+0x1a5/0x4a0
[   50.791189][  T987]  ? kernfs_put+0x77b/0x7e0
[   50.791200][  T987]  ? acpi_has_method+0x20/0xe0
[   50.791211][  T987]  ? dev_pm_domain_attach+0xbd/0xf0
[   50.791222][  T987]  ? __pfx_dln2_spi_probe+0x10/0x10
[   50.791232][  T987]  platform_probe+0xf9/0x190
[   50.791241][  T987]  ? __pfx_platform_probe+0x10/0x10
[   50.791248][  T987]  really_probe+0x267/0xaf0
[   50.791259][  T987]  __driver_probe_device+0x1e2/0x350
[   50.791269][  T987]  driver_probe_device+0x4f/0x240
[   50.791279][  T987]  __device_attach_driver+0x270/0x410
[   50.791290][  T987]  bus_for_each_drv+0x258/0x2f0
[   50.791301][  T987]  ? __pfx___device_attach_driver+0x10/0x10
[   50.791310][  T987]  ? __pfx_bus_for_each_drv+0x10/0x10
[   50.791321][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.792223][  T987]  __device_attach+0x2c5/0x450
[   50.792233][  T987]  ? __pfx___device_attach+0x10/0x10
[   50.792243][  T987]  ? _raw_spin_unlock+0x28/0x50
[   50.792254][  T987]  device_initial_probe+0xa1/0xd0
[   50.792263][  T987]  bus_probe_device+0x12a/0x220
[   50.792275][  T987]  device_add+0x7ec/0xb90
[   50.792285][  T987]  platform_device_add+0x46a/0x800
[   50.792296][  T987]  ? __asan_memcpy+0x40/0x70
[   50.792311][  T987]  mfd_add_devices+0x125c/0x17c0
[   50.792322][  T987]  ? __dev_printk+0x131/0x190
[   50.792339][  T987]  ? __pfx_mfd_add_devices+0x10/0x10
[   50.792353][  T987]  ? dln2_hw_init+0x25c/0x270
[   50.792362][  T987]  ? __pfx_dln2_hw_init+0x10/0x10
[   50.792374][  T987]  dln2_probe+0xef9/0xfd0
[   50.792383][  T987]  ? __pfx_do_raw_spin_lock+0x10/0x10
[   50.792398][  T987]  ? __pfx_dln2_probe+0x10/0x10
[   50.792406][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.792416][  T987]  ? __pm_runtime_set_status+0x9d4/0xcd0
[   50.792428][  T987]  usb_probe_interface+0x71f/0xe00
[   50.792443][  T987]  ? __pfx_usb_probe_interface+0x10/0x10
[   50.792457][  T987]  ? driver_sysfs_add+0x1fe/0x210
[   50.792466][  T987]  ? __pfx_usb_probe_interface+0x10/0x10
[   50.792475][  T987]  really_probe+0x267/0xaf0
[   50.792486][  T987]  __driver_probe_device+0x1e2/0x350
[   50.792496][  T987]  driver_probe_device+0x4f/0x240
[   50.792506][  T987]  __device_attach_driver+0x270/0x410
[   50.792517][  T987]  bus_for_each_drv+0x258/0x2f0
[   50.792527][  T987]  ? __pfx___device_attach_driver+0x10/0x10
[   50.792536][  T987]  ? __pfx_bus_for_each_drv+0x10/0x10
[   50.792548][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.792560][  T987]  __device_attach+0x2c5/0x450
[   50.792569][  T987]  ? __pfx___device_attach+0x10/0x10
[   50.792579][  T987]  ? _raw_spin_unlock+0x28/0x50
[   50.792590][  T987]  device_initial_probe+0xa1/0xd0
[   50.792599][  T987]  bus_probe_device+0x12a/0x220
[   50.792611][  T987]  device_add+0x7ec/0xb90
[   50.792621][  T987]  usb_set_configuration+0x1a87/0x2110
[   50.792642][  T987]  usb_generic_driver_probe+0x8d/0x150
[   50.792652][  T987]  usb_probe_device+0x1c4/0x3b0
[   50.792664][  T987]  ? __pfx_usb_probe_device+0x10/0x10
[   50.792673][  T987]  really_probe+0x267/0xaf0
[   50.792684][  T987]  __driver_probe_device+0x1e2/0x350
[   50.792694][  T987]  driver_probe_device+0x4f/0x240
[   50.792704][  T987]  __device_attach_driver+0x270/0x410
[   50.792714][  T987]  bus_for_each_drv+0x258/0x2f0
[   50.792724][  T987]  ? __pfx___device_attach_driver+0x10/0x10
[   50.792733][  T987]  ? __pfx_bus_for_each_drv+0x10/0x10
[   50.792745][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.792756][  T987]  __device_attach+0x2c5/0x450
[   50.792766][  T987]  ? __pfx___device_attach+0x10/0x10
[   50.792776][  T987]  ? _raw_spin_unlock+0x28/0x50
[   50.792786][  T987]  device_initial_probe+0xa1/0xd0
[   50.792796][  T987]  bus_probe_device+0x12a/0x220
[   50.792807][  T987]  device_add+0x7ec/0xb90
[   50.792817][  T987]  usb_new_device+0xa14/0x1720
[   50.792830][  T987]  ? __pfx_usb_new_device+0x10/0x10
[   50.792840][  T987]  ? _raw_spin_unlock_irq+0x23/0x50
[   50.792851][  T987]  hub_event+0x2a1c/0x4f30
[   50.792860][  T987]  ? do_raw_spin_unlock+0xf5/0x200
[   50.792888][  T987]  ? __pfx_hub_event+0x10/0x10
[   50.792901][  T987]  ? process_scheduled_works+0x94e/0x1900
[   50.792913][  T987]  ? process_scheduled_works+0x94e/0x1900
[   50.792923][  T987]  ? process_scheduled_works+0x94e/0x1900
[   50.792935][  T987]  process_scheduled_works+0xc99/0x1900
[   50.792959][  T987]  ? __pfx_process_scheduled_works+0x10/0x10
[   50.792973][  T987]  ? assign_work+0x3d5/0x5e0
[   50.792987][  T987]  worker_thread+0xa53/0xfc0
[   50.793006][  T987]  kthread+0x38b/0x480
[   50.793015][  T987]  ? __pfx_worker_thread+0x10/0x10
[   50.793022][  T987]  ? __pfx_kthread+0x10/0x10
[   50.793031][  T987]  ret_from_fork+0x514/0xb70
[   50.793042][  T987]  ? __pfx_ret_from_fork+0x10/0x10
[   50.793086][  T987]  ? __switch_to+0xc79/0x1410
[   50.793096][  T987]  ? __pfx_kthread+0x10/0x10
[   50.793106][  T987]  ret_from_fork_asm+0x1a/0x30
[   50.793123][  T987]  </TASK>
[   50.800603][  T987] ---[ end trace ]---
[   50.800625][  T987] Kernel panic - not syncing: UBSAN: panic_on_warn set ...
[   50.853815][  T987] CPU: 1 UID: 0 PID: 987 Comm: kworker/1:3 Not
tainted 7.3.0-rc2-00099-g50d05c7c76c9 #1 PREEMPT(full)
[   50.854873][  T987] Hardware name: QEMU Standard PC (Q35 + ICH9,
2009), BIOS 1.17.0-debian-1.17.0-1ubuntu1 04/01/2014
[   50.855914][  T987] Workqueue: usb_hub_wq hub_event
[   50.856422][  T987] Call Trace:
[   50.856764][  T987]  <TASK>
[   50.857132][  T987]  vpanic+0x56d/0xa60
[   50.857549][  T987]  ? __pfx_vpanic+0x10/0x10
[   50.858014][  T987]  ? irqentry_exit+0x218/0x760
[   50.858498][  T987]  panic+0xc5/0xd0
[   50.858872][  T987]  ? __pfx_panic+0x10/0x10
[   50.859323][  T987]  ? __pfx__printk+0x10/0x10
[   50.859785][  T987]  check_panic_on_warn+0x89/0xb0
[   50.860298][  T987]  __ubsan_handle_shift_out_of_bounds+0x385/0x410
[   50.860938][  T987]  dln2_spi_probe+0x104d/0x13c0
[   50.861434][  T987]  ? __pfx_dln2_spi_probe+0x10/0x10
[   50.861950][  T987]  ? up_write+0x1a5/0x4a0
[   50.862387][  T987]  ? kernfs_put+0x77b/0x7e0
[   50.862841][  T987]  ? acpi_has_method+0x20/0xe0
[   50.863325][  T987]  ? dev_pm_domain_attach+0xbd/0xf0
[   50.863841][  T987]  ? __pfx_dln2_spi_probe+0x10/0x10
[   50.864370][  T987]  platform_probe+0xf9/0x190
[   50.864838][  T987]  ? __pfx_platform_probe+0x10/0x10
[   50.865355][  T987]  really_probe+0x267/0xaf0
[   50.865868][  T987]  __driver_probe_device+0x1e2/0x350
[   50.866498][  T987]  driver_probe_device+0x4f/0x240
[   50.867121][  T987]  __device_attach_driver+0x270/0x410
[   50.867757][  T987]  bus_for_each_drv+0x258/0x2f0
[   50.868376][  T987]  ? __pfx___device_attach_driver+0x10/0x10
[   50.868990][  T987]  ? __pfx_bus_for_each_drv+0x10/0x10
[   50.869559][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.870253][  T987]  __device_attach+0x2c5/0x450
[   50.870822][  T987]  ? __pfx___device_attach+0x10/0x10
[   50.871465][  T987]  ? _raw_spin_unlock+0x28/0x50
[   50.872043][  T987]  device_initial_probe+0xa1/0xd0
[   50.872619][  T987]  bus_probe_device+0x12a/0x220
[   50.873137][  T987]  device_add+0x7ec/0xb90
[   50.873595][  T987]  platform_device_add+0x46a/0x800
[   50.874110][  T987]  ? __asan_memcpy+0x40/0x70
[   50.874573][  T987]  mfd_add_devices+0x125c/0x17c0
[   50.875072][  T987]  ? __dev_printk+0x131/0x190
[   50.875546][  T987]  ? __pfx_mfd_add_devices+0x10/0x10
[   50.876076][  T987]  ? dln2_hw_init+0x25c/0x270
[   50.876543][  T987]  ? __pfx_dln2_hw_init+0x10/0x10
[   50.877172][  T987]  dln2_probe+0xef9/0xfd0
[   50.877622][  T987]  ? __pfx_do_raw_spin_lock+0x10/0x10
[   50.878200][  T987]  ? __pfx_dln2_probe+0x10/0x10
[   50.878718][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.879294][  T987]  ? __pm_runtime_set_status+0x9d4/0xcd0
[   50.879850][  T987]  usb_probe_interface+0x71f/0xe00
[   50.880399][  T987]  ? __pfx_usb_probe_interface+0x10/0x10
[   50.880986][  T987]  ? driver_sysfs_add+0x1fe/0x210
[   50.881526][  T987]  ? __pfx_usb_probe_interface+0x10/0x10
[   50.882095][  T987]  really_probe+0x267/0xaf0
[   50.882548][  T987]  __driver_probe_device+0x1e2/0x350
[   50.883073][  T987]  driver_probe_device+0x4f/0x240
[   50.883573][  T987]  __device_attach_driver+0x270/0x410
[   50.884141][  T987]  bus_for_each_drv+0x258/0x2f0
[   50.884656][  T987]  ? __pfx___device_attach_driver+0x10/0x10
[   50.885280][  T987]  ? __pfx_bus_for_each_drv+0x10/0x10
[   50.885833][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.886446][  T987]  __device_attach+0x2c5/0x450
[   50.886950][  T987]  ? __pfx___device_attach+0x10/0x10
[   50.887564][  T987]  ? _raw_spin_unlock+0x28/0x50
[   50.888047][  T987]  device_initial_probe+0xa1/0xd0
[   50.888568][  T987]  bus_probe_device+0x12a/0x220
[   50.889062][  T987]  device_add+0x7ec/0xb90
[   50.889499][  T987]  usb_set_configuration+0x1a87/0x2110
[   50.890070][  T987]  usb_generic_driver_probe+0x8d/0x150
[   50.890610][  T987]  usb_probe_device+0x1c4/0x3b0
[   50.891161][  T987]  ? __pfx_usb_probe_device+0x10/0x10
[   50.891711][  T987]  really_probe+0x267/0xaf0
[   50.892200][  T987]  __driver_probe_device+0x1e2/0x350
[   50.892750][  T987]  driver_probe_device+0x4f/0x240
[   50.893307][  T987]  __device_attach_driver+0x270/0x410
[   50.893839][  T987]  bus_for_each_drv+0x258/0x2f0
[   50.894328][  T987]  ? __pfx___device_attach_driver+0x10/0x10
[   50.894910][  T987]  ? __pfx_bus_for_each_drv+0x10/0x10
[   50.895450][  T987]  ? _raw_spin_unlock_irqrestore+0x4c/0x80
[   50.896027][  T987]  __device_attach+0x2c5/0x450
[   50.896514][  T987]  ? __pfx___device_attach+0x10/0x10
[   50.897117][  T987]  ? _raw_spin_unlock+0x28/0x50
[   50.897603][  T987]  device_initial_probe+0xa1/0xd0
[   50.898107][  T987]  bus_probe_device+0x12a/0x220
[   50.898591][  T987]  device_add+0x7ec/0xb90
[   50.899061][  T987]  usb_new_device+0xa14/0x1720
[   50.899569][  T987]  ? __pfx_usb_new_device+0x10/0x10
[   50.900123][  T987]  ? _raw_spin_unlock_irq+0x23/0x50
[   50.900638][  T987]  hub_event+0x2a1c/0x4f30
[   50.901089][  T987]  ? do_raw_spin_unlock+0xf5/0x200
[   50.901613][  T987]  ? __pfx_hub_event+0x10/0x10
[   50.902109][  T987]  ? process_scheduled_works+0x94e/0x1900
[   50.902674][  T987]  ? process_scheduled_works+0x94e/0x1900
[   50.903247][  T987]  ? process_scheduled_works+0x94e/0x1900
[   50.903810][  T987]  process_scheduled_works+0xc99/0x1900
[   50.904376][  T987]  ? __pfx_process_scheduled_works+0x10/0x10
[   50.904968][  T987]  ? assign_work+0x3d5/0x5e0
[   50.905442][  T987]  worker_thread+0xa53/0xfc0
[   50.905917][  T987]  kthread+0x38b/0x480
[   50.906331][  T987]  ? __pfx_worker_thread+0x10/0x10
[   50.906838][  T987]  ? __pfx_kthread+0x10/0x10
[   50.907416][  T987]  ret_from_fork+0x514/0xb70
[   50.907877][  T987]  ? __pfx_ret_from_fork+0x10/0x10
[   50.908401][  T987]  ? __switch_to+0xc79/0x1410
[   50.908858][  T987]  ? __pfx_kthread+0x10/0x10
[   50.909334][  T987]  ret_from_fork_asm+0x1a/0x30
[   50.909818][  T987]  </TASK>
[   50.910402][  T987] Kernel Offset: disabled
[   50.910950][  T987] Rebooting in 86400 seconds..

Regards,
SEFCOM Lab @ ASU
--- a/drivers/spi/spi-dln2.c
+++ b/drivers/spi/spi-dln2.c
@@ -70,7 +70,9 @@
 #define DLN2_SPI_GET_MIN_DELAY_BETWEEN_FRAMES	DLN2_SPI_CMD(0x4B)
 #define DLN2_SPI_GET_MAX_DELAY_BETWEEN_FRAMES	DLN2_SPI_CMD(0x4C)

 #define DLN2_SPI_MAX_XFER_SIZE			256
+/* The CS bitmask is carried in a u8 on the wire, so 8 lines is the ceiling. */
+#define DLN2_SPI_MAX_CS				8
 #define DLN2_SPI_BUF_SIZE			(DLN2_SPI_MAX_XFER_SIZE + 16)
 #define DLN2_SPI_ATTR_LEAVE_SS_LOW		BIT(0)
 #define DLN2_TRANSFERS_WAIT_COMPLETE		1
@@ -200,7 +202,9 @@ static int dln2_spi_get_cs_num(struct dln2_spi *dln2, u16 *cs_num)
 	if (rx_len < sizeof(rx))
 		return -EPROTO;

 	*cs_num = le16_to_cpu(rx.cs_count);
+	if (*cs_num == 0 || *cs_num > DLN2_SPI_MAX_CS)
+		return -EPROTO;

 	dev_dbg(&dln2->pdev->dev, "cs_num = %d\n", *cs_num);

/*
 * Reproducer for 213-ubsan-shift-out-of-bounds-in-dln2-spi-probe
 */
#define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/usb/ch9.h>
#include <linux/usb/raw_gadget.h>

#define EP0_MAX_TRANSFER  4096
#define BULK_MAX_TRANSFER 512

/* DLN2 protocol, drivers/mfd/dln2.c */
#define DLN2_HANDLE_CTRL	1
#define DLN2_HANDLE_SPI		4
#define DLN2_RESPONSE_HEADER_LEN 10	/* dln2_header{size,id,echo,handle} + __le16 result */
#define CMD_GET_DEVICE_VER	0x0030
#define DLN2_HW_ID		0x200	/* dln2_check_hw() rejects any other id */
#define SPI_GET_SS_COUNT	0x0244

/*
 * The chip-select count we report as the device. dln2_spi_cs_enable_all()
 * (drivers/spi/spi-dln2.c:179) does GENMASK(num_chipselect - 1, 0), which
 * expands to ~0UL >> (BITS_PER_LONG - 1 - 8192). That shift count is
 * 64 - 1 - 8192 = -8129, i.e. 18446744073709543487 as an unsigned long,
 * which is the exponent UBSAN reports. Nothing in the driver bounds this
 * against BITS_PER_LONG.
 */
#define BAD_CHIPSELECT_COUNT 8193

static int raw_gadget_fd = -1;
static int bulk_in_ep_handle = -1, bulk_out_ep_handle = -1;
static volatile int response_thread_started;

static void put_le16(__u8 *p, unsigned v) { p[0] = v; p[1] = v >> 8; }
static void put_le32(__u8 *p, unsigned v)
{
	p[0] = v; p[1] = v >> 8; p[2] = v >> 16; p[3] = v >> 24;
}

/* descriptors of the emulated Diolan DLN-2 */
static struct usb_device_descriptor device_descriptor = {
	.bLength = sizeof(struct usb_device_descriptor),
	.bDescriptorType = USB_DT_DEVICE,
	.bcdUSB = 0x0200,
	.bMaxPacketSize0 = 64,
	.idVendor = 0xa257,	/* the dln2 usb_device_id: Diolan */
	.idProduct = 0x2013,	/* DLN-2 */
	.bcdDevice = 0x0100,
	.bNumConfigurations = 1,
};

#define LE16(v) ((v) & 0xff), (((v) >> 8) & 0xff)
static const __u8 configuration_descriptor[] = {
	9, USB_DT_CONFIG, LE16(9 + 9 + 7 + 7), 1, 1, 0, 0x80, 0x32,
	/* interface 0, vendor specific, 2 endpoints */
	9, USB_DT_INTERFACE, 0, 0, 2, 0xff, 0x00, 0x00, 0,
	/* EP 0x81 bulk IN  */
	7, USB_DT_ENDPOINT, 0x81, USB_ENDPOINT_XFER_BULK, LE16(BULK_MAX_TRANSFER), 0,
	/* EP 0x02 bulk OUT */
	7, USB_DT_ENDPOINT, 0x02, USB_ENDPOINT_XFER_BULK, LE16(BULK_MAX_TRANSFER), 0,
};

static struct usb_endpoint_descriptor bulk_in_ep_descriptor = {
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = 0x81,
	.bmAttributes = USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize = BULK_MAX_TRANSFER,
};
static struct usb_endpoint_descriptor bulk_out_ep_descriptor = {
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = 0x02,
	.bmAttributes = USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize = BULK_MAX_TRANSFER,
};

/*
 * Answer the two dln2 requests that gate probe: the hardware id, and the
 * chip-select count that carries the poisoned value. Everything else gets a
 * zero payload, which every remaining dln2 cell treats as a benign answer.
 */
static void *dln2_response_thread(void *unused)
{
	__u8 request_buffer[sizeof(struct usb_raw_ep_io) + BULK_MAX_TRANSFER];
	__u8 response_buffer[sizeof(struct usb_raw_ep_io) + BULK_MAX_TRANSFER];
	struct usb_raw_ep_io *request = (void *)request_buffer;
	struct usb_raw_ep_io *response = (void *)response_buffer;
	const unsigned payload_len = 64;

	for (;;) {
		unsigned id, echo, handle;
		__u8 *payload;
		int n;

		request->ep = bulk_out_ep_handle;
		request->flags = 0;
		request->length = BULK_MAX_TRANSFER;
		n = ioctl(raw_gadget_fd, USB_RAW_IOCTL_EP_READ, request);
		if (n < 0) {
			if (errno == EINTR || errno == EBUSY)
				continue;
			return unused;
		}
		if (n < 8)
			continue;
		id     = request->data[2] | (request->data[3] << 8);
		echo   = request->data[4] | (request->data[5] << 8);
		handle = request->data[6] | (request->data[7] << 8);

		memset(response_buffer, 0, sizeof(response_buffer));
		payload = response->data + DLN2_RESPONSE_HEADER_LEN;
		if (handle == DLN2_HANDLE_CTRL && id == CMD_GET_DEVICE_VER)
			put_le32(payload, DLN2_HW_ID);
		else if (handle == DLN2_HANDLE_SPI && id == SPI_GET_SS_COUNT)
			put_le16(payload, BAD_CHIPSELECT_COUNT);

		/* dln2_rx() drops the response unless hdr.size == actual_length. */
		put_le16(response->data + 0, DLN2_RESPONSE_HEADER_LEN + payload_len);
		put_le16(response->data + 2, id);
		put_le16(response->data + 4, echo);
		put_le16(response->data + 6, handle);
		/* response->data + 8 is __le16 result, already 0 == success */

		response->ep = bulk_in_ep_handle;
		response->flags = 0;
		response->length = DLN2_RESPONSE_HEADER_LEN + payload_len;
		ioctl(raw_gadget_fd, USB_RAW_IOCTL_EP_WRITE, response);
	}
	return unused;
}

static void handle_ep0_control(const struct usb_ctrlrequest *ctrl)
{
	__u8 buffer[sizeof(struct usb_raw_ep_io) + EP0_MAX_TRANSFER];
	struct usb_raw_ep_io *io = (void *)buffer;
	int len = -1;

	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
		switch (ctrl->bRequest) {
		case USB_REQ_GET_DESCRIPTOR:
			if ((ctrl->wValue >> 8) == USB_DT_DEVICE) {
				len = sizeof(device_descriptor);
				memcpy(io->data, &device_descriptor, len);
			} else if ((ctrl->wValue >> 8) == USB_DT_CONFIG) {
				len = sizeof(configuration_descriptor);
				memcpy(io->data, configuration_descriptor, len);
			}
			break;
		case USB_REQ_SET_CONFIGURATION: {
			pthread_t response_thread;

			ioctl(raw_gadget_fd, USB_RAW_IOCTL_CONFIGURE, 0);
			bulk_in_ep_handle = ioctl(raw_gadget_fd,
						  USB_RAW_IOCTL_EP_ENABLE,
						  &bulk_in_ep_descriptor);
			bulk_out_ep_handle = ioctl(raw_gadget_fd,
						   USB_RAW_IOCTL_EP_ENABLE,
						   &bulk_out_ep_descriptor);
			if (bulk_in_ep_handle >= 0 && bulk_out_ep_handle >= 0 &&
			    !response_thread_started) {
				response_thread_started = 1;
				pthread_create(&response_thread, NULL,
					       dln2_response_thread, NULL);
			}
			len = 0;
			break;
		}
		}
	}

	if (len < 0) {
		ioctl(raw_gadget_fd, USB_RAW_IOCTL_EP0_STALL, 0);
		return;
	}
	io->ep = 0;
	io->flags = 0;
	io->length = len < ctrl->wLength ? len : ctrl->wLength;

	/* raw-gadget latches the direction per setup packet: a zero-length
	 * request is an OUT no matter what bRequestType says, and the opposite
	 * ioctl returns EBUSY. */
	if (ctrl->bRequestType & USB_DIR_IN && ctrl->wLength)
		ioctl(raw_gadget_fd, USB_RAW_IOCTL_EP0_WRITE, io);
	else
		ioctl(raw_gadget_fd, USB_RAW_IOCTL_EP0_READ, io);
}

int main(void)
{
	__u8 event_buffer[sizeof(struct usb_raw_event) + EP0_MAX_TRANSFER];
	struct usb_raw_event *event = (void *)event_buffer;
	struct usb_raw_init init;
	struct dirent *udc_entry;
	DIR *udc_dir;
	char *udc_index;

	/* dummy_hcd numbers its UDCs, so the name must be discovered:
	 * device "dummy_udc.2" belongs to driver "dummy_udc". */
	udc_dir = opendir("/sys/class/udc");
	while (udc_dir && (udc_entry = readdir(udc_dir)) && udc_entry->d_name[0] == '.')
		;
	if (!udc_dir || !udc_entry) {
		fprintf(stderr, "no UDC in /sys/class/udc\n");
		return 1;
	}
	memset(&init, 0, sizeof(init));
	snprintf((char *)init.device_name, UDC_NAME_LENGTH_MAX, "%s", udc_entry->d_name);
	snprintf((char *)init.driver_name, UDC_NAME_LENGTH_MAX, "%s", udc_entry->d_name);
	closedir(udc_dir);
	udc_index = strrchr((char *)init.driver_name, '.');
	if (udc_index)
		*udc_index = 0;
	init.speed = USB_SPEED_HIGH;

	raw_gadget_fd = open("/dev/raw-gadget", O_RDWR);
	if (raw_gadget_fd < 0) {
		perror("/dev/raw-gadget");
		return 1;
	}
	if (ioctl(raw_gadget_fd, USB_RAW_IOCTL_INIT, &init) < 0) {
		perror("USB_RAW_IOCTL_INIT");
		return 1;
	}
	/* RUN must precede EVENT_FETCH: the device has to be in
	 * STATE_DEV_RUNNING or the fetch returns -EINVAL. */
	if (ioctl(raw_gadget_fd, USB_RAW_IOCTL_RUN, 0) < 0) {
		perror("USB_RAW_IOCTL_RUN");
		return 1;
	}

	for (;;) {
		event->type = 0;
		event->length = EP0_MAX_TRANSFER;
		if (ioctl(raw_gadget_fd, USB_RAW_IOCTL_EVENT_FETCH, event) < 0) {
			if (errno == EINTR)
				continue;
			perror("USB_RAW_IOCTL_EVENT_FETCH");
			return 1;
		}
		if (event->type == USB_RAW_EVENT_CONTROL)
			handle_ep0_control((struct usb_ctrlrequest *)event->data);
	}
	return 0;
}
Re: [BUG] UBSAN: shift-out-of-bounds in dln2_spi_probe
Posted by Andy Shevchenko 2 weeks, 1 day ago
On Wed, Sep 09, 2026 at 05:33:20PM -0700, Farhad Alemi wrote:

> We are reporting the following crash (reproducer and a proposed patch attached):

Thank you.

Please, submit the patch as a formal fix. You can find the necessary steps in
the Submitting Patches documentation.

> Linux version 7.3.0-rc2 50d05c7c76c96b90462f24debacca971d2e86713
> Build Config: UpstreamAppArmorKASAN (KASAN + UBSAN, panic_on_warn=1)

-- 
With Best Regards,
Andy Shevchenko