Simics: Забиваем гвозди сваебоем

Любите ли вы отзывчивые программы так, как люблю их я? Любовь эта привела меня к Колибри ОС - невероятно шустрой операционной системе, которая запускает программу до того, как вы осознаете, что кликнули по ней. И недавно у неё нашли уязвимость: ping of death.Так получилось, что моя первая работа была связана с симуляцией компьютерных систем – от серверов до мобильных устройств. И там мы использовали симулятор Simics. Этой системой пользуются крупные производители железа для опережающей разработки драйверов.

Если бы только можно было использовать Simics для отладки любительской ОС...

Simics: Забиваем гвозди сваебоем

Часть 0: Проблема

Баг описал пользователь turbocat на форуме: достаточно большой ICMP echo-запрос может убить один из процессов операционной системы или полностью вывести её из строя. Для воспроизведения проблемы нужно пингануть машину с запущенной системой Колибри. А для этого потребуется два компьютера, подключенных к одной локальной сети.

Если бы только Simics мог симулировать компьютерные сети…

Часть 1: Воспроизведение

Симуляция в Simics настраивается с использованием simics-скриптов, которые создают и настраивают устройства. Для воспроизведения можно создать свой скрипт: «targets/linux-kolibri.simics».

Для начала нужна машина с Linux на борту. Это самая простая часть, такая машина включена в базовый дистрибутив Simics, и её можно создать, используя скрипт из пакета «simics-qsp-x86». Он даже сеть автоматически может настроить! Но здесь это не нужно:

Simics: Забиваем гвозди сваебоем

Этот скрипт уже можно запустить командой simics linux-kolibri.simics и введя continue в командной строке Simics (изначально симуляция находится в состоянии паузы). После этого видим графику и консоль Linux:

Simics: Забиваем гвозди сваебоем

Ещё нужен ПК с установленной Колибри. Его можно создать тем же скриптом, нужно только указать свой образ жесткого диска. А для упрощения работы можно настроить виртуальный USB-планшет - он позволит с комфортом использовать мышь внутри Колибри:

Simics: Забиваем гвозди сваебоем

Теперь скрипт запускает две системы:

Simics: Забиваем гвозди сваебоем

Но у Колибри проблема: она не распознаёт сетевую карточку. Не беда! В стандартную поставку Simics входит вагон устройств на любой вкус: от USB флешек до видеокарт. Есть и сетевые карточки. Нужно найти ту, которая поддерживается в Колибри ОС:

Simics: Забиваем гвозди сваебоем

Среди устройств Simics есть i82543 – должен заработать. Нужно только загрузить одноименный модуль, создать устройство и подключить его к свободному PCIe слоту на северном мосту машины. Главное – не забыть инициализировать новые устройства командой «instantiate-components»:

Simics: Забиваем гвозди сваебоем

Вуаля: новая сетевая карта определилась как «I8254x»:

Simics: Забиваем гвозди сваебоем

Теперь нужно как-то соединить эти две машины в одну сеть. Для этого достаточно создать коммутатор и подключить к нему сетевые карты машин:

Simics: Забиваем гвозди сваебоем

Чтобы не настраивать IP руками, можно поставить локальный DHCP сервер, который присвоит IP адреса автоматически. Для этого существует объект «Service node». Service node - это вообще универсальная штука: и DNS он тебе предоставит, и DHCP, и даже доступ к интернету. Нужно только его настроить: создать на нём шлюз по умолчанию, подключить его к коммутатору и указать пул выдаваемых адресов:

Simics: Забиваем гвозди сваебоем

Ну и, конечно же, снова инициализировать все компоненты:

Simics: Забиваем гвозди сваебоем

Если симулируемая машина ничем не нагружена, симуляция может идти очень быстро, что может приводить к таймаутам и разрывам соединений. Поэтому для ручного тестирования лучше включить режим реального времени:

Simics: Забиваем гвозди сваебоем

Можно запустить этот скрипт и посмотреть IP адреса в Колибри:

Simics: Забиваем гвозди сваебоем

И в Linux:

Simics: Забиваем гвозди сваебоем

И пингануть Колибри из Линукса:

Simics: Забиваем гвозди сваебоем

Система настроена! Теперь можно воспроизводить проблему. Но для начала лучше сохранить текущее состояние симуляции, чтобы не загружать обе системы заново, если что-то пойдет не так: остановить симуляцию командой stop, и выполнить команду:

Simics: Забиваем гвозди сваебоем

Текущая конфигурация сохранится в папку «after_boot.ckpt». Её можно будет запустить просто передав Simics название этой папки вместо скрипта: simics after_boot.ckpt.

