4.19.8以前,在Linux內核中,hso_probe()函數中發現了一個缺陷,該函數從USB設備(做爲u8)讀取if_num值,而且不須要對數組進行長度檢查就使用它來索引數組,從而致使在hso_probe()或hso_get_config_data()中讀取OOB內存。攻擊者使用僞造的USB設備和對系統的物理訪問(須要鏈接這樣的設備)能夠致使系統崩潰和拒絕服務。linux
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 184c24b..d6916f7 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2807,6 +2807,12 @@ static int hso_get_config_data(struct usb_interface *interface) return -EIO; } + /* check if we have a valid interface */ + if (if_num > 16) { + kfree(config_data); + return -EINVAL; + } + switch (config_data[if_num]) { case 0x0: result = 0; @@ -2877,10 +2883,18 @@ static int hso_probe(struct usb_interface *interface, /* Get the interface/port specification from either driver_info or from * the device itself */ - if (id->driver_info) + if (id->driver_info) { + /* if_num is controlled by the device, driver_info is a 0 terminated + * array. Make sure, the access is in bounds! */ + for (i = 0; i <= if_num; ++i) + if (((u32 *)(id->driver_info))[i] == 0) + goto exit; port_spec = ((u32 *)(id->driver_info))[if_num]; - else + } else { port_spec = hso_get_config_data(interface); + if (port_spec < 0) + goto exit; + } /* Check if we need to switch to alt interfaces prior to port * configuration */
肯定問題出在drivers/net/usb/hso.c,分析一下這個文件git
文件最後,有模塊的一些操做,文件開頭有一些註釋,得知這是文件是一個驅動的實現,Option High Speed Mobile Devices,還有一個專門的編譯選項CONFIG_USB_HSO數組
module_init(hso_init);
module_exit(hso_exit);
MODULE_AUTHOR(MOD_AUTHOR);
MODULE_DESCRIPTION(MOD_DESCRIPTION);
MODULE_LICENSE(MOD_LICENSE);
初始化函數是hso_init,卸載函數是hso_exit,卸載函數比較簡單,只是註銷了tty和usb的驅動,一樣在hso_init函數中,也註冊了這兩個驅動。函數
static void __exit hso_exit(void) { printk(KERN_INFO "hso: unloaded\n"); tty_unregister_driver(tty_drv); /* deregister the usb driver */ usb_deregister(&hso_driver); }
這應該是一個連在USB總線上的串口,下層使用USB來和硬件交流,而後將USB得到的信息傳遞給tty層。從文件中定義的tty_operations這些個函數看下去,最終實現都是USB這邊的實現,usb_submit_urb、usb_control_msg函數源碼分析
static const struct tty_operations hso_serial_ops = { .open = hso_serial_open, .close = hso_serial_close, .write = hso_serial_write, .write_room = hso_serial_write_room, .ioctl = hso_serial_ioctl, .set_termios = hso_serial_set_termios, .chars_in_buffer = hso_serial_chars_in_buffer, .tiocmget = hso_serial_tiocmget, .tiocmset = hso_serial_tiocmset, .get_icount = hso_get_count, .unthrottle = hso_unthrottle };
hso_probe函數是定義的usb驅動的probe函數,就是USB設備插上以後,USB驅動第一個被調用的函數,在這個函數中應該要作一些設備的初始化功能spa
static struct usb_driver hso_driver = { .name = driver_name, .probe = hso_probe, .disconnect = hso_disconnect, .id_table = hso_ids, .suspend = hso_suspend, .resume = hso_resume, .reset_resume = hso_resume, .supports_autosuspend = 1, };
補丁的修改位置就在hso_probe的開頭,問題出在if_num處code
if_num = interface->altsetting->desc.bInterfaceNumber; /* Get the interface/port specification from either driver_info or from * the device itself */ if (id->driver_info) port_spec = ((u32 *)(id->driver_info))[if_num]; else port_spec = hso_get_config_data(interface); if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { dev_err(&interface->dev, "Not our interface\n"); return -ENODEV; }
altsetting,這個值表示可選的設置,bInterfaceNumber表示該配置的接口號。這些值都是USB枚舉階段從外部設備讀入的值,也就是if_num這個值是能夠被惡意的外部設備所控制blog
在port_spec = ((u32 *)(id->driver_info))[if_num];這句話,當id->driver_info==0的時候if_num的超過會形成越界訪問,不過好像看了下hso_ids中的值,會有等於0的狀況嗎?索引
而後是hso_get_config_data函數,開頭一樣有這樣的一段和上述代碼相似
struct usb_device *usbdev = interface_to_usbdev(interface); u8 config_data[17]; u32 if_num = interface->altsetting->desc.bInterfaceNumber; s32 result; if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x86, 0xC0, 0, 0, config_data, 17, USB_CTRL_SET_TIMEOUT) != 0x11) { return -EIO; } switch (config_data[if_num]) {
一樣是對於interface->altsetting->desc.bInterfaceNumber這個值的數據訪問。因此加上一個越界判斷