Как эффективно выполнить ненадежную функцию кросс-платформенным способом?
Я пишу кроссплатформенное приложение с открытым исходным кодом, написанное на C++ и предназначенное для Windows, Mac и Linux на процессорах x86. Приложение создает поток данных (целые числа), которые необходимо проверить, и мое приложение будет выполнять действия в зависимости от результата проверки. Существует несколько валидаторов, которые мы будем называть "модулями", и их можно менять друг на друга.
Любой может писать и делиться модулями с другими пользователями, поэтому мое приложение должно гарантировать, что написанные со злым умыслом модули не смогут нанести вред пользователю каким-либо образом (возможно, кроме как из-за высокой загрузки ЦП, и в этом случае мое приложение сможет убить модуль после некоторого количество времени - это можно сделать с помощью суррогатного процесса). Кроме того, поток данных отправляется с высокой скоростью (до 100 кБ / с).
К счастью, код в этих модулях - это, как правило, простые арифметические операции с данными в потоке (обычно обработка каждого входящего целого числа в постоянное время), и им не нужно совершать какие-либо системные вызовы (даже выделение кучи).
Я рассмотрел следующие возможности (все они с некоторыми недостатками):
Основанная на ядре песочница
В Linux мы можем использовать безопасные вычисления ( seccomp), которые не позволяют процессу делать какие-либо системные вызовы, кроме чтения и записи с уже открытыми файловыми деструкторами. Создатели модулей пишут свои модули как единую функцию, которая принимает входные и выходные файловые дескрипторы (на языке, подобном C или C++) и компилирует их в общий объект, а затем распространяет этот общий объект.
Мое приложение, вероятно, подготовит дескрипторы файлов ввода и вывода, затем
fork()
сам илиexec()
суррогатный процесс, и этот дочерний процесс используетdlopen()
а такжеdlsym()
чтобы получить указатель на ненадежную функцию. Тогда строгий безопасный режим вычислений будет включен, прежде чем выполнять ненадежную функцию.Недостатки: есть проблема, которая
dlopen()
фактически запустит функцию конструктора из разделяемой библиотеки. Это также должно быть правильно помещено в песочницу, и я не могу придумать, как это сделать. Также, конечно, эта штука будет работать только на Linux. Насколько я знаю, нет способа запретить системные вызовы WinNT в Windows, поэтому подобное решение в Windows не будет очень безопасным.
Песочница на уровне приложений
[[Любая форма песочницы на уровне приложения означает, что мы не можем запустить ненадежный машинный код любой формы. Ненадежная функция может перезаписать свое возвращаемое значение или данные вне своего стека вызовов, тем самым ставя под угрозу все приложение (и эффективно получая любые разрешения, которые были у исходного приложения). ]]
Модули make используют простой язык сценариев, который не поддерживает какие-либо системные вызовы - только арифметические операции и, возможно, возможность чтения входного потока. Моя заявка будет содержать переводчика для этого языка.
Недостатки: К сожалению, я не нашел этот язык сценариев. Многие языки сценариев имеют широкие функциональные возможности (например, Python), а песочница (например , песочница PyPy) просто фильтрует системные вызовы ОС. Я бы поставлял много бесполезного кода интерпретатора с моим приложением, и, возможно, он более подвержен проблемам безопасности из-за ошибок в интерпретаторе, чем язык, у которого просто нет функциональности для выполнения каких-либо действий, кроме простых вычислений и инструкций потока управления (в основном функция, которая не делает никаких системных вызовов). Кроме того, сортировка данных между C++ (машинный код) и языком сценариев обычно является медленным процессом.
Распространите модули с "безопасным" скомпилированным языком, который снова не поддерживает системные вызовы. Моя заявка будет содержать JIT для этого языка.
Маршаллинг не понадобится, потому что мое приложение будет вызывать машинный код JITted ненадежного модуля, поэтому производительность через эту границу должна быть быстрой. Ненадежный модуль теперь не сможет повреждать стек, пытаться вернуть ориентированное программирование или выполнять какие-либо другие вредоносные действия из-за языковых ограничений и проверок "безопасного" языка. WebAssembly - это первый и единственный язык, который приходит на ум (если его можно назвать языком). (Насколько я могу судить, WebAssembly предоставляет гарантии безопасности для моего варианта использования, верно?)Недостатки: Кажется, что существующие реализации WebAssembly основаны на браузере, поэтому мне пришлось бы украсть реализацию из браузера с открытым исходным кодом. Это кажется большой работой, учитывая, что мне пришлось бы отсоединить ее от всех JavaScript и других частей браузера. Тем не менее, автономная JIT WebAssembly на основе LLVM, похоже, находится в стадии разработки.
Вопрос:
Каков наилучший способ эффективного выполнения ненадежной функции, работающей в Windows, Mac и Linux?
Прямо сейчас, я думаю, что язык сценариев, вероятно, будет самым безопасным и самым простым для авторов модулей. Но для более эффективного решения WebAssembly, вероятно, лучше. Я прав или есть лучшие или более простые решения, о которых я не думал?
(Примечание: я думаю, что несколько пар тегов, использованных в этом вопросе, никогда раньше не встречались вместе!)
1 ответ
Что касается WebAssembly:
К сожалению, пока не существует самостоятельной реализации с качеством производства. Я ожидаю, что некоторые появятся в будущем, но это еще не произошло.
По историческим причинам все существующие производственные реализации являются частью виртуальной машины JavaScript. К счастью, ни одна из этих виртуальных машин не связана с браузером. Если вы не возражаете против включения неиспользованного багажа JS, вы можете встраивать его как есть (вырвать JS будет очень сложно). Одна проблема, однако, заключается в том, что эти виртуальные машины еще не предоставляют интерфейсы встраивания специально для Wasm. Вы должны пройти через JS, что глупо.
Существует исходный дизайн для API C и C++ для WebAssembly, который предоставит прямой доступ к встроенной виртуальной машине Wasm. Он должен быть нейтральным к ВМ, т. Е. Может быть реализован любой существующей ВМ (репозиторий содержит реализацию прототипа поверх V8). Это может превратиться в стандарт, но я не могу обещать никаких сроков. Прямо сейчас это только для смелых.