У нашего Ping of Death два симптома: либо он убивает процесс сетевого драйвера, либо заставляет виснуть всю систему целиком. Возможно, если разобраться с первым случаем, будет исправлен и второй.

В Колибри есть доска отладки, в которую выводятся логи приложений и ядра. Если открыть её в режиме ядра, можно запечатлеть падение процесса драйвера. Но чтобы понять, на каком размере эхо-запроса начинаются проблемы, придется несколько раз запускать ping из консоли Линукса, и если зависание таки произойдёт - перезапускать симуляцию и пинговать заново.

Если бы только Simics позволял автоматизировать работу с ОС…

Часть 2: Автоматизация

Simics скрипты позволяют полностью автоматизировать взаимодействие с симулируемыми системами. Можно написать скрипт «ping-of-death.simics», который самостоятельно загрузит чекпойнт и пинганёт Колибри из Линукса. Скрипт с одним параметром – размер отправляемого echo-запроса. Это надуманный повод познакомить читателя с очередным понятием Это поможет быстрее найти значение, которое роняет драйвер.

Для загрузки чекпойнта в Simics скриптах есть отдельная команда:

Simics: Забиваем гвозди сваебоем

А дальше нужно как-то ввести в консоль линукса команду «ping», причём сделать это нужно при запущенной симуляции. Но если написать в скрипте «continue», то следующие команды скрипта не выполнятся, пока пользователь сам не остановит симуляцию, как же быть?

На помощь приходят Script Branches. Они позволяют выполнять команды параллельно с запущенной симуляцией. Можно создать такой, который введёт команду в консоль линукса:

Simics: Забиваем гвозди сваебоем

Осталось сделать так, чтобы можно было настроить размер пинга. У каждого скрипта могут быть собственные параметры, которые задаются при запуске. Обычно такие создаются в начале скрипта:

Simics: Забиваем гвозди сваебоем

Их можно использовать как обычные переменные:

Simics: Забиваем гвозди сваебоем

А чтобы не приходилось вводить «continue» в консоль Simics можно ввести его уже в скрипте, в самом его конце:

Simics: Забиваем гвозди сваебоем

С таким скриптом можно спокойно экспериментировать с размерами пингов:

Simics: Забиваем гвозди сваебоем

Экспериментальным путём было выявлено, что размер пинга, при котором он перестаёт возвращаться: 1469, но драйвер падает на другом значении: 1473. И, судя по доске отладки, падение вызвано исключением Page Fault:

Simics: Забиваем гвозди сваебоем

Если бы только Simics мог показать, где возникло это исключение…

Часть 3: Взятие с поличным

В Simics предусмотрена тонна инструментов для отладки оборудования и драйверов. Например, точки останова: ставим такую на выполнение какого-то действия – и программа останавливается при её достижении. Можно поставить точку останова на вызов нужного исключения процессора:

Simics: Забиваем гвозди сваебоем

Тогда симуляция остановится на первом же возникшем Page Fault. Если сделать это в скрипте, то в консоли Simics это не будет никак отражено, он просто поставит симуляцию на паузу и разрешит ввод (simics>). Чтобы посмотреть, на какой инструкции возникло исключение, можно воспользоваться вторым инструментом: отладкой процессора. Начать отладку можно так:

Simics: Забиваем гвозди сваебоем

В simics можно загрузить информацию о символах ОС, но, к сожалению, ассемблер, на котором написана Колибри, не умеет генерировать информацию нужного формата. Но к счастью – это ассемблер! А значит, можно выявить строку кода с ошибкой по инструкциям, окружающим этот код.

Simics: Забиваем гвозди сваебоем

Внезапно, ECHO-запрос вызывает блок кода «dump» (очевидно, имелось в виду «drop» – отбросить пакет). И если вывести содержимое регистра edi, становится понятно, что оно совсем не такое, какое ожидается:

Simics: Забиваем гвозди сваебоем

В edi должен был бы быть индекс сетевого девайса, а лежит какой-то адрес. Но почему? И что привело систему в этот блок?

Если бы только Simics умел выполнять код в обратном направлении…

Вывод

Simics имеет достаточное количество инструментов для разработки и отладки операционных систем, а система скриптов позволяет автоматизировать тестирование и воспроизведение проблем.

Сильные стороны системы:

  • Возможность обратного выполнения кода.
  • Обширный инструментарий для отладки.
  • Богатый скриптовый язык с интегрированным Python.
  • Большое количество эмулируемых девайсов и возможность написания своих.

Слабые стороны:

  • Медлительность ввиду избыточного функционала.
  • Проприетарность.

Лично я предпочитаю использовать её при отладке багов в ядре и для тестирования драйверов. В прочих тестах использую Qemu.

Полный оригинал статьи можно прочитать здесь:

11
Начать дискуссию