echo 'main(){}' | gcc -xc - -o /dev/stdout | ???
Is there a way to run the output binary on a unix-like system?
EDIT: I needed it to run the output of g++ in a sandboxed environment where I can't write any file (nothing malicious, I promise).
echo 'main(){}' | gcc -xc - -o /dev/stdout | ???
Is there a way to run the output binary on a unix-like system?
EDIT: I needed it to run the output of g++ in a sandboxed environment where I can't write any file (nothing malicious, I promise).
I don't believe this is possible. The exec(2) system call always requires a filename or absolute path (the filename is always a char*
). posix_spawn
also has similar requirements for a filename.
The closest you could do is pipe the output into a named pipe and try executing from the pipe. That may work, although the shell may refuse to execute any file that does not have the --x--x--x
bits set. Create the pipe with mkfifo(1)
and see if you can get it to work.
Another approach would be to write something that reads standard input, writes a file out to a temporay area, sets the --x bits on it, forks and execs then deletes the file. The inode and contents will remain until the program finishes executing but it won't be accessible through the file system. When the process terminates the inode will be released and storage will be returned to the free list.
EDIT: As Mat points out, the first approach won't work as the loader will attempt to demand-page in the executable, which will generate random seek traffic on the file, and this isn't possible on a pipe. This leaves some sort of approach like the second.
echo
, cat
won't do the jump. Anyway, the sandbox (ideone.com) already allows running g++ and its output, so you are already running pretty much arbitrary code, I just needed to run g++ with additional compiler flags.
– Alex B
Oct 08 '11 at 05:35
A solution using memfd syscall: https://github.com/abbat/elfexec
It creates a named file descriptor in memory which could be used in exec
. A pseudo-code:
#include <linux/memfd.h>
...
int memfd = syscall(SYS_memfd_create, "someName", 0);
...
write(memfd,... elf-content...);
...
fexecve(memfd, argv, environ);
memfd.h
header unless you want to use MFD_CLOEXEC
(which will break #! /bin/sh
scripts because of bugs in linux' fexecve()
). That's not too complicated, you can include a 20-line working sample in your answer (eg. this git gist -- though that's not a drop-in replacement for your elfexec
, since that will also allow you to specify argv[0]
, and will run a binary only from a pipe (UUoC mandated ;-))
–
Jan 06 '19 at 13:36
.o
files to /tmp and die if it can't.
– Joshua
Jul 08 '19 at 18:06
This will automatically run the compilation of your code, but creates a file (temparaily) on the filesystem in order to do it.
echo 'main(){}' | gcc -xc -o /tmp/a.out && chmod u+x /tmp/a.out && /tmp/a.out && rm -f /tmp/a.out
(I'm currently testing this now, but I'm pretty sure this, or something close to it will work for you)
EDIT: If the goal of your piping is to cut physical disks out of the equation for speed, consider creating a ram disk to hold the intermediate file.
Just like @TheQUUX suggested, I haven't tested it my self but you might want to try out cling
- "the interactive C++ interpreter, built on top of LLVM and Clang libraries.".
Find more info here: https://cdn.rawgit.com/root-project/cling/master/www/index.html
From @kan answer and a perl implementation also done in this blog. This prints "Hello from C".
Requires: perl, base64, gunzip
#!/usr/bin/env bash
# From: https://unix.stackexchange.com/a/492559/257838
# And: https://magisterquis.github.io/2018/03/31/in-memory-only-elf-execution.html
execute_elf_string(){
local bin_encoded=$1
perl -e '
require qw/syscall.ph/;
# Create memfd
my $name = "";
my $fd = syscall(SYS_memfd_create(), $fn, 0);
if (-1 == $fd) { die "memfd_create: $!"; }
# Copy binary
open(my $fh, ">&=".$fd) or die "open: $!";
my $bin = `echo "'"$bin_encoded"'" | base64 -d | gunzip`;
print $fh $bin;
# Execute
exec {"/proc/$$/fd/$fd"} "memfd";
'
}
execute_elf_string "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"
To encode the binary to string, run
gzip -f < hi.so | base64
In any case, gcc
creates many temporary files when compiling C files into executables:
$ echo 'int main(){}' | strace -fe /open -o >(grep CREAT) gcc -xc -
96706 openat(AT_FDCWD, "/tmp/ccxiPEgx.s", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96707 openat(AT_FDCWD, "/tmp/ccxiPEgx.s", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 0
96706 openat(AT_FDCWD, "/tmp/ccIBSUD4.o", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96711 openat(AT_FDCWD, "/tmp/ccIBSUD4.o", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
96706 openat(AT_FDCWD, "/tmp/cciAP0FV.res", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96712 openat(AT_FDCWD, "/tmp/ccfZa2PH.cdtor.c", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96712 openat(AT_FDCWD, "/tmp/cc4LkCnx.cdtor.o", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96713 openat(AT_FDCWD, "a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
So you might as well create another one for the final executable.
If on Linux, it could be a pre-deleted one. For instance, with shells that still implement here-documents or here-strings as deleted temporary files such as zsh:
$ echo 'int main(){puts("Hello World");}' | { gcc -o /dev/fd/3 -x c - && /dev/fd/3; } 3<<< ''
<stdin>: In function ‘main’:
<stdin>:1:12: warning: implicit declaration of function ‘puts’ [-Wimplicit-function-declaration]
<stdin>:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘puts’
Hello World
csh
. – rozcietrzewiacz Oct 07 '11 at 15:02