
Эмуляционный фаззер для прошивок MSP430, который находит и анализирует ошибки повреждения памяти с подробными отчётами о сбоях и воспроизводимыми входными данными.
Сбои, обнаруженные в бинарных файлах MSP430 с помощью Icicle.
| # | Цель | Описание |
|---|---|---|
| I03 | Goodwatch | Некорректное сравнение при записи в dmesg_buffer |
| I04 | Goodwatch | Нулевая длина сообщения |
| I05 | Goodwatch | Переполнение RNG |
| I06 | Goodwatch | Выход за границы при нажатии клавиш в OOK |
| I07 | Goodwatch | Выход за границы в секундомере после 60 часов |
| I08 | Goodwatch | Выход за границы при отображении дня недели |
| I09 | Goodwatch | Приложение Hex Viewer |
| I10 | Goodwatch | Команды монитора PEEK/POKE |
| I11 | H4_PacketProtocol | Непроверяемый индекс интерфейса в Get Descriptor |
| I12 | H4_PacketProtocol | Переполнение буфера в Set Report |
dmesg bufferputchar вызывает запись одного байта за границы (OOB write) по адресу 0x2c00.Пример стека вызовов при сбое:
UnhandledException(code=WriteUnmapped, value=0x2c00):
0x000000c81a: putchar+0x1c
0x000000c832: dmesg_putc+0x6
0x0000009706: tfp_format+0x22
0x000000ad9c: tfp_printf+0x12
0x000000b924: key_scan.part.0+0x68
0x000000cd3c: PORT2_ISR+0x1a
0x0000008e04: <unknown>
Сообщения, отправляемые в UART-интерфейс с нулевым полем длины, обрабатываются некорректно. В состоянии MSG после сохранения только что принятого байта по текущему индексу index в uart_buffer код увеличивает index и проверяет, равен ли он теперь length. Однако, поскольку сохранение и инкремент index выполняются до сравнения с length, для сообщений с нулевой длиной условие index == length никогда не будет истинным.
В конечном итоге index выйдет за пределы массива uart_buffer, что позволит изменять глобальные переменные, расположенные после uart_buffer. Обычно это приводит к сбою программы, когда перезаписываются указатели на функции stdout_putf или stdout_putp и выводится отладочное сообщение.
Входные данные, вызывающие сбой:
./run_goodwatch.sh I04a
Стек вызовов при сбое:
UnhandledException(code=InvalidInstruction)
0x000000d3d3: <unknown>
0x0000009648: putchw+0x40
0x0000009826: tfp_format+0x142
0x000000ad9c: tfp_printf+0x12
0x000000b924: key_scan.part.0+0x68
0x000000cd3c: PORT2_ISR+0x1a
0x0000008e04: <unknown>
appindex (или subindex), что приводит к сбою при следующей загрузке applet с использованием appindex (например, в app_draw).Стек вызовов при сбое:
UnhandledException(code=InvalidInstruction)
0x000000531c: <unknown>
0x0000009a96: app_init+0xe
0x000000c0a8: settime_draw+0x28
0x0000009b0a: app_draw+0x3e
0x000000ce78: watchdog_timer+0x6a
0x0000008e04: <unknown>
RANDINT заставляет прошивку генерировать и отправлять список случайных чисел. Однако количество генерируемых случайных значений управляется принятой командой. Если количество значений слишком велико, rints выйдет за пределы области, зарезервированной под стек, что приведёт к записи за границы (OOB write).Входные данные, вызывающие сбой:
./run_goodwatch.sh I05
Стек вызовов при сбое:
UnhandledException(code=WriteUnmapped, value=0x6e6a)
0x000000cbd4: USCI_A0_ISR+0x24c (inlined `send_randint`)
0x0000008e04: <unknown>
ook keypress:Приложение OOK отправляет предварительно настроенный OOK-пакет из button_array при нажатии одной из цифровых клавиш (0-9). Однако ook_keypress не проверяет, что нажатая клавиша находится в границах массива button_array. Цифровых кнопок десять (0-9), а button_array содержит только 9 записей.
button_array содержит массив указателей; когда в приложении OOK нажата кнопка 9, функция setrate читает указатель за пределами button_array и разыменовывает его, что приводит к сбою программы.
Указатель, который она читает, — это первое значение из ook_settings (0x3012), отсутствующее в памяти.
Входные данные, вызывающие сбой:
./run_goodwatch.sh I06
Стек вызовов при сбое:
UnhandledException(code=ReadUnmapped, value=0x3012)
0x000000b87a: ook_keypress+0x2c
0x000000cd7e: PORT2_ISR+0x5c
0x0000008e04: <unknown>
Прошивка реализует преобразование целого числа в двоично-десятичный код (BCD) с помощью таблицы поиска, однако таблица (bcdtable) содержит только значения для преобразования целых чисел от 0 до 59. Для секунд и минут это нормально, поскольку при счёте эти значения сбрасываются после достижения 60, однако переменная hour может превысить 60, что вызовет выход индекса за границы.
Этот баг был обнаружен фаззером только как побочный эффект повреждений, вызванных багом нулевой длины сообщения. В найденном фаззером сбое обе глобальные переменные min и hour повреждены из-за переполнения uart_buffer.
Входные данные, вызывающие сбой:
./run_goodwatch.sh I07
Стек вызовов при сбое:
UnhandledException(code=ReadUnmapped, value=0x4b57)
0x000000a3e4: stopwatch_draw+0x9e
0x0000009b0a: app_draw+0x3e
0x000000ce78: watchdog_timer+0x6a
0x0000008e04: <unknown>
Ложноположительный баг, вызванный тем, что фаззер генерирует большое значение для периферийного регистра RTCDOW.
При нажатии кнопки 9 в приложении-часах предпринимается попытка вывести текущий день недели на LCD. День недели читается напрямую из регистра RTCDOW периферии RTC; в техническом описании этого регистра указано, что возвращаемое значение находится в диапазоне от 0 до 6, однако во время фаззинга значение этого регистра ничем не ограничено. Поскольку это значение используется как индекс в массиве dayofweek, если фаззер сгенерирует для регистра RTCDOW значение больше 6, программа прочитает указатель за пределами массива daysofweek, что приведёт к (ложноположительному) сбою.
Этот сбой может возникать как в clock_keypress, так и в hebrew_keypress.
Входные данные, вызывающие сбой:
./run_goodwatch.sh I08a
Стек вызовов при сбое:
UnhandledException(code=ReadUnmapped, value=0x5150)
0x0000009e6e: lcd_string+0x8
0x000000c1dc: clock_keypress+0x6c
0x000000cd7e: PORT2_ISR+0x5c
0x0000008e04: <unknown>
Входные данные, вызывающие сбой:
./run_goodwatch.sh I08b
Стек вызовов при сбое:
UnhandledException(code=ReadPerm, value=0x1006)
0x0000009e6e: lcd_string+0x8
0x000000b2aa: hebrew_keypress+0x116
0x000000c48c: clock_keypress+0x31c
0x000000cd7e: PORT2_ISR+0x5c
0x0000008e04: <unknown>
Входные данные, вызывающие сбой:
./run_goodwatch.sh I09
Стек вызовов при сбое:
UnhandledException(code=ReadUnmapped, value=0x7000)
0x0000009c74: hex_draw.part.0+0x3c
0x0000009c88: hex_draw+0x8
0x0000009b0a: app_draw+0x3e
0x000000cd8c: PORT2_ISR+0x6a
0x0000008e04: <unknown>
Входные данные, вызывающие сбой, для PEEK:
./run_goodwatch.sh I10a
Стек вызовов при сбое:
UnhandledException(code=ReadUnmapped, value=0x5100)
0x000000cb00: USCI_A0_ISR+0x178
0x0000008e04: <unknown>
Входные данные, вызывающие сбой, для POKE:
./run_goodwatch.sh I10b
Стек вызовов при сбое:
UnhandledException(code=WritePerm, value=0x4b00)
0x000000cb32: USCI_A0_ISR+0x1aa
0x0000008e04: <unknown>
Get_Descriptor. Это может привести к выходу за границы. Например:GetHidDescriptor:
./run_H4_PacketProtocol.sh I11a
Стек вызовов при сбое:
UnhandledException(code=ReadUninitialized, value=0xe90e)
0x0000011176: usbSendNextPacketOnIEP0 at ./USB_API/USB_Common/usb.c:1082.13
0x0000011c12: usbGetHidDescriptor at ./USB_API/USB_HID_API/UsbHidReq.c:78.5
0x0000010fb0: usbDecodeAndProcessUsbRequest at ./USB_API/USB_Common/usb.c:1655.5
0x0000011724: SetupPacketInterruptHandler at ./USB_config/UsbIsr.c:250.5
0x0000004410: iUsbInterruptHandler at ./USB_config/UsbIsr.c:88.9
0x0000004572: _c_int00_noargs
GetReportDescriptor:
./run_H4_PacketProtocol.sh I11b
Стек вызовов при сбое:
UnhandledException(code=ReadUninitialized, value=0xa44c)
0x0000011c28: usbGetReportDescriptor at ./USB_API/USB_HID_API/UsbHidReq.c:85.5
0x0000010fb0: usbDecodeAndProcessUsbRequest at ./USB_API/USB_Common/usb.c:1655.5
0x0000011724: SetupPacketInterruptHandler at ./USB_config/UsbIsr.c:250.5
0x00000044fe: iUsbInterruptHandler at ./USB_config/UsbIsr.c:166.9
0x0000004572: _c_int00_noargs
После получения пакета Set_Report прошивка назначает буфер для хранения отчёта переменной pbOEP0Buffer, а длину отчёта — переменной wBytesRemainingOnOEP0. Однако прошивка не проверяет, помещается ли отчёт в назначенный буфер, что вызывает переполнение буфера при последующей записи в него в usbReceiveNextPacketOnOEP0.
В приведённых ниже входных данных, вызывающих сбой, адрес 8-байтового массива (0x24a4: abUsbRequestIncomingData) присваивается переменной pbOEP0Buffer; после переполнения массива повреждается указатель на функцию (0x24b4: USB_RX_memcpy), что приводит к сбою при последующем вызове этого указателя на функцию.
./run_H4_PacketProtocol.sh I12
Стек вызовов при сбое:
UnhandledException(code=InvalidInstruction, value=0xf95b7)
0x00000f95b7: <unknown>
0x0000011066: HidCopyUsbToBuff at ./USB_API/USB_HID_API/UsbHid.c:417.5
0x0000010172: USBHID_receiveData at ./USB_API/USB_HID_API/UsbHid.c:577.13
0x000001026a: main at ./main.c:115.25
0x0000004572: _c_int00_noargs