Архитектура высокого уровня
Введение
Чтобы понять эту кодовую базу, нужно охватить много информации, поэтому мы начнем с рассмотрения некоторых основных компонентов, чтобы терминология в будущих сообщениях и других статьях имела смысл. Я собираюсь опираться на множество ссылок, чтобы представить этот материал, потому что множество высокоуровневых описаний компонентов V8 уже дают гораздо лучшую работу, чем я мог бы в таком коротком посте.V8 в хроме
Сам V8 является относительно небольшой частью браузера Chromium. По сути, это совершенно отдельная база кода, которую можно встраивать в другие проекты (например, NodeJS!). Здесь важно понимать структуру, потому что мы будем много говорить об эксплуатации V8, но V8 обычно помещается в песочницу внутри другого процесса (процессов). Ошибки V8 должны быть связаны с дополнительными эксплойтами, чтобы добиться полноценного выполнения кода на большинстве систем. Для наших целей мы будем рады получить неограниченное выполнение кода в процессе V8. Например, это общий взгляд на Chrome, который показывает, как V8 расположен в нескольких слоях компонентов.
https://www.chromium.org/developers/how-tos/getting-around-the-chrome-source-code/Content.png
Самое замечательное в том, что V8 настолько модульный, что нам не нужно глубоко копаться в каких-либо других базах кода, чтобы понять его. Хотя просмотр исходного кода Chromium может дать лучшую картину для определенных вариантов дизайна, в этом, по большому счету, нет необходимости.Компоненты V8
Теперь мы рассмотрим некоторые из наиболее важных частей V8. Вы можете найти весь код здесь или на зеркале GitHub . В следующем посте я подробно расскажу о том, как эти компоненты написаны на C++, а пока давайте просто поймем, что они делают.Чтобы понять, как сегментируется V8, вы должны посмотреть, как современные веб-браузеры запускают JavaScript. Хотя это интерпретируемый язык, механизмы JS часто компилируют этот код в машинный код для конкретной архитектуры в процессе, известном как JIT-компиляция. Обычно интерпретируемый скрипт преобразуется в байт-код, который может выполняться независимо от архитектуры. Однако этот код работает очень медленно, так как все инструкции необходимо преобразовать из промежуточного языка в конкретные инструкции, поддерживаемые текущим процессором.
С другой стороны, скомпилированный код работает очень быстро, но требует некоторых предварительных затрат на преобразование исходного кода в исполняемый файл. Идея движков JS состоит в том, чтобы использовать JIT, чтобы получить лучшее из обоих миров. Они используют интерпретатор для немедленного запуска JavaScript; однако, если движок обнаружит, что какой-то код запускается часто, он скомпилирует этот раздел кода с несколькими оптимизациями для повышения производительности. То, как весь этот процесс работает в V8, лучше всего объясняет Бенедикт Мёрер в его пост .
Ignition
Ignition — интерпретатор V8. Многие эксплойты сосредоточены на JIT-коде и ошибках, допущенных в процессе компиляции. Однако скомпилированный код зависит от того, что выдает интерпретатор! Хотя здесь было обнаружено меньше ошибок, связанных с безопасностью, некоторые из них все же есть , на что указывают Димитри Фурни и Мориц Йодейт. Чтобы понять этот компонент, мы рекомендуем кратко просмотреть этот подробный документ от Google и прочитать это хорошее, краткое объяснение того, как V8 генерирует байт , написанное Франциской Хинкельманн.Ключевым выводом является то, что Ignition в первую очередь отвечает за генерацию байт-кода из JavaScript. Далее мы поговорим о других компанентах.
Turbofan
Turbofan является единственным компилятором JavaScript V8, хотя некоторые ресурсы могут содержать ссылки на Crankshaft (замененный в 2017 году :'( ). Другие движки также имеют различные уровни оптимизации, которые выполняются разными компиляторами, но это не относится к V8. Turbofan срабатывает, когда V8 замечает, что конкретная функция является «горячей» (это означает, что код был выполнен определенное количество раз). После того, как функция скомпилирована, она перенаправит поток управления на JIT-код при будущих вызовах этой функции. Большинство эксплойтов V8 сосредоточены на этом компоненте, и, как следствие, мы тоже. Некоторыми аспектами, которые нужно понять здесь, будут конвейер оптимизации, типизация переменных и проверки безопасности памяти. Есть отличная презентация ( слайды ) для базового понимания. Эта статья Джереми Фетиво — действительно потрясающее введение с точки зрения эксплуатации (обратите внимание, что она гораздо более глубока, чем мы до сих пор).Важная часть, которую нужно понять о Turbofan, заключается в том, что у него тяжелая работа. Изначально JavaScript не предназначался для компиляции. Он слабо типизирован и допускает безумную гибкость между типами, а стандарт ECMAScript не всегда кажется логичным (мягко говоря). Эта трудность является причиной того, что многие JS-движки, а не только V8, содержат ошибки. В нашем анализе прошлых эксплойтов мы увидим, что некоторые уязвимости возникают из-за расхождения в разумных предположениях о том, как должен вести себя JavaScript, по сравнению с тем, как он на самом деле .