Есть ли способ выполнить родной двоичный файл из трубы?

13
echo 'main(){}' | gcc -xc - -o /dev/stdout | ???

Есть ли способ запустить выходной двоичный файл в Unix-подобной системе?

EDIT: мне нужно было его запустить вывод г ++ в изолированном окружении , где я не могу писать какой - либо файл (ничего злонамеренного, я обещаю).

Алекс Б
источник
Я считаю, что основные механизмы безопасности должны этому помешать. Но если вы хотите запускать C-код на лету, просто используйте csh.
rozcietrzewiacz

Ответы:

10

Я не верю, что это возможно. Exec (2) системный вызов всегда требует имя файла или абсолютный путь (имя файла всегда char*). posix_spawnтакже имеет аналогичные требования для имени файла.

Самое близкое, что вы можете сделать, это направить вывод в именованный канал и попытаться выполнить его из канала. Это может сработать, хотя оболочка может отказаться выполнять любой файл, для которого не установлены --x--x--xбиты. Создайте трубу с помощью mkfifo(1)и посмотрите, сможете ли вы заставить ее работать.

Другой подход состоит в том, чтобы написать что-то, что читает стандартный ввод, записывает файл во временную область, устанавливает для него биты --x, разветвляется и исполняет, а затем удаляет файл. Индекс и содержимое останутся до тех пор, пока не завершится выполнение программы, но не будут доступны через файловую систему. Когда процесс завершится, индекс будет освобожден, и хранилище будет возвращено в свободный список.

РЕДАКТИРОВАТЬ: Как указывает Mat, первый подход не будет работать, так как загрузчик будет пытаться запросить страницу в исполняемом файле, который будет генерировать трафик случайного поиска по файлу, а это невозможно на конвейере. Это оставляет какой-то подход, как второй.

ConcernedOfTunbridgeWells
источник
2
Я был бы очень удивлен, если бы трубочный трюк сработал - вы не можете выполнять произвольный поиск по каналу и не можете их отобразить - я уверен, что это будет раздражать загрузчик / компоновщик времени выполнения :) Ваше второе предложение кажется хорошим хотя, ничего не могу придумать без временного файла.
Мат
@ Мат - я думаю, что ты прав. Пейджинг по требованию в исполняемом файле вызовет трафик произвольного доступа, который не будет работать на канале. По иронии судьбы он мог работать на SVR2.0 (последняя версия, которая не использовала пейджинг по требованию) - просто чтобы показать свой возраст, я действительно имел обыкновение иметь AT & T 3B2 / 400 с SVR2.0 в качестве O / S.
ConcernedOfTunbridgeWells
Подумав об этом еще немного, я уверен, что упаковщики exe, такие как UPX, могут выполнять распаковку и выполнение на носителях только для чтения. Измените любую заглушку, которую они прикрепляют к упакованным исполняемым файлам, чтобы читать из канала, а не распаковывать, и ... может работать.
Мат
У упаковщиков @Mat уже загружено изображение, и они не запускают новый процесс. Чтобы сделать подобное, мне нужен один из процессов для произвольного перехода к входным данным (что будет считаться уязвимостью безопасности).
Алекс Б
@ Алекс Б: Вы конкретно спрашиваете, как сделать произвольный переход во входные данные. Почему вы жалуетесь, когда вам предлагают именно это? Предназначена ли песочница специально для предотвращения того, что вы пытаетесь сделать?
Дэвид Шварц
7

Решение с использованием системного вызова memfd: https://github.com/abbat/elfexec

Он создает именованный дескриптор файла в памяти, который может быть использован в exec. Псевдокод:

#include <linux/memfd.h>
...
int memfd = syscall(SYS_memfd_create, "someName", 0);
...
write(memfd,... elf-content...);
...
fexecve(memfd, argv, environ);
кан
источник
1
Вам не нужен memfd.hзаголовок, если вы не хотите его использовать MFD_CLOEXEC(что приведет к поломке #! /bin/shскриптов из-за ошибок в linux ' fexecve()). Это не так уж сложно, вы можете включить в ваш ответ 20-строчный рабочий пример (например, этот git gist - хотя это не является заменой для вас elfexec, так как это также позволит вам указать argv[0]и запустит двоичный файл). только из трубы (UUoC поручено ;-))
mosvy
Так что я не понимаю, как это вообще сработает. gcc запишет .oфайлы в / tmp и умрет, если не сможет.
Джошуа
4

Вы можете попробовать tcc , который скомпилирует и выполнит программу за один шаг, без записи каких-либо промежуточных файлов. Это не GCC, что может быть проблемой для вас, но это невероятно быстро, поэтому он может даже поставить лучше, чем GCC для ваших целей.

TheQUUX
источник
2

Это автоматически запустит компиляцию вашего кода, но создаст файл (временно) в файловой системе, чтобы сделать это.

echo 'main(){}' | gcc -xc -o /tmp/a.out && chmod u+x /tmp/a.out && /tmp/a.out && rm -f /tmp/a.out

(Я сейчас проверяю это сейчас, но я почти уверен, что или что-то близкое к этому подойдет вам)

РЕДАКТИРОВАТЬ: Если цель вашего трубопровода состоит в том, чтобы вырезать физические диски из уравнения для скорости, подумайте о создании оперативного диска для хранения промежуточного файла.

dtyler
источник
Это, конечно, сработает, но при этом упускает суть вопроса - выполнить двоичный код, который никогда не записывается на диск.
rozcietrzewiacz
@rozcietrzewiacz Я надеюсь, что было бы полезно, если бы цель состояла в том, чтобы легко запустить фрагмент кода на лету, не имея дело с требуемым физическим файлом.
dtyler
Да, я понимаю. Но для этого можно было бы просто использовать csh.
rozcietrzewiacz
0

Точно так же, как предложил @TheQUUX , я сам не тестировал, но вы можете попробовать cling- «интерактивный интерпретатор C ++, построенный на основе библиотек LLVM и Clang».

Более подробную информацию можно найти здесь: https://cdn.rawgit.com/root-project/cling/master/www/index.html.

Дорон Бехар
источник