Объявлен победитель конкурса Underhanded C Contest 2015, который проходил вместе с Nuclear Threat Initiative. Задача — написать на C код для сравнения спектров ядерных материалов. Код должен выглядеть честно, но содержать скрытый баг, позволяющий подделать результат. Цель — показать, насколько опасны неочевидные ошибки в критическом софте.
Около трети участников использовали трюк под названием NaN poisoning. Суть: если в вычислениях появляется NaN («не число»), любое сравнение с ним возвращает false. Если итоговое условие написано как score < threshold, то NaN даст ложную тревогу. Проблема в том, что такие атаки обычно слишком заметны или требуют от проверяемой страны вмешательства в данные — это сочли нереалистичным.
Среди финалистов отметили несколько интересных работ. Philipp Klenze заставил переполниться расчёт вероятности Пуассона при значениях около 1686 — это достигается вполне реальным спектром. Ghislain Lemaur спрятал баг в логе: строка определённой длины перезаписывает два байта в переменной-экспоненте, и при отрицательном аргументе pow возвращает NaN — триггером служит изменение uid файла лога. Josh Lospinoso пропустил букву в isinf(), так что вместо проверки на бесконечность вызывался sinf() — нужна экстремально большая входная величина. Stephen Dolan нашёл атаку через точность double: если первое значение спектра на много порядков больше остальных, сложение квадратов превращается в «первое + 0», а потом вычитание этого же «выброса» даёт ноль. Matt Bierner использовал строку с китайскими иероглифами, которая при вызове как функция выполняет машинный код — но для запуска нужно сначала получить исключение с плавающей точкой. Gregory Stewart сотворил утечку памяти в пуле объектов: после нескольких вызовов новые узлы перестают инициализироваться и всегда возвращают совпадение. Stephen Oman перепутал время начала и конца сканирования: если подкрутить системные часы так, чтобы разница стала отрицательной, спектр делится на отрицательное число и все значения становятся отрицательными — тогда проверки площадей пиков всегда проходят.
Победителем стал Linus Åkesson. В его коде определён тип float_t как double в одном файле, но в другом используется стандартный float из math.h. Когда match() передаёт массив double в функцию spectral_contrast(), та видит каждый double как два float. Поскольку в спектрах хранятся целые числа до миллиона, старшая часть double содержит все значащие биты, а младшая — нули. В результате вторая половина массива заполняется нулями, а первая — сильно сжатыми значениями (SQUASH-функция). Между числами 10 и 1000 разница становится крошечной. Проверяющая страна может оставить чуть-чуть делящегося материала и добавить крупный сигнал во второй половине спектра — он пройдёт проверку на полную энергию, а потом будет проигнорирован из-за нулей. Сжатие делает финальный спектр почти неотличимым от эталона. Код абсолютно невинный, всего 60 строк, и атака не требует вмешательства в компьютер — только физически подобрать спектр. Поздравляем Linus Åkesson с титулом самого коварного программиста на C 2015 года.