На днях компания Google сделала важный шаг, объявив о включении языка программирования Rust в число языков, которые допускаются для разработки платформы Android. Да, еще в 2019 году компилятор Rust включили в дерево исходных текстов Android, но это была экспериментальная поддержка.
Сейчас в Android планируется добавить первые компоненты на Rust, это будут новые реализации механизма межпроцессного взаимодействия Binder и Bluetooth-стека. Все это хорошо, но зачем весь этот сыр-бор с включением Rust?
По словам представителей Google, Rust добавили в список языков разработки Android для усиления защищенности последнего, плюс для продвижения приемов безопасного программирования и повышения выявления проблем при работе с памятью в Android. Около 70% из всех опасных уязвимостей, которые выявлены в Android, вызваны ошибками при работе с памятью. Использование Rust дает возможность снизить риск появления уязвимостей, которые вызваны ошибками при работе с памятью, включая обращение к области памяти после ее освобождения и выход за границы буфера.
Безопасная работа с памятью обеспечивается в Rust во время компиляции посредством проверки ссылок, отслеживания владения объектами и учета времени жизни объектов (области видимости). Дополнительно — через оценку корректности доступа к памяти во время выполнения кода. Кроме того, Rust предоставляет средства защиты от целочисленных переполнений и требует обязательной инициализации значений переменных перед использованием. А еще он лучше обрабатывает ошибки в стандартной библиотеке и применяет концепцию неизменяемости (immutable) ссылок и переменных по умолчанию, предлагает сильную статическую типизацию для минимизации логических ошибок.
Что касается Android, то здесь безопасная работа с памятью обеспечивается в поддерживаемых языках Kotlin и Java. Правда, они не подходят для разработки системных компонентов из-за больших накладных расходов. Rust позволяет добиться увеличения производительности до близкой к языкам C и С++. А это значит, что язык можно использовать для разработки низкоуровневых частей платформы и компонентов для взаимодействия с оборудованием.
Безопасность кода на С и С++ в Android обеспечивается благодаря sandbox-изоляции, статическому анализу и fuzzing-тестированию. Возможности изоляции, правда, ограничены — они достигли предела возможностей. Эта ограниченность вызывает рост накладных расходов и увеличение объемов потребляемой памятью, что вызвано необходимостью порождения новых процессов. Плюс есть издержки, которые связаны с использованием IPC.
Sandbox не устраняет уязвимости в коде, все это работает иначе — снижаются риски и усложняется проведение атаки. К сожалению, для того чтобы бороться с проблемами максимально эффективно, нужно знать их все или подавляющую часть. А в ходе тестирования для выявления ошибок нужно создавать условия для их появления. Соответственно, абсолютно все условия предусмотреть невозможно, так что многие ошибки проходят мимо внимания разработчиков.
Компания Google использует так называемое «правило двух», в соответствии с которым для системных процессов любой добавляемый код должен подпадать не больше, чем под два условия из трех:
Работа с непроверенными входными данными.
Использование небезопасного языка программирования.
Выполнение процесса без жесткой sandbox-изоляции.
Соответственно, код для обработки внешних данных должен быть либо урезан до минимальных привилегий, либо написан на безопасном языке программирования.
Можно было бы подумать, что Google планирует переписать на Rust уже имеющийся CC++ код, но нет — его будут использовать для разработки нового кода. В этом есть смысл, поскольку, согласно статистике разработки, большая часть ошибок появляется либо в новом, либо недавно модифицированном коде. Так, около 50% ошибок работы с памятью в Android OS выявляются как раз в коде, который написан менее года назад.
Автор: Андрей