proot-3.0.2/0000755000175000017500000000000012156552156012243 5ustar ivoireivoireproot-3.0.2/.travis.yml0000644000175000017500000000027612156552156014361 0ustar ivoireivoirelanguage: c compiler: gcc before_install: - sudo apt-get update -qq - sudo apt-get install -qq libtalloc-dev script: make -C src && env PATH=/bin:/usr/bin:/sbin:/usr/sbin make -C tests proot-3.0.2/README.rst0000777000175000017500000000000012156552156016512 2doc/manual.txtustar ivoireivoireproot-3.0.2/tests/0000755000175000017500000000000012156552156013405 5ustar ivoireivoireproot-3.0.2/tests/test-88888888.c0000644000175000017500000000245212156552156015510 0ustar ivoireivoire#include #include #include #include #include #include #include int main() { int fd; fd = open("/bin/true", O_RDONLY); if (fd < 0) { perror(NULL); exit(EXIT_FAILURE); } close(fd); fd = open("/bin/true/", O_RDONLY); if (fd >= 0 || errno != ENOTDIR) { perror(NULL); exit(EXIT_FAILURE); } close(fd); fd = open("/bin/true/.", O_RDONLY); if (fd >= 0 || errno != ENOTDIR) { perror(NULL); exit(EXIT_FAILURE); } close(fd); fd = open("/bin/true/..", O_RDONLY); if (fd >= 0 || errno != ENOTDIR) { perror(NULL); exit(EXIT_FAILURE); } close(fd); fd = open("/6a05942f08d5a72de56483487963deec", O_RDONLY); if (fd >= 0 || errno != ENOENT) { perror(NULL); exit(EXIT_FAILURE); } close(fd); fd = open("/6a05942f08d5a72de56483487963deec/", O_RDONLY); if (fd >= 0 || errno != ENOENT) { perror(NULL); exit(EXIT_FAILURE); } close(fd); fd = open("/6a05942f08d5a72de56483487963deec/.", O_RDONLY); if (fd >= 0 || errno != ENOENT) { perror(NULL); exit(EXIT_FAILURE); } close(fd); #if 0 /* This test fails in OBS, why? */ fd = open("/6a05942f08d5a72de56483487963deec/..", O_RDONLY); if (fd >= 0 || errno != ENOENT) { perror(NULL); exit(EXIT_FAILURE); } close(fd); #endif exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-092c5e26.sh0000644000175000017500000000264012156552156015777 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/echo ] || [ ! -x ${ROOTFS}/bin/argv0 ] || [ -z `which mcookie` ] || [ -z `which grep` ] || [ -z `which cat` ] || [ -z `which rm` ]; then exit 125; fi TMP=$(mcookie) TMP_ABS=/tmp/${TMP} ${PROOT} -r ${ROOTFS} argv0 | grep '^argv0$' ${PROOT} -r ${ROOTFS} /bin/argv0 | grep '^/bin/argv0$' cat > ${ROOTFS}/${TMP_ABS} < ${ROOTFS}/${TMP_ABS} < #include #include #include #include #include #include bool sigtrap_received = false; void handler(int signo) { if (signo == SIGTRAP) sigtrap_received = true; } int main() { struct sigaction sa; int status; sa.sa_flags = 0; sa.sa_handler = handler; status = sigemptyset(&sa.sa_mask); if (status < 0) { perror("sigemptyset()"); exit(EXIT_FAILURE); } status = sigaction(SIGTRAP, &sa, 0); if (status < 0) { perror("sigaction(SIGTRAP)"); exit(EXIT_FAILURE); } status = raise(SIGTRAP); if (status != 0) { perror("raise(SIGTRAP)"); exit(EXIT_FAILURE); } if (sigtrap_received) exit(EXIT_SUCCESS); else exit(EXIT_FAILURE); } proot-3.0.2/tests/test-1fedd9a3.sh0000644000175000017500000000145212156552156016220 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which id` ] || [ -z `which mkdir` ] || [ -z `which touch` ] || [ -z `which chmod` ] || [ -z `which stat` ] || [ -z `which grep` ]; then exit 125; fi if [ `id -u` == 0 ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir -p ${TMP}/foo chmod a-rwx ${TMP}/foo chmod a-rwx ${TMP} ! ${PROOT} touch ${TMP}/foo/bar [ $? -eq 0 ] ${PROOT} -0 touch ${TMP}/foo/bar stat -c %a ${TMP} | grep '^0$' ! stat -c %a ${TMP}/foo [ $? -eq 0 ] ${PROOT} -0 stat -c %a ${TMP}/foo | grep '^0$' chmod -R a+rwx ${TMP} chmod a-rwx ${TMP}/foo/bar chmod a-rwx ${TMP}/foo chmod a-rwx ${TMP} ! ${PROOT} chmod g+w ${TMP}/foo/bar [ $? -eq 0 ] ${PROOT} -0 chmod g+w ${TMP}/foo/bar chmod u+wx ${TMP} chmod u+x ${TMP}/foo stat -c %a ${TMP}/foo/bar | grep '^20$' chmod -R +rwx ${TMP} rm -fr ${TMP} proot-3.0.2/tests/test-2401b850.sh0000644000175000017500000000417412156552156015711 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which echo` ] || [ -z `which rm` ] || [ -z `which realpath` ] || [ -z `which touch` ] || [ -z `which chmod` ] || [ -z `which grep` ]; then exit 125; fi TMP=/tmp/$(mcookie) TMP2=/tmp/$(mcookie) rm -f ${TMP} touch ${TMP} chmod +x ${TMP} # Valgrind prepends "/bin/sh" in front of foreign binaries and uses # LD_PRELOAD. if $(echo ${PROOT} | grep -q valgrind); then ENV=$(realpath $(which env)) PROOT="env PROOT_FORCE_FOREIGN_BINARY=1 ${PROOT}" COMMAND1="-E LD_PRELOAD=.* -0 /bin/sh /bin/sh ${TMP}" TEST1="-- -U LD_LIBRARY_PATH -E LD_PRELOAD=.* -0 env ${ENV} LD_LIBRARY_PATH=test1 ${TMP}" TEST2="-- -E LD_PRELOAD=.* -E LD_LIBRARY_PATH=test2 -0 /bin/sh /bin/sh ${TMP}" TEST3="-- -E LD_PRELOAD=.* -E LD_LIBRARY_PATH=test2 -0 env ${ENV} LD_LIBRARY_PATH=test1 ${TMP}" TEST4="-- -U LD_LIBRARY_PATH -E LD_PRELOAD=.* -0 env ${ENV} LD_TRACE_LOADED_OBJECTS=1 ${TMP}" COMMAND2="-E LD_PRELOAD=.* -0 ${TMP} ${TMP} ${TMP2}" else COMMAND1="-0 ${TMP} ${TMP}" TEST1="-- -E LD_LIBRARY_PATH=test1 ${COMMAND1}" TEST2="-- -E LD_LIBRARY_PATH=test2 ${COMMAND1}" TEST3="${TEST1}" TEST4="-- -E LD_TRACE_LOADED_OBJECTS=1 -E LD_LIBRARY_PATH=.+ ${COMMAND1}" COMMAND2="-0 ${TMP} ${TMP} ${TMP2}" fi ${PROOT} -q true / ${TMP} ! ${PROOT} -q false / ${TMP} [ $? -eq 0 ] (cd /; ${PROOT} -q ./$(which true) / ${TMP}) ! (cd /; ${PROOT} -q ./$(which false) / ${TMP}) [ $? -eq 0 ] HOST_LD_LIBRARY_PATH=$(${PROOT} -q 'echo --' / env | grep LD_LIBRARY_PATH) test ! -z "${HOST_LD_LIBRARY_PATH}" unset LD_LIBRARY_PATH ${PROOT} -q 'echo --' / ${TMP} | grep -- "^-- -U LD_LIBRARY_PATH ${COMMAND1}$" ${PROOT} -q 'echo --' / env LD_LIBRARY_PATH=test1 ${TMP} | grep -- "^${TEST1}$" env LD_LIBRARY_PATH=test2 ${PROOT} -q 'echo --' / ${TMP} | grep -- "^${TEST2}$" env LD_LIBRARY_PATH=test2 ${PROOT} -q 'echo --' / env LD_LIBRARY_PATH=test1 ${TMP} | grep -- "^${TEST3}$" ${PROOT} -q 'echo --' / env LD_TRACE_LOADED_OBJECTS=1 ${TMP} | grep -E -- "^${TEST4}$" rm -f ${TMP2} echo "#!${TMP}" > ${TMP2} chmod +x ${TMP2} ${PROOT} -q 'echo --' ${TMP2} | grep -- "^-- -U LD_LIBRARY_PATH ${COMMAND2}$" rm -fr ${TMP} ${TMP2} proot-3.0.2/tests/test-b94dd86a.sh0000644000175000017500000000013312156552156016140 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/true ]; then exit 125; fi ${PROOT} -w /bin ${ROOTFS} ./true proot-3.0.2/tests/test-aaaaaaaa.sh0000644000175000017500000000346212156552156016412 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/true ] || [ -z `which id` ] || [ -z `which mcookie` ] || [ -z `which ln` ] || [ -z `which rm` ]; then exit 125; fi if [ `id -u` == 0 ]; then exit 125; fi DONT_EXIST=/$(mcookie) ${PROOT} ${ROOTFS} true ! ${PROOT} ${DONT_EXIST} true [ $? -eq 0 ] ${PROOT} -r ${ROOTFS} true ${PROOT} -r /etc -r ${ROOTFS} true ! ${PROOT} -r ${ROOTFS} -r ${DONT_EXIST} true [ $? -eq 0 ] ! ${PROOT} -r ${DONT_EXIST} ${ROOTFS} true [ $? -eq 0 ] ! ${PROOT} ${ROOTFS} -r ${ROOTFS} true [ $? -eq 0 ] ! ${PROOT} -v [ $? -eq 0 ] ${PROOT} -b /bin/true:${DONT_EXIST} ${DONT_EXIST} ! ${PROOT} -r / -b /etc:/ true [ $? -eq 0 ] ! ${PROOT} -b /etc:/ true [ $? -eq 0 ] ${PROOT} -b /etc:/ -r / true TMP1=/tmp/$(mcookie) TMP2=/tmp/$(mcookie) echo "${TMP1}" > ${TMP1} echo "${TMP2}" > ${TMP2} REGULAR=/tmp/$(mcookie) SYMLINK_TO_REGULAR=/tmp/$(mcookie) ln -s ${REGULAR} ${SYMLINK_TO_REGULAR} ${PROOT} -v -1 -b ${TMP1}:${REGULAR} -b ${TMP2}:${SYMLINK_TO_REGULAR} cat ${REGULAR} | grep "^${TMP2}$" ${PROOT} -v -1 -b ${TMP2}:${SYMLINK_TO_REGULAR} -b ${TMP1}:${REGULAR} cat ${REGULAR} | grep "^${TMP1}$" ${PROOT} -v -1 -b ${TMP1}:${REGULAR} -b ${TMP2}:${SYMLINK_TO_REGULAR}! cat ${REGULAR} | grep "^${TMP1}$" ${PROOT} -v -1 -b ${TMP1}:${REGULAR} -b ${TMP2}:${SYMLINK_TO_REGULAR}! cat ${SYMLINK_TO_REGULAR} | grep "^${TMP2}$" ${PROOT} -v -1 -b ${TMP1}:${REGULAR}! -b ${TMP2}:${SYMLINK_TO_REGULAR}! cat ${REGULAR} | grep "^${TMP1}$" ${PROOT} -v -1 -b ${TMP1}:${REGULAR}! -b ${TMP2}:${SYMLINK_TO_REGULAR}! cat ${SYMLINK_TO_REGULAR} | grep "^${TMP2}$" ${PROOT} -v -1 -b ${TMP1}:${REGULAR} -b ${TMP2}:${SYMLINK_TO_REGULAR} cat ${SYMLINK_TO_REGULAR} | grep "^${TMP2}$" ${PROOT} -v -1 -b ${TMP2}:${SYMLINK_TO_REGULAR} -b ${TMP1}:${REGULAR} cat ${SYMLINK_TO_REGULAR} | grep "^${TMP1}$" rm -fr ${TMP1} ${TMP2} ${REGULAR} $SYMLINK_TO_REGULAR} proot-3.0.2/tests/test-d2175fc3.sh0000644000175000017500000000060312156552156016053 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readlink ] || [ -z `which grep` ]; then exit 125; fi ${PROOT} ${ROOTFS} /bin/readlink /bin/abs-true | grep '^/bin/true$' ${PROOT} ${ROOTFS} /bin/readlink /bin/rel-true | grep '^\./true$' ${PROOT} -b /:/host-rootfs ${ROOTFS} /bin/readlink /bin/abs-true | grep '^/bin/true$' ${PROOT} -b /:/host-rootfs ${ROOTFS} /bin/readlink /bin/rel-true | grep '^./true$' proot-3.0.2/tests/test-5bed7141.c0000644000175000017500000000335112156552156015664 0ustar ivoireivoire#include #include #include #include #include void *routine(void *path) { int status; status = chdir(path); if (status < 0) { perror("chdir"); pthread_exit((void *)-1); } pthread_exit(NULL); } static void pterror(const char *message, int error) { fprintf(stderr, "%s: %s\n", message, strerror(error)); } void check_cwd(const char *expected) { char path[PATH_MAX]; int status; if (getcwd(path, PATH_MAX) == NULL) { perror("getcwd"); exit(EXIT_FAILURE); } if (strcmp(path, expected) != 0) { fprintf(stderr, "getcwd: %s != %s\n", path, expected); exit(EXIT_FAILURE); } status = readlink("/proc/self/cwd", path, PATH_MAX - 1); if (status < 0) { perror("readlink"); exit(EXIT_FAILURE); } path[status] = '\0'; if (strcmp(path, expected) != 0) { fprintf(stderr, "readlink /proc/self/cwd: %s != %s\n", path, expected); exit(EXIT_FAILURE); } } int main(int argc, char *argv[]) { pthread_t thread; int child_status; void *result; int status; status = pthread_create(&thread, NULL, routine, "/etc"); if (status != 0) { pterror("pthread_create", status); exit(EXIT_FAILURE); } status = pthread_join(thread, &result); if (status != 0) { pterror("pthread_create", status); exit(EXIT_FAILURE); } if (result != NULL) exit(EXIT_FAILURE); check_cwd("/etc"); switch (fork()) { case -1: perror("readlink"); exit(EXIT_FAILURE); case 0: /* child */ status = chdir("/usr"); if (status < 0) { perror("chdir"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); default: status = wait(&child_status); if (status < 0 || child_status != 0) { perror("wait()"); exit(EXIT_FAILURE); } check_cwd("/etc"); break; } exit(EXIT_SUCCESS); } proot-3.0.2/tests/false.c0000644000175000017500000000003512156552156014641 0ustar ivoireivoireint main(void) { return 1; } proot-3.0.2/tests/test-af062114.c0000644000175000017500000000105512156552156015573 0ustar ivoireivoire#include #include #include #include #include int main(void) { int fd; int status; bool stop = false; fd = open("/proc/self/cmdline", O_RDONLY); if (fd < 0) { perror("open()"); exit(EXIT_FAILURE); } do { char buffer; status = read(fd, &buffer, 1); if (status < 0) { perror("read()"); exit(EXIT_FAILURE); } stop = (status == 0); status = write(1, &buffer, 1); if (status < 0) { perror("write()"); exit(EXIT_FAILURE); } } while (!stop); exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-e940896f.sh0000644000175000017500000000110212156552156016006 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readdir ] || [ -z `which mcookie` ] || [ -z `which rm` ] || [ -z `which mkdir` ] || [ -z `which chmod` ] || [ -z `which rm` ]; then exit 125; fi TMP1=/tmp/$(mcookie) TMP2=${TMP1}/$(mcookie)/$(mcookie) rm -fr ${TMP1} rm -fr ${ROOTFS}/${TMP1} mkdir -p ${TMP2} mkdir -p ${ROOTFS}/${TMP1} chmod -w ${ROOTFS}/${TMP1} cd ${TMP2} ${PROOT} -r ${ROOTFS} -b . readdir ${TMP1} ${PROOT} -r ${ROOTFS} -b . readdir ${TMP2} ${PROOT} -r ${ROOTFS} -b . readdir ${TMP2}/.. ${PROOT} -r ${ROOTFS} -b . readdir ${TMP2}/../.. rm -fr ${TMP1} rm -fr ${ROOTFS}/${TMP1} proot-3.0.2/tests/test-e99993c8.sh0000644000175000017500000000047312156552156016030 0ustar ivoireivoireif [ -z `which uname` ] || [ -z `which grep` ]; then exit 125; fi LONG_RELEASE=0123456789012345678901234567890123456789012345678901234567890123456789 ${PROOT} -k 3.33.333 uname -r | grep ^3.33.333$ ${PROOT} -k ${LONG_RELEASE} uname -r | grep ^0123456789012345678901234567890123456789012345678901234567890123$ proot-3.0.2/tests/test-pppppppp.sh0000644000175000017500000000047112156552156016577 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which true` ] || [ -z `which mkdir` ]|| [ -z `which env` ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir -p ${TMP}/true ! ${PROOT} true if [ $? -eq 0 ]; then exit 125; fi env PATH=${TMP}:${PATH} ${PROOT} true env PATH=${TMP}:${PATH} ${PROOT} env true rm -fr ${TMP} proot-3.0.2/tests/test-3dec4597.sh0000644000175000017500000000016612156552156016070 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/pwd ]; then exit 125; fi ${PROOT} -m /tmp:/longer-tmp -w /longer-tmp ${ROOTFS} /bin/pwd proot-3.0.2/tests/test-79cf6614.c0000644000175000017500000000120712156552156015617 0ustar ivoireivoire/* * Submitted-by: Thomas P. HIGDON * Ref.: https://groups.google.com/d/msg/proot_me/4WbUndy-aXI/lmKiDfoIK_IJ */ #include #include #include #include int main() { int status; struct timeval times[2] = { {.tv_sec = 52353, .tv_usec = 0}, { .tv_sec = 52353, .tv_usec = 0 } }; char tmp[] = "proot-XXXXXX"; mktemp(tmp); if (tmp[0] == '\0') exit(EXIT_FAILURE); (void) unlink(tmp); status = symlink("/etc/fstab", tmp); if (status < 0) exit(EXIT_FAILURE); status = lutimes(tmp, times); exit(status < 0 && errno != ENOSYS ? EXIT_FAILURE : EXIT_SUCCESS); } proot-3.0.2/tests/symlink.c0000644000175000017500000000071012156552156015235 0ustar ivoireivoire#include /* syscall(2), */ #include /* perror(3), fprintf(3), */ #include /* exit(3), */ #include /* SYS_symlink, */ int main(int argc, char *argv[]) { int status; if (argc != 3) { fprintf(stderr, "usage: symlink REFEREE REFERER\n"); exit(EXIT_FAILURE); } status = syscall(SYS_symlink, argv[1], argv[2]); if (status < 0) { perror("symlink()"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-ffffffff.sh0000644000175000017500000000034612156552156016460 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which touch` ] || [ -z `which stat` ] || [ -z `which grep` ] || [ -z `which rm` ]; then exit 125; fi TMP=/tmp/$(mcookie) touch ${TMP} ${PROOT} -0 stat -c %u:%g ${TMP} | grep 0:0 rm ${TMP} proot-3.0.2/tests/test-bdc90417.c.todo0000644000175000017500000000166512156552156016637 0ustar ivoireivoire#define _GNU_SOURCE /* See feature_test_macros(7) */ #include /* execv(3), syscall(2), */ #include /* SYS_*, */ #include /* *rlimit(2), */ #include /* *rlimit(2), */ #include /* EXIT_*, exit(3), */ int main(int argc, char *argv[]) { char *const dummy_argv[] = { "test", "stage2", NULL }; int brk1, brk2, status; struct rlimit rlimit; switch (argc) { case 1: /* 1st step: set the stack limit to the max. */ status = getrlimit(RLIMIT_STACK, &rlimit); if (status < 0) exit(EXIT_FAILURE); rlimit.rlim_cur = rlimit.rlim_max; status = setrlimit(RLIMIT_STACK, &rlimit); if (status < 0) exit(EXIT_FAILURE); return execv(argv[0], dummy_argv); default: /* 2nd step: try to allocate some heap space. */ brk1 = syscall(SYS_brk, 0); brk2 = syscall(SYS_brk, brk1 + 1024 * 1024); exit(brk1 != brk2 ? EXIT_SUCCESS : EXIT_FAILURE); } } proot-3.0.2/tests/test-99999999.sh0000644000175000017500000000331712156552156015711 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readlink ] || [ -z `which readlink` ] || [ -z `which cut` ] || [ -z `which grep` ] || [ -z `which md5sum` ]; then exit 125; fi WHICH_READLINK=$(readlink -f $(which readlink)) ${PROOT} / readlink /proc/self/exe | grep ^${WHICH_READLINK}$ ${PROOT} / sh -c 'readlink /proc/self/exe' | grep ^${WHICH_READLINK}$ ${PROOT} / bash -c 'readlink /proc/$$/exe' | grep ^${WHICH_READLINK}$ ${PROOT} -b /proc ${ROOTFS} readlink /proc/self/exe | grep ^/bin/readlink$ ${PROOT} / readlink /proc/1/../self/exe | grep ^${WHICH_READLINK}$ ${PROOT} / sh -c 'readlink /proc/1/../self/exe' | grep ^${WHICH_READLINK}$ ${PROOT} / bash -c 'readlink /proc/1/../$$/exe' | grep ^${WHICH_READLINK}$ ${PROOT} -b /proc ${ROOTFS} readlink /proc/1/../self/exe | grep ^/bin/readlink$ ! ${PROOT} / readlink /proc/self/exe/ [ $? -eq 0 ] ! ${PROOT} / readlink /proc/self/exe/.. [ $? -eq 0 ] ! ${PROOT} / readlink /proc/self/exe/../exe [ $? -eq 0 ] ! ${PROOT} -b /proc / readlink /proc/self/exe/ [ $? -eq 0 ] ! ${PROOT} -b /proc / readlink /proc/self/exe/.. [ $? -eq 0 ] ! ${PROOT} -b /proc / readlink /proc/self/exe/../exe [ $? -eq 0 ] TEST=$(${PROOT} / readlink /proc/self/fd/0 | grep -E "^/proc/[[:digit:]]+/fd/0$" | true) test -z $TEST TEST=$(${PROOT} -b /proc ${ROOTFS} readlink /proc/self/fd/0 | grep -E "^/proc/[[:digit:]]+/fd/0$" | true) test -z $TEST if [ ! -z $$ ]; then TEST=$(readlink -f /proc/$$/exe) ${PROOT} / sh -c 'true; readlink /proc/$$/exe' | grep ${TEST} fi MD5=$(md5sum $(which md5sum) | cut -f 1 -d ' ') MD5_PROOT=$(${PROOT} / md5sum /proc/self/exe | cut -f 1 -d ' ') test ${MD5_PROOT} = ${MD5} MD5_PROOT=$(${PROOT} -b /proc / md5sum /proc/self/exe | cut -f 1 -d ' ') test ${MD5_PROOT} = ${MD5} proot-3.0.2/tests/test-0238c7f1.sh0000644000175000017500000000042012156552156015767 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/pwd ] || [ -z `which grep` ]; then exit 125; fi ${PROOT} -m /:/hostfs -w /hostfs/etc ${ROOTFS} pwd | grep '^/hostfs/etc$' ${PROOT} -m /:/hostfs -w /hostfs ${ROOTFS} pwd | grep '^/hostfs$' ${PROOT} -m /:/hostfs -w / ${ROOTFS} pwd | grep '^/$' proot-3.0.2/tests/test-d1da0d8d.sh0000644000175000017500000000045712156552156016215 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readlink ] || [ ! -e /proc/self/cwd ] || [ -z `which grep` ]; then exit 125; fi ${PROOT} -m /proc -m /tmp:/asym -w /asym ${ROOTFS} /bin/readlink /proc/self/cwd | grep '^/asym$' ${PROOT} -m /proc -m /tmp:/asym -w /tmp ${ROOTFS} /bin/readlink /proc/self/cwd | grep '^/tmp$' proot-3.0.2/tests/test-c6b77b77.sh0000644000175000017500000000013212156552156016062 0ustar ivoireivoireif [ -z `which make` ]; then exit 125; fi ${PROOT} / make -f ${PWD}/test-c6b77b77.mk proot-3.0.2/tests/test-xxxxxxxx.c0000644000175000017500000000171412156552156016510 0ustar ivoireivoire#include #include #include #include #include extern char *environ[]; #define CONTENT "this isn't an executable" int main(void) { char * const argv[] = { "argv0", "argv1", "argv2", NULL }; char tmp_name[] = "/tmp/proot-XXXXXX"; int status; int fd; status = execve("/tmp", argv, environ); if (errno != EACCES) { perror("execve (1)"); exit(EXIT_FAILURE); } fd = mkstemp(tmp_name); if (fd < 0) { perror("mkstemp"); exit(EXIT_FAILURE); } status = write(fd, CONTENT, sizeof(CONTENT)); if (status != sizeof(CONTENT)) { perror("write"); exit(EXIT_FAILURE); } close(fd); status = chmod(tmp_name, 0700); if (status < 0) { perror("chmod"); exit(EXIT_FAILURE); } status = execve(tmp_name, argv, environ); if (errno != ENOEXEC) { perror("execve (2)"); exit(EXIT_FAILURE); } printf("Check the stack integrity: %F + %F\n", (double) status, (double) errno); exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-d2175fc4.c0000644000175000017500000000117612156552156015672 0ustar ivoireivoire#include /* syscall(2), */ #include /* perror(3), fprintf(3), */ #include /* PATH_MAX, */ #include /* exit(3), */ #include /* bzero(3), */ #include /* SYS_readlink, */ int main(int argc, char *argv[]) { char path[PATH_MAX]; int status; bzero(path, sizeof(path)); status = syscall(SYS_readlink, "/proc/self/exe", path, PATH_MAX); if (status < 0) { perror("readlink()"); exit(EXIT_FAILURE); } if (status >= PATH_MAX) return 125; if (path[status] != '\0') { path[PATH_MAX - 1] = '\0'; puts(path); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-c6b77b77.mk0000644000175000017500000000013512156552156016062 0ustar ivoireivoireSHELL=/bin/bash FOO:=$(shell test -e /dev/null && echo OK) all: @/usr/bin/test -n "$(FOO)" proot-3.0.2/tests/test-5bed7142.sh0000644000175000017500000000026112156552156016052 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/pwd ] || [ -z `which mkdir` ] || [ -z `which grep` ]; then exit 125; fi mkdir -p ${ROOTFS}/${PWD} ${PROOT} -v 1 -w . ${ROOTFS} pwd | grep ^${PWD}$ proot-3.0.2/tests/test-hhhhhhhh.sh0000644000175000017500000000075212156552156016501 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/true ] || [ -z `which mcookie` ] || [ -z `which true` ] || [ -z `which mkdir` ] || [ -z `which ln` ] || [ -z `which rm` ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir -p ${ROOTFS}/${TMP} A=$(mcookie) B=$(mcookie) ln -s /bin/true ${ROOTFS}/${TMP}/${A} ln -s ${TMP}/${A} ${ROOTFS}/${TMP}/${B} env PATH=${TMP} ${PROOT} ${ROOTFS} ${B} rm -f ${TMP}/${B} # just in case it also exists in the host env. ${PROOT} ${ROOTFS} /${TMP}/${B} rm -fr ${ROOTFS}/${TMP} proot-3.0.2/tests/test-305ae31d.c0000644000175000017500000000251112156552156015652 0ustar ivoireivoire#include #include #include #include #include #include #include int main() { int fd; int fd_dir; int fd_file; char path[64]; /* 64 > sizeof("/proc//fd/") + 2 * sizeof(#ULONG_MAX) */ int status; fd_dir = open("/bin", O_RDONLY); if (fd_dir < 0) exit(EXIT_FAILURE); fd_file = open("/bin/true", O_RDONLY); if (fd_file < 0) exit(EXIT_FAILURE); status = snprintf(path, sizeof(path), "/proc/%d/fd/%d/", getpid(), fd_dir); if (status < 0 || status >= sizeof(path)) exit(EXIT_FAILURE); fd = open(path, O_RDONLY); if (fd < 0) exit(EXIT_FAILURE); close(fd); status = snprintf(path, sizeof(path), "/proc/%d/fd/%d/..", getpid(), fd_dir); if (status < 0 || status >= sizeof(path)) exit(EXIT_FAILURE); fd = open(path, O_RDONLY); if (fd < 0) exit(EXIT_FAILURE); close(fd); status = snprintf(path, sizeof(path), "/proc/%d/fd/%d/..", getpid(), fd_file); if (status < 0 || status >= sizeof(path)) exit(EXIT_FAILURE); fd = open(path, O_RDONLY); if (fd >= 0 || errno != ENOTDIR) exit(EXIT_FAILURE); status = snprintf(path, sizeof(path), "/proc/%d/fd/999999/..", getpid()); if (status < 0 || status >= sizeof(path)) exit(EXIT_FAILURE); fd = open(path, O_RDONLY); if (fd >= 0 || errno != ENOENT) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-6b5a254a.sh0000644000175000017500000000150112156552156016044 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which echo` ] || [ -z `which touch` ] || [ -z `which rm` ]; then exit 125; fi FOO1=/tmp/$(mcookie) FOO2=/tmp/$(mcookie) FOO3=/tmp/$(mcookie) FOO4=/tmp/$(mcookie) echo "content of FOO1" > ${FOO1} echo "content of FOO2" > ${FOO2} ln -s ${FOO1} ${FOO3} # FOO3 -> FOO1 ln -s ${FOO2} ${FOO4} # FOO4 -> FOO2 ${PROOT} -b ${FOO3}:${FOO4} cat ${FOO2} | grep '^content of FOO1$' ${PROOT} -b ${FOO4}:${FOO3} cat ${FOO1} | grep '^content of FOO2$' ${PROOT} -b ${FOO3}:${FOO4}! cat ${FOO2} | grep '^content of FOO2$' ${PROOT} -b ${FOO4}:${FOO3}! cat ${FOO1} | grep '^content of FOO1$' ${PROOT} -v -1 -b ${FOO1} -b ${FOO3} cat ${FOO1} | grep '^content of FOO1$' ${PROOT} -v -1 -b ${FOO1} -b ${FOO2}:/tmp/../${FOO1} cat ${FOO1} | grep '^content of FOO2$' rm -f ${FOO1} ${FOO2} ${FOO3} proot-3.0.2/tests/test-8e5fa256.sh0000644000175000017500000000434412156552156016070 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readlink ] || [ ! -x ${ROOTFS}/bin/symlink ] || [ -z `which mcookie` ] || [ -z `which rm` ] || [ -z `which ln` ] || [ -z `which mkdir` ]; then exit 125; fi LINK_NAME1=`mcookie` LINK_NAME2=`mcookie` rm -f /tmp/${LINK_NAME1} rm -f /tmp/${LINK_NAME2} mkdir -p ${ROOTFS}/tmp ln -s /tmp/ced-host /tmp/${LINK_NAME1} ln -s /tmp/ced-guest ${ROOTFS}/tmp/${LINK_NAME1} ${PROOT} ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ ${PROOT} -b /tmp ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-host$ ${PROOT} -b /tmp:/foo ${ROOTFS} readlink /foo/${LINK_NAME1} | grep ^/foo/ced-host$ ${PROOT} -b /tmp:/foo ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ ${PROOT} -b /:/host-rootfs ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ ${PROOT} -b /:/host-rootfs -b /tmp:/foo ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ # Always use the deepest binding, deepest from the host point-of-view. ${PROOT} -b /:/host-rootfs ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ ${PROOT} -b /:/host-rootfs -b /tmp ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-host$ ${PROOT} -b /:/host-rootfs -b /tmp:/foo ${ROOTFS} readlink /foo/${LINK_NAME1} | grep ^/foo/ced-host$ ${PROOT} -b /:/host-rootfs -b /tmp ${ROOTFS} readlink /host-rootfs/tmp/${LINK_NAME1} | grep ^/tmp/ced-host$ ${PROOT} -b /:/host-rootfs -b /tmp:/foo ${ROOTFS} readlink /host-rootfs/tmp/${LINK_NAME1} | grep ^/foo/ced-host$ rm /tmp/${LINK_NAME1} rm ${ROOTFS}/tmp/${LINK_NAME1} ${PROOT} -b /:/host-rootfs -b /tmp -w /bin ${ROOTFS} symlink /bin/bar /bin/${LINK_NAME1} ${PROOT} -b /:/host-rootfs -b /tmp -w /bin ${ROOTFS} readlink ${LINK_NAME1} | grep ^/bin/bar$ rm ${ROOTFS}/bin/${LINK_NAME1} ${PROOT} -b /:/host-rootfs -b /tmp -w /tmp ${ROOTFS} symlink /bin/bar /tmp/${LINK_NAME1} ${PROOT} -b /:/host-rootfs -b /tmp -w /tmp ${ROOTFS} readlink ${LINK_NAME1} | grep ^/bin/bar$ ${PROOT} -b /:/host-rootfs -b /tmp:/foo -w /foo ${ROOTFS} symlink /foo/bar /foo/${LINK_NAME2} ${PROOT} -b /:/host-rootfs -b /tmp:/foo -w /foo ${ROOTFS} readlink ${LINK_NAME2} | grep ^/foo/bar$ ${PROOT} -b /:/host-rootfs -b /tmp -w /host-rootfs/tmp ${ROOTFS} readlink ${LINK_NAME2} | grep ^/foo/bar$ rm /tmp/${LINK_NAME2} rm /tmp/${LINK_NAME1} proot-3.0.2/tests/test-82ba4ba1.sh0000644000175000017500000000046412156552156016126 0ustar ivoireivoireif [ -z `which id` ] || [ -z `which grep` ] || [ -z `which chown` ] || [ -z `which chroot` ] || [ ! /bin/true ] ; then exit 125; fi ${PROOT} -0 / id -u | grep ^0$ ${PROOT} -0 / id -g | grep ^0$ ${PROOT} -0 / chown root.root /root ${PROOT} -0 / chroot / /bin/true ${PROOT} -0 / chroot /tmp/.. /bin/true proot-3.0.2/tests/test-22222222.sh0000644000175000017500000000121412156552156015613 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readlink ] || [ -z `which mcookie` ] || [ -z `which touch` ] || [ -z `which mkdir` ] || [ -z `which ln` ] || [ -z `which grep` ] || [ -z `which rm` ]; then exit 125; fi REGULAR=`mcookie` SYMLINK=`mcookie` touch /tmp/${REGULAR} ln -fs /tmp/${REGULAR} /tmp/${SYMLINK} mkdir -p ${ROOTFS}/tmp touch ${ROOTFS}/tmp/${REGULAR} ln -fs /tmp/${REGULAR} ${ROOTFS}/tmp/${SYMLINK} ${PROOT} -b /tmp:/ced ${ROOTFS} /bin/readlink /tmp/${SYMLINK} | grep ^/tmp/${REGULAR}$ ${PROOT} -b /tmp:/ced ${ROOTFS} /bin/readlink /ced/${SYMLINK} | grep ^/ced/${REGULAR}$ rm -f /tmp/${REGULAR} rm -f /tmp/${SYMLINK} rm -f ${ROOTFS}/tmp/${REGULAR} proot-3.0.2/tests/test-b161bc0a.sh0000644000175000017500000000017612156552156016121 0ustar ivoireivoireif [ -z `which pwd` ] || [ -z `which grep` ]; then exit 125; fi ${PROOT} -w /tmp/a -m /etc:/tmp/a pwd | grep '^/tmp/a$' proot-3.0.2/tests/test-7601199b.sh0000644000175000017500000000017512156552156015723 0ustar ivoireivoireif [ ! -x /bin/sh ] || [ -z `which grep` ]; then exit 125; fi ${PROOT} -w /tmp / /bin/sh -c 'echo $PWD' | grep '^/tmp$' proot-3.0.2/tests/test-44444444.c0000644000175000017500000000053112156552156015444 0ustar ivoireivoire#include #include #include #include int main(void) { char buffer[2 * PATH_MAX]; if (!getcwd(buffer, sizeof(buffer))) { perror("getcwd"); exit(EXIT_FAILURE); } if (readlink("/bin/abs-true", buffer, sizeof(buffer)) < 0) { perror("readlink"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } proot-3.0.2/tests/cat.c0000644000175000017500000000112412156552156014316 0ustar ivoireivoire#include #include #include #include #include #include #include int main(int argc, char *argv[]) { int status; int fd; int i; for (i = 1; i < argc; i++) { char buffer[1024]; fd = open(argv[i], O_RDONLY); if (fd < 0) { perror("open(2)"); exit(EXIT_FAILURE); } while ((status = read(fd, buffer, sizeof(buffer))) > 0 && write(1, buffer, status) == status) errno = 0; if (errno != 0) { perror("read(2)/write(2)"); exit(EXIT_FAILURE); } (void) close(fd); } exit(EXIT_SUCCESS); } proot-3.0.2/tests/chdir_getcwd.c0000644000175000017500000000104712156552156016201 0ustar ivoireivoire#include #include #include #include #include #include #include int main(int argc, char *argv[]) { char path[PATH_MAX]; int status; int fd; if (argc < 2) { fprintf(stderr, "missing argument\n"); exit(EXIT_FAILURE); } status = chdir(argv[1]); if (status < 0) { perror("chdir()"); exit(EXIT_FAILURE); } if (getcwd(path, PATH_MAX) == NULL) { perror("getcwd()"); exit(EXIT_FAILURE); } printf("%s\n", get_current_dir_name()); exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-a8e69d6f.c0000644000175000017500000000076712156552156015774 0ustar ivoireivoire#include /* syscall(2), */ #include /* perror(3), fprintf(3), */ #include /* exit(3), */ #include /* SYS_lstat, */ #include /* struct stat, */ int main(void) { struct stat stat; int status; status = syscall(SYS_lstat, "/proc/self/cwd/", &stat); if (status < 0) { perror("lstat()"); exit(EXIT_FAILURE); } if (S_ISLNK(stat.st_mode)) { fprintf(stderr, "trailing '/' ignored\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-1cd9d8f9.sh0000644000175000017500000000015412156552156016151 0ustar ivoireivoireif ! `which pwd` -P || [ -z `which grep` ] ; then exit 125; fi ${PROOT} -w /tmp pwd -P | grep '^/tmp$' proot-3.0.2/tests/test-03969e70.sh0000644000175000017500000000057512156552156015733 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/true ] || [ -z `which env` ]; then exit 125; fi ! ${PROOT} -r ${ROOTFS} /true [ $? -eq 0 ] ! ${PROOT} -r ${ROOTFS} ./true [ $? -eq 0 ] ! env PATH='' ${PROOT} -r ${ROOTFS} true [ $? -eq 0 ] env PATH='' ${PROOT} -r ${ROOTFS} -w /bin true ! env --unset PATH ${PROOT} -r ${ROOTFS} true [ $? -eq 0 ] env --unset PATH ${PROOT} -r ${ROOTFS} -w /bin true proot-3.0.2/tests/test-f7089d4f.sh0000644000175000017500000000023112156552156016065 0ustar ivoireivoireif [ -z `which timeout` ] || [ -z `which msgmerge` ] || [ ! -e /dev/null ]; then exit 125; fi timeout 5s ${PROOT} / msgmerge -q /dev/null /dev/null proot-3.0.2/tests/test-230f47cg.sh0000644000175000017500000000176412156552156016065 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/cat ] || [ -z `which mcookie` ] || [ -z `which echo` ] || [ -z `which cp` ] || [ -z `which grep` ]|| [ -z `which rm` ]; then exit 125; fi ! ${PROOT} ${PROOT_RAW} /bin/true if [ $? -eq 0 ]; then exit 125; fi FOO1=/tmp/$(mcookie) FOO2=/tmp/$(mcookie) ROOTFS2=/$(mcookie) FOO3=/tmp/$(mcookie) mkdir -p ${ROOTFS}/tmp mkdir -p ${ROOTFS}/${ROOTFS2}/bin cp ${ROOTFS}/bin/cat ${ROOTFS}/${ROOTFS2}/bin/cat echo "content of foo1" > ${FOO1} echo "content of foo2" > ${FOO2} echo "content of foo3" > ${ROOTFS}/${FOO3} CMD="${PROOT} -r ${ROOTFS} \ -b ${FOO2} \ -b ${FOO1}:${ROOTFS2}/${FOO1} \ -b ${FOO2}:${ROOTFS2}/${FOO2} \ -b ${PROOT_RAW} \ ${PROOT_RAW} -r ${ROOTFS2} \ -b /:/host-rootfs \ -b ${FOO3}:${FOO2} \ -v -1" ${CMD} cat /${FOO1} | grep '^content of foo1$' ${CMD} cat /host-rootfs/${FOO2} | grep '^content of foo2$' ${CMD} cat /${FOO2} | grep '^content of foo3$' rm -fr ${FOO1} rm -fr ${FOO2} rm -fr ${ROOTFS2} rm -fr ${ROOTFS}/${FOO3} proot-3.0.2/tests/pwd.c0000644000175000017500000000061312156552156014343 0ustar ivoireivoire#include /* syscall(2), */ #include /* perror(3), */ #include /* PATH_MAX, */ #include /* exit(3), */ #include /* SYS_getcwd, */ int main(void) { char path[PATH_MAX]; int status; status = syscall(SYS_getcwd, path, PATH_MAX); if (status < 0) { perror("getcwd()"); exit(EXIT_FAILURE); } puts(path); exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-5467b986.sh0000644000175000017500000000342712156552156015742 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which grep` ] || [ ! -x ${ROOTFS}/bin/readlink ] || [ ! -x ${ROOTFS}/bin/chdir_getcwd ] || [ ! -x ${ROOTFS}/bin/fchdir_getcwd ]; then exit 125; fi DOES_NOT_EXIST=/$(mcookie) ${PROOT} -v -1 -b /proc -w ${DOES_NOT_EXIST} ${ROOTFS} readlink /proc/self/cwd | grep '^/$' ${PROOT} -v -1 -w /a -b /tmp:/a -b /tmp:/b ${ROOTFS} pwd | grep '^/a$' ${PROOT} -v -1 -w /a -b /tmp:/b -b /tmp:/a ${ROOTFS} pwd | grep '^/a$' ${PROOT} -v -1 -w /b -b /tmp:/a -b /tmp:/b ${ROOTFS} pwd | grep '^/b$' ${PROOT} -v -1 -w /b -b /tmp:/b -b /tmp:/a ${ROOTFS} pwd | grep '^/b$' ${PROOT} -v -1 -b /tmp:/a -b /tmp:/b ${ROOTFS} chdir_getcwd /a | grep '^/a$' ${PROOT} -v -1 -b /tmp:/b -b /tmp:/a ${ROOTFS} chdir_getcwd /a | grep '^/a$' ${PROOT} -v -1 -b /tmp:/a -b /tmp:/b ${ROOTFS} chdir_getcwd /b | grep '^/b$' ${PROOT} -v -1 -b /tmp:/b -b /tmp:/a ${ROOTFS} chdir_getcwd /b | grep '^/b$' ${PROOT} -v -1 -b /tmp:/a -b /tmp:/b ${ROOTFS} fchdir_getcwd /a | grep '^/[ab]$' ${PROOT} -v -1 -b /tmp:/b -b /tmp:/a ${ROOTFS} fchdir_getcwd /a | grep '^/[ab]$' ${PROOT} -v -1 -b /tmp:/a -b /tmp:/b ${ROOTFS} fchdir_getcwd /b | grep '^/[ab]$' ${PROOT} -v -1 -b /tmp:/b -b /tmp:/a ${ROOTFS} fchdir_getcwd /b | grep '^/[ab]$' ! ${PROOT} ${ROOTFS} chdir_getcwd /bin/true [ $? -eq 0 ] ! ${PROOT} ${ROOTFS} fchdir_getcwd /bin/true [ $? -eq 0 ] ! ${PROOT} -w /bin ${ROOTFS} chdir_getcwd true [ $? -eq 0 ] ! ${PROOT} -w /bin ${ROOTFS} fchdir_getcwd true [ $? -eq 0 ] ${PROOT} -v -1 -w /usr -r / ${ROOTFS}/bin/chdir_getcwd share | grep '^/usr/share$' ${PROOT} -v -1 -w /usr -r / ${ROOTFS}/bin/fchdir_getcwd share | grep '^/usr/share$' (cd /; ${PROOT} -v -1 -w usr -r / ${ROOTFS}/bin/chdir_getcwd share | grep '^/usr/share$') (cd /; ${PROOT} -v -1 -w usr -r / ${ROOTFS}/bin/fchdir_getcwd share | grep '^/usr/share$') proot-3.0.2/tests/test-654decce.sh0000644000175000017500000000717012156552156016225 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readdir ] || [ ! -x ${ROOTFS}/bin/cat ] || [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which chmod` ] || [ -z `which grep` ] || [ -z `which rm` ] || [ -z `which id` ]; then exit 125; fi if [ `id -u` == 0 ]; then exit 125; fi TMP1=/tmp/$(mcookie) TMP2=/tmp/$(mcookie) TMP3=$(mcookie) TMP4=$(mcookie) echo "content of ${TMP1}" > ${TMP1} mkdir -p ${ROOTFS}/${TMP2} chmod -rw ${ROOTFS}/${TMP2} export LANG=C ! ${PROOT} -v -1 -r ${ROOTFS} -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} readdir ${TMP2} | grep '^opendir(3): Permission denied$' ${PROOT} -v -1 -r ${ROOTFS} -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} readdir ${TMP2}/${TMP3} | grep "DT_REG ${TMP4}" ${PROOT} -v -1 -r ${ROOTFS} -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} cat ${TMP2}/${TMP3}/${TMP4} | grep "^content of ${TMP1}$" ${PROOT} -v -1 -r ${ROOTFS} -b /tmp:${TMP2}/${TMP3}/${TMP4} readdir ${TMP2}/${TMP3} | grep "DT_DIR ${TMP4}" # TODO ${PROOT} -v -1 -r ${ROOTFS} -b /tmp:${TMP2}/${TMP3}/${TMP4} readdir /tmp | grep "DT_DIR ${TMP2}" # TODO ${PROOT} -v -1 -r ${ROOTFS} -b /tmp:/${TMP4} readdir / | grep "DT_REG ${TMP4}" ${PROOT} -v -1 -r ${ROOTFS} -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} -b /etc/fstab:${TMP2}/${TMP3}/motd readdir ${TMP2}/${TMP3} | grep "DT_REG ${TMP4}" ${PROOT} -v -1 -r ${ROOTFS} -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} -b /etc/fstab:${TMP2}/${TMP3}/motd readdir ${TMP2}/${TMP3} | grep "DT_REG motd" ${PROOT} -v -1 -r ${ROOTFS} -b ${TMP1}:${TMP2}/${TMP3}/${TMP4}/motd -b /etc/fstab:${TMP2}/${TMP3}/motd cat ${TMP2}/${TMP3}/${TMP4}/motd | grep "^content of ${TMP1}$" ${PROOT} -v -1 -r ${ROOTFS} -b /etc/fstab:${TMP2}/${TMP3}/motd -b ${TMP1}:${TMP2}/${TMP3}/${TMP4}/motd cat ${TMP2}/${TMP3}/${TMP4}/motd | grep "^content of ${TMP1}$" ! chmod +rw ${ROOTFS}/${TMP2} rm -fr ${ROOTFS}/${TMP2} mkdir -p ${TMP2} chmod -rw ${TMP2} export LANG=C ! ${PROOT} -v -1 -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} ${ROOTFS}/bin/readdir ${TMP2} | grep '^opendir(3): Permission denied$' ${PROOT} -v -1 -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} ${ROOTFS}/bin/readdir ${TMP2}/${TMP3} | grep "DT_REG ${TMP4}" ${PROOT} -v -1 -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} ${ROOTFS}/bin/cat ${TMP2}/${TMP3}/${TMP4} | grep "^content of ${TMP1}$" ${PROOT} -v -1 -b /tmp:${TMP2}/${TMP3}/${TMP4} ${ROOTFS}/bin/readdir ${TMP2}/${TMP3} | grep "DT_DIR ${TMP4}" # TODO ${PROOT} -v -1 -b /tmp:${TMP2}/${TMP3}/${TMP4} readdir /tmp | grep "DT_DIR ${TMP2}" # TODO ${PROOT} -v -1 -b /tmp:/${TMP4} readdir / | grep "DT_REG ${TMP4}" ${PROOT} -v -1 -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} -b /etc/fstab:${TMP2}/${TMP3}/motd ${ROOTFS}/bin/readdir ${TMP2}/${TMP3} | grep "DT_REG ${TMP4}" ${PROOT} -v -1 -b ${TMP1}:${TMP2}/${TMP3}/${TMP4} -b /etc/fstab:${TMP2}/${TMP3}/motd ${ROOTFS}/bin/readdir ${TMP2}/${TMP3} | grep "DT_REG motd" ${PROOT} -v -1 -b ${TMP1}:${TMP2}/${TMP3}/${TMP4}/motd -b /etc/fstab:${TMP2}/${TMP3}/motd ${ROOTFS}/bin/cat ${TMP2}/${TMP3}/${TMP4}/motd | grep "^content of ${TMP1}$" ${PROOT} -v -1 -b /etc/fstab:${TMP2}/${TMP3}/motd -b ${TMP1}:${TMP2}/${TMP3}/${TMP4}/motd ${ROOTFS}/bin/cat ${TMP2}/${TMP3}/${TMP4}/motd | grep "^content of ${TMP1}$" ${PROOT} -b /bin:/this1/does/not/exist -b /tmp:/this2/does/not/exist ${ROOTFS}/bin/readdir /this1/ ${PROOT} -b /bin:/this1/does/not/exist -b /tmp:/this2/does/not/exist ${ROOTFS}/bin/readdir /this2/ ${PROOT} -b /tmp:/this1/does/not/exist -b /bin:/this2/does/not/exist ${ROOTFS}/bin/readdir /this1/ ${PROOT} -b /tmp:/this1/does/not/exist -b /bin:/this2/does/not/exist ${ROOTFS}/bin/readdir /this2/ ! chmod +rw ${TMP1} ${TMP2} rm -fr ${TMP1} ${TMP2} proot-3.0.2/tests/test-1c68c218.c0000644000175000017500000000070412156552156015606 0ustar ivoireivoire#include #include #include #include int main() { int status; char *path; path = tmpnam(NULL); status = symlink(path, path); if (status < 0) exit(EXIT_FAILURE); status = fchownat(AT_FDCWD, path, getuid(), getgid(), 0); if (status >= 0) exit(EXIT_FAILURE); status = fchownat(AT_FDCWD, path, getuid(), getgid(), AT_SYMLINK_NOFOLLOW); if (status < 0) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } proot-3.0.2/tests/true.c0000644000175000017500000000003512156552156014526 0ustar ivoireivoireint main(void) { return 0; } proot-3.0.2/tests/readdir.c0000644000175000017500000000171612156552156015170 0ustar ivoireivoire#include #include #include #include #include int main(int argc, char *argv[]) { struct dirent *dirents; DIR *dir; int status; int i; for (i = 1; i < argc; i++) { dir = opendir(argv[i]); if (dir == NULL) { perror("opendir(3)"); exit(EXIT_FAILURE); } errno = 0; while ((dirents = readdir(dir)) != NULL) { printf("%s %s\n", dirents->d_type == DT_BLK ? "DT_BLK " : dirents->d_type == DT_CHR ? "DT_CHR " : dirents->d_type == DT_DIR ? "DT_DIR " : dirents->d_type == DT_FIFO ? "DT_FIFO" : dirents->d_type == DT_LNK ? "DT_LNK " : dirents->d_type == DT_REG ? "DT_REG " : dirents->d_type == DT_SOCK ? "DT_SOCK" : "DT_UNKNOWN", dirents->d_name); errno = 0; } if (errno != 0) { perror("readdir(3)"); exit(EXIT_FAILURE); } status = closedir(dir); if (status < 0) { perror("closedir(3)"); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-230f47ch.sh0000644000175000017500000000127012156552156016056 0ustar ivoireivoireif [ -z `which id` ] || [ -z `which uname` ] || [ -z `which grep` ]; then exit 125; fi ! ${PROOT} ${PROOT_RAW} /bin/true if [ $? -eq 0 ]; then exit 125; fi export PROOT_NO_SECCOMP=1 ${PROOT} ${PROOT_RAW} -0 id -u | grep ^0$ ${PROOT} ${PROOT_RAW} -k 3.33.333 uname -r | grep ^3\.33\.333$ ${PROOT} -0 ${PROOT_RAW} id -u | grep ^0$ ${PROOT} -k 3.33.333 ${PROOT_RAW} uname -r | grep ^3\.33\.333$ ${PROOT} -0 ${PROOT_RAW} -k 3.33.333 id -u | grep ^0$ ${PROOT} -0 ${PROOT_RAW} -k 3.33.333 uname -r | grep ^3\.33\.333$ ${PROOT} -k 3.33.333 ${PROOT_RAW} -0 id -u | grep ^0$ ${PROOT} -k 3.33.333 ${PROOT_RAW} -0 uname -r | grep ^3\.33\.333$ proot-3.0.2/tests/test-11111111.sh0000644000175000017500000000267312156552156015615 0ustar ivoireivoire#!/bin/bash if [ -z `which cat` ] || [ -z `which readlink` ] || [ -z `which mcookie` ] || [ -z `which touch` ] || [ -z `which mkdir` ] || [ -z `which ln` ] || [ -z `which grep` ] || [ -z `which rm` ]; then exit 125; fi set +e x1="r1 d1 rl1 dl1" # root of the test tree. x2="r2 d2 rl2 dl2" # subtree of d1/dl1, every components exist. x3="r3 d3 rl3 dl3" # subtree of d1/dl1, no component exists. x4="/ /. /.." # terminators. generate () { output=${1} make_tests () { for c in ${x4} ""; do x="${1}${c}" $(cd ${x} 2>/dev/null); cd_result=$? cat ${x} 2>/dev/null; cat_result=$? readlink ${x} 2>/dev/null 1>&2; readlink_result=$? echo "${x}, $cd_result, $cat_result, $readlink_result" >> $output done } echo "path, chdir, cat, readlink" > $output for a in ${x1}; do for b in ${x2}; do make_tests "${a}/${b}" done for b in ${x3}; do make_tests "${a}/${b}" done done } if [ -z ${PROOT_STAGE2} ]; then create_components () { touch r${1} 2>/dev/null mkdir -p d${1} 2>/dev/null ln -fs r${1} rl${1} 2>/dev/null ln -fs d${1} dl${1} 2>/dev/null } create_components 1 $(cd d1; create_components 2) REF=/tmp/`mcookie` mkdir -p /tmp generate $REF env PROOT_STAGE2=$REF ${PROOT} -w ${PWD} / sh ./$0 exit $? fi TMP=/tmp/`mcookie` mkdir -p /tmp generate $TMP set -e cmp $TMP $PROOT_STAGE2 rm $TMP $PROOT_STAGE2 proot-3.0.2/tests/test-230f47cf.sh0000644000175000017500000000016612156552156016057 0ustar ivoireivoire! ${PROOT} ${PROOT_RAW} /bin/true if [ $? -eq 0 ]; then exit 125; fi echo exit | ${PROOT} -v 0 ${PROOT_RAW} -v 0 proot-3.0.2/tests/test-cccccccc.sh0000644000175000017500000000035312156552156016426 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which rmdir` ] || [ -z `which mkdir` ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir ${TMP} ! ${PROOT} rmdir ${TMP}/. [ $? -eq 0 ] ! ${PROOT} rmdir ${TMP}/./ [ $? -eq 0 ] ${PROOT} rmdir ${TMP} proot-3.0.2/tests/test-a4d7ed70.sh0000644000175000017500000000045012156552156016134 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which ln` ] || [ -z `which ls` ] || [ -z `which rm` ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir ${TMP} ln -s /proc/self/fd ${TMP}/fd ln -s ${TMP}/fd/0 ${TMP}/stdin ${PROOT} \ls ${TMP}/stdin | grep ^${TMP}/stdin$ rm -fr ${TMP} proot-3.0.2/tests/test-cea75343.sh0000644000175000017500000000174112156552156016057 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which cat` ] || [ -z `which grep` ] || [ -z `which rm` ]; then exit 125; fi TMP1=/tmp/`mcookie` TMP2=/tmp/`mcookie` TMP3=/tmp/`mcookie` # /a/b/c # /a/b # /a/d # /a echo 'binding 1' > ${TMP1} echo 'binding 2' > ${TMP2} mkdir -p ${TMP3}/a/b BINDINGS="-b ${TMP1}:${TMP3}/a/b/c -b ${TMP3}/a/b -b ${TMP2}:${TMP3}/a/d -b ${TMP3}/a" ${PROOT} ${BINDINGS} / cat ${TMP3}/a/b/c | grep '^binding 1$' BINDINGS="-b ${TMP3}/a -b ${TMP2}:${TMP3}/a/d -b ${TMP3}/a/b -b ${TMP1}:${TMP3}/a/b/c" ${PROOT} ${BINDINGS} / cat ${TMP3}/a/d | grep '^binding 2$' mkdir -p ${TMP3}/c/b # /c/b/a # /c/b # /c/d # /c BINDINGS="-b ${TMP1}:${TMP3}/c/b/a -b ${TMP3}/c/b -b ${TMP2}:${TMP3}/c/d -b ${TMP3}/c" ${PROOT} ${BINDINGS} / cat ${TMP3}/c/b/a | grep '^binding 1$' BINDINGS="-b ${TMP3}/c -b ${TMP2}:${TMP3}/c/d -b ${TMP3}/c/b -b ${TMP1}:${TMP3}/c/b/a" ${PROOT} ${BINDINGS} / cat ${TMP3}/c/d | grep '^binding 2$' rm ${TMP1} rm ${TMP2} rm -fr ${TMP3} proot-3.0.2/tests/test-67972fbe.sh0000644000175000017500000000065012156552156016072 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/readdir ] || [ ! -e /bin/true ] || [ -z `which mkdir` ] || [ -z `which ln` ] || [ -z `which rm` ] || [ -z `which grep` ] || [ -z `which mcookie` ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir -p ${ROOTFS}/${TMP}/run/dbus mkdir -p ${ROOTFS}/${TMP}/var ln -s ../run ${ROOTFS}/${TMP}/var/run ${PROOT} -b /bin:${TMP}/var/run/dbus ${ROOTFS} readdir ${TMP}/var/run/dbus/ | grep true rm -fr ${TMP} proot-3.0.2/tests/test-33333333.c0000644000175000017500000000065412156552156015442 0ustar ivoireivoire/* Check a child is traced even if its parent doesn't call wait(2). * * Reported-by: Clément BAZIN * on Ubuntu 11.10 x86_64 */ #include /* exit(3), */ #include /* fork(2), */ int main(void) { switch (fork()) { case -1: exit(EXIT_FAILURE); case 0: /* Child: XXX */ sleep(2); return 0; default: /* Parent: "look child, no wait(2)!" */ return 1; } } proot-3.0.2/tests/test-cb1143ab.sh0000644000175000017500000000255012156552156016120 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which ln` ] || [ -z `which ls` ]; then exit 125; fi D1=`mcookie` D2=`mcookie` LINK=`mcookie` F=`mcookie` TMP=/tmp/${D1}/${D2} mkdir -p ${TMP} ln -s ${TMP}/./. ${TMP}/${LINK} ${PROOT} / \ls ${TMP}/${LINK} | grep ^${LINK}$ ${PROOT} / \ls ${TMP}/${LINK}/ | grep ^${LINK}$ ${PROOT} / \ls ${TMP}/${LINK}/. | grep ^${LINK}$ ${PROOT} / \ls ${TMP}/${LINK}/.. | grep ^${D2}$ ${PROOT} / \ls ${TMP}/${LINK}/./.. | grep ^${D2}$ rm ${TMP}/${LINK} touch ${TMP}/${F} ln -s ${TMP}/${F} ${TMP}/${LINK} ${PROOT} \ls ${TMP}/${LINK} ! ${PROOT} \ls ${TMP}/${LINK}/ [ $? -eq 0 ] ! ${PROOT} \ls ${TMP}/${LINK}/. [ $? -eq 0 ] ! ${PROOT} \ls ${TMP}/${LINK}/.. [ $? -eq 0 ] ! ${PROOT} \ls ${TMP}/${LINK}/./.. [ $? -eq 0 ] ! ${PROOT} \ls ${TMP}/${LINK}/../.. [ $? -eq 0 ] ${PROOT} -b /tmp/${D1}:${TMP}/${F} \ls ${TMP}/${LINK} ${PROOT} -b /tmp/${D1}:${TMP}/${F} \ls ${TMP}/${LINK}/ ${PROOT} -b /tmp/${D1}:${TMP}/${F} \ls ${TMP}/${LINK}/. ${PROOT} -b /tmp/${D1}:${TMP}/${F} \ls ${TMP}/${LINK}/.. rm ${TMP}/${LINK} ln -s ${TMP}/${D1} ${TMP}/${LINK} ${PROOT} -b /tmp/${F}:${TMP}/${D1} \ls ${TMP}/${LINK} ! ${PROOT} -b /tmp/${F}:${TMP}/${D1} \ls ${TMP}/${LINK}/ [ $? -eq 0 ] ! ${PROOT} -b /tmp/${F}:${TMP}/${D1} \ls ${TMP}/${LINK}/. [ $? -eq 0 ] ! ${PROOT} -b /tmp/${F}:${TMP}/${D1} \ls ${TMP}/${LINK}/.. [ $? -eq 0 ] rm -fr /tmp/${D1} proot-3.0.2/tests/argv0.c0000644000175000017500000000012312156552156014564 0ustar ivoireivoire#include int main(int argc, char **argv) { puts(argv[0]); return 0; } proot-3.0.2/tests/readlink.c0000644000175000017500000000104612156552156015343 0ustar ivoireivoire#include /* syscall(2), */ #include /* perror(3), fprintf(3), */ #include /* PATH_MAX, */ #include /* exit(3), */ #include /* SYS_readlink, */ int main(int argc, char *argv[]) { char path[PATH_MAX]; int status; if (argc != 2) { fprintf(stderr, "usage: readlink FILE\n"); exit(EXIT_FAILURE); } status = syscall(SYS_readlink, argv[1], path, PATH_MAX); if (status < 0) { perror("readlink()"); exit(EXIT_FAILURE); } path[status] = '\0'; puts(path); exit(EXIT_SUCCESS); } proot-3.0.2/tests/GNUmakefile0000644000175000017500000000547212156552156015467 0ustar ivoireivoireDIR = $(dir $(abspath $(firstword $(MAKEFILE_LIST)))) ROOTFS = $(DIR)/rootfs PROOT = $(DIR)/../src/proot CC = gcc PROOT_RAW = $(PROOT) CHECK_TESTS = $(patsubst %,check-%, $(wildcard test-*.sh) $(wildcard test-*.c)) .PHONY: check clean_failure check_failure setup check-% check: | clean_failure check_failure memcheck: PROOT_RAW := $(PROOT) memcheck: PROOT := $(shell which valgrind) -q --error-exitcode=1 $(PROOT) memcheck: check clean_failure: @rm -f failure check_failure: $(CHECK_TESTS) @bash -c '! test -e failure' check-%.sh: %.sh setup $(Q)env PROOT_RAW="$(PROOT_RAW)" PROOT="$(PROOT)" ROOTFS=$(ROOTFS) sh -ex $< $(silently); $(call check,$*) check-%.c: $(ROOTFS)/bin/% setup $(call check_c,$*,$(PROOT) -b /proc $(ROOTFS) /bin/$*) # Special cases. check-test-bdc90417.c: test-bdc90417 $(call check_c,$<,$(PROOT) -w . / $<) check-test-af062114.c: test-af062114 $(call check_c,$<,$(PROOT) -v -1 -q /bin/true -b . -b /lib -b /lib64 -b /proc $(ROOTFS) ./$< | grep -- --inhibit-rpath) $(call check_c,$<,$(PROOT) -v -1 -q /bin/true / ./$< | grep '^./$<') check-test-5bed7141.c: test-5bed7141 $(call check_c,$<,$(PROOT) $<) check-test-16573e73.c: test-16573e73 $(call check_c,$<,$(PROOT) ./$<) $(call check_c,$<,$(PROOT) ./$< 1) check_c = $(Q)if [ -e $< ]; then \ $(2) $(silently); $(call check,$(1)) \ else \ echo " CHECK $(1) skipped"; \ fi check = case "$$?" in \ 0) echo " CHECK $(1) ok";; \ 125) echo " CHECK $(1) skipped";; \ *) echo " CHECK $(1) FAILED"; \ touch failure ;; \ esac ###################################################################### # Build a clean rootfs setup: $(ROOTFS)/bin/true $(ROOTFS)/bin/false \ $(ROOTFS)/bin/pwd $(ROOTFS)/bin/readlink $(ROOTFS)/bin/symlink \ $(ROOTFS)/bin/abs-true $(ROOTFS)/bin/rel-true $(ROOTFS)/bin/echo \ $(ROOTFS)/bin/argv0 $(ROOTFS)/bin/readdir $(ROOTFS)/bin/cat $(ROOTFS)/tmp \ $(ROOTFS)/bin/chdir_getcwd $(ROOTFS)/bin/fchdir_getcwd $(ROOTFS)/tmp: @mkdir $@ $(ROOTFS)/bin/abs-true: @ln -fs /bin/true $@ $(ROOTFS)/bin/rel-true: @ln -fs ./true $@ .SECONDARY: $(patsubst %.c,$(ROOTFS)/bin/%, $(wildcard test-*.c)) $(ROOTFS)/bin/%: %.c @test -e $(dir $@) || mkdir -p $(dir $@) $(Q)$(CC) -static $*.c -o $@ $(silently) || true # Special cases. test-bdc90417: test-bdc90417.c $(Q)$(CC) $< -o $@ $(silently) || true test-af062114: test-af062114.c $(Q)$(CC) $< -Wl,-rpath=foo -o $@ $(silently) || true test-5bed7141: test-5bed7141.c $(Q)$(CC) $< -pthread -static -o $@ $(silently) || true test-16573e73: test-16573e73.c $(Q)$(CC) $< -static -o $@ $(silently) || true ###################################################################### # Beautified output V = 0 ifeq ($(V), 0) quiet = quiet_ Q = @ silently = >/dev/null 2>&1 else quiet = Q = silently = endif proot-3.0.2/tests/test-dfb0c3b6.sh0000644000175000017500000000151312156552156016207 0ustar ivoireivoireif [ -z `which sh` ] || [ -z `which readlink` ] || [ -z `which grep` ] || [ -z `which echo` ] || [ -z `which mcookie` ] || [ ! -e /proc/self/fd/0 ]; then exit 125; fi ${PROOT} / readlink /proc/self | grep -E "^[[:digit:]]+$" ! ${PROOT} / readlink /proc/self/.. [ $? -eq 0 ] ${PROOT} / readlink /proc/self/../self | grep -E "^[[:digit:]]+$" ${PROOT} / sh -c 'echo "OK" | readlink /proc/self/fd/0' | grep -E "^pipe:\[[[:digit:]]+\]$" ! ${PROOT} / sh -c 'echo "OK" | readlink /proc/self/fd/0/' [ $? -eq 0 ] ! ${PROOT} / sh -c 'echo "OK" | readlink /proc/self/fd/0/..' [ $? -eq 0 ] ! ${PROOT} / sh -c 'echo "OK" | readlink /proc/self/fd/0/../0' [ $? -eq 0 ] ${PROOT} / sh -c 'echo "echo OK" | sh /proc/self/fd/0' | grep ^OK$ TMP=/tmp/$(mcookie) ${PROOT} / sh -c "exec 6<>${TMP}; readlink /proc/self/fd/6" | grep ^${TMP} rm -f ${TMP} proot-3.0.2/tests/test-6fb08ce1.sh0000644000175000017500000000034212156552156016133 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which grep` ] || [ -z `which rm` ]; then exit 125; fi TMP=/tmp/$(mcookie) echo "OK" > ${TMP} ${PROOT} -b ${TMP}:/etc/fstab -b /dev/null -b /etc / cat /etc/fstab | grep ^OK$ rm ${TMP} proot-3.0.2/tests/test-gggggggg.sh0000644000175000017500000000066412156552156016473 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which true` ] || [ -z `which mkdir` ] || [ -z `which rm` ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir ${TMP} env PROOT_DONT_POLLUTE_ROOTFS=1 ${PROOT} -b /bin:${TMP}/do/not/create -b /bin:${TMP}/dont/create true test ! -e ${TMP}/do test ! -e ${TMP}/dont ${PROOT} -b /bin:${TMP}/do/create -b /bin:${TMP}/dont/create true test -e ${TMP}/do test -e ${TMP}/dont chmod +rx -R ${TMP} rm -fr ${TMP} proot-3.0.2/tests/test-e87b34ae.c0000644000175000017500000000120312156552156015744 0ustar ivoireivoire#include /* syscall(2), fork(2), usleep(3), */ #include /* perror(3), printf(3), */ #include /* PATH_MAX, */ #include /* exit(3), */ #include /* SYS_readlink, SYS_getcwd, */ #include /* errno, */ int main(void) { pid_t pid; int status; int i; for (i = 0; i < 1000; i++) { pid = fork(); switch (pid) { case -1: /* Is the maximum number of processes * reached? */ if (errno == EAGAIN) break; perror("fork()"); exit(EXIT_FAILURE); case 0: /* child */ exit(EXIT_SUCCESS); default: /* parent */ break; } } exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-bbbbbbbb.sh0000644000175000017500000000111012156552156016406 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which rm` ] || [ -z `which ln` ]; then exit 125; fi DONT_EXIST=$(mcookie) TMP1=$(mcookie) TMP2=$(mcookie) rm -f /tmp/${DONT_EXIST} ${PROOT} ln -sf /${DONT_EXIST} /tmp/ ${PROOT} ln -sf /${DONT_EXIST} /tmp/ rm -f /tmp/${DONT_EXIST} ${PROOT} ln -sf /etc/fstab/${DONT_EXIST} /tmp/ ! ${PROOT} ln -sf /etc/fstab/${DONT_EXIST} /tmp/ rm -f /tmp/${DONT_EXIST} rm -f /tmp/${TMP1} /tmp/${TMP2} touch /tmp/${TMP2} ln -sf /tmp/${DONT_EXIST} /tmp/${TMP1} ! ${PROOT} ln /tmp/${TMP2} /tmp/${TMP1} rm -f /tmp/${TMP1} /tmp/${TMP2} rm -f /tmp/${DONT_EXIST} proot-3.0.2/tests/echo.c0000644000175000017500000000017212156552156014467 0ustar ivoireivoire#include int main(int argc, char **argv) { int i; for (i = 1; i < argc; i++) puts(argv[i]); return 0; } proot-3.0.2/tests/test-305ae31d.sh0000644000175000017500000000030712156552156016043 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which ln` ] || [ -z `which true` ] || [ -z `which rm` ]; then exit 125; fi TMP=$(mcookie) ln -s /proc/self/mounts ${TMP} ${PROOT} -b ${TMP} true rm ${TMP} proot-3.0.2/tests/fchdir_getcwd.c0000644000175000017500000000122712156552156016347 0ustar ivoireivoire#define _GNU_SOURCE #include #include #include #include #include #include #include int main(int argc, char *argv[]) { char path[PATH_MAX]; int status; int fd; if (argc < 2) { fprintf(stderr, "missing argument\n"); exit(EXIT_FAILURE); } fd = open(argv[1], O_DIRECTORY); if (fd < 0) { perror("open()"); exit(EXIT_FAILURE); } status = fchdir(fd); if (status < 0) { perror("fchdir()"); exit(EXIT_FAILURE); } if (getcwd(path, PATH_MAX) == NULL) { perror("getcwd()"); exit(EXIT_FAILURE); } printf("%s\n", get_current_dir_name()); exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-55b731d3.sh0000644000175000017500000000007412156552156015774 0ustar ivoireivoireif ! `which pwd` -P; then exit 125; fi ${PROOT} pwd -P proot-3.0.2/tests/test-nnnnnnnn.c0000644000175000017500000000325112156552156016366 0ustar ivoireivoire#include #include #include #include #include #include #include #include #include int main() { const char *sockname = "/test-nnnnnnnn-socket"; struct sockaddr_un sockaddr; socklen_t socklen; mode_t mask; int status; int fd; /* root can create $hostfs/test-nnnnnnnn-socket. */ if (getuid() == 0) return 125; /* clean-up previous socket. */ (void) unlink(sockname); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); exit(EXIT_FAILURE); } bzero(&sockaddr, sizeof(sockaddr)); sockaddr.sun_family = AF_UNIX; strcpy(sockaddr.sun_path, sockname); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); status = bind(fd, (const struct sockaddr *) &sockaddr, SUN_LEN(&sockaddr)); if (status < 0) { perror("bind"); exit(EXIT_FAILURE); } umask(mask); status = listen(fd, 50); if (status < 0) { perror("listen"); exit(EXIT_FAILURE); } bzero(&sockaddr, sizeof(sockaddr)); socklen = sizeof(sockaddr); status = getsockname(fd, (struct sockaddr *) &sockaddr, &socklen); if (status < 0) { perror("getsockname"); exit(EXIT_FAILURE); } if (socklen != SUN_LEN(&sockaddr) + 1) { fprintf(stderr, "socklen: %d != %d + 1\n", socklen, SUN_LEN(&sockaddr)); exit(EXIT_FAILURE); } if (sockaddr.sun_family != AF_UNIX) { fprintf(stderr, "! AF_UNIX\n"); exit(EXIT_FAILURE); } if (socklen == sizeof(sockaddr) + 1) status = strncmp(sockaddr.sun_path, sockname, sizeof(sockaddr.sun_path)); else status = strcmp(sockaddr.sun_path, sockname); if (status != 0) { fprintf(stderr, "! %s\n", sockname); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } proot-3.0.2/tests/test-00000000.sh0000644000175000017500000000012412156552156015572 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/true ]; then exit 125; fi ${PROOT} ${ROOTFS} /bin/true proot-3.0.2/tests/test-0cf405b0.c0000644000175000017500000000043212156552156015650 0ustar ivoireivoire#include /* execlp(2), */ #include /* exit(3), */ #include /* strcmp(3), */ int main(int argc, char *argv[]) { if (argc == 0) //strcmp(argv[0], "/proc/self/exe") == 0) exit(EXIT_SUCCESS); execlp("/proc/self/exe", NULL); exit(EXIT_FAILURE); } proot-3.0.2/tests/test-6d1e2650.sh0000644000175000017500000000023412156552156015771 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/true ] || [ -z `which env` ]; then exit 125; fi ! env PATH=/nib ${PROOT} ${ROOTFS} true env PATH=/bin ${PROOT} ${ROOTFS} true proot-3.0.2/tests/test-1743dd3d.sh0000644000175000017500000000060412156552156016053 0ustar ivoireivoireif [ ! -x ${ROOTFS}/bin/true ] || [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which echo` ] || [ -z `which chmod` ]; then exit 125; fi TMP=/tmp/`mcookie` rm -f ${ROOTFS}/${TMP} mkdir -p ${ROOTFS}/tmp echo '#!/bin/true' > ${ROOTFS}/${TMP} chmod -x ${ROOTFS}/${TMP} ! ${PROOT} ${ROOTFS} ${TMP} chmod +x ${ROOTFS}/${TMP} ${PROOT} ${ROOTFS} ${TMP} rm -f ${ROOTFS}/${TMP} proot-3.0.2/tests/test-c10e2073.c0000644000175000017500000000160312156552156015572 0ustar ivoireivoire#include /* syscall(2), */ #include /* perror(3), fprintf(3), */ #include /* PATH_MAX, */ #include /* exit(3), */ #include /* strlen(3), */ #include /* SYS_readlink, SYS_getcwd, */ int main(void) { char path[PATH_MAX]; int status; status = syscall(SYS_readlink, "/proc/self/cwd", path, PATH_MAX); if (status < 0) { perror("readlink()"); exit(EXIT_FAILURE); } path[status] = '\0'; if (status != strlen(path)) { fprintf(stderr, "readlink() returned the wrong size %d != %z.\n", status, strlen(path)); exit(EXIT_FAILURE); } status = syscall(SYS_getcwd, path, PATH_MAX); if (status < 0) { perror("getcwd()"); exit(EXIT_FAILURE); } if (status != strlen(path) + 1) { fprintf(stderr, "getcwd() returned the wrong size %d != %z.\n", status, strlen(path)); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } proot-3.0.2/tests/tests-16573e73.c0000644000175000017500000000101112156552156015706 0ustar ivoireivoire#include #include #include #include int main(int argc, char **ignored) { char *const argv[] = { "true", NULL}; char *const envp[] = { NULL }; pid_t pid; int status; pid = (argc <= 1 ? vfork() : fork()); switch (pid) { case -1: exit(EXIT_FAILURE); case 0: /* child */ exit(execve("/bin/true", argv, envp)); default: /* parent */ if (wait(&status) < 0 || !WIFEXITED(status)) exit(EXIT_FAILURE); exit(WEXITSTATUS(status)); } exit(EXIT_FAILURE); } proot-3.0.2/tests/test-dddddddd.sh0000644000175000017500000000107412156552156016437 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which rm` ] || [ -z `which ln` ] || [ -z `which rmdir` ] || [ -z `which env` ]; then exit 125; fi TMP="/tmp/$(mcookie)" TMP2="/tmp/$(mcookie)" export LAND=C ln -s /bin ${TMP} ! rmdir ${TMP} > ${TMP}.ref 2>&1 ! ${PROOT} -v -1 rmdir ${TMP} > ${TMP}.res 2>&1 cmp ${TMP}.ref ${TMP}.res ln -s /this/does/not/exist ${TMP2} ! mkdir ${TMP2} > ${TMP2}.ref 2>&1 ! ${PROOT} -v -1 mkdir ${TMP2} > ${TMP2}.res 2>&1 cmp ${TMP2}.ref ${TMP2}.res rm -f ${TMP} rm -f ${TMP}.ref rm -f ${TMP}.res rm -f ${TMP2} rm -f ${TMP2}.ref rm -f ${TMP2}.res proot-3.0.2/tests/test-3624be91.sh0000644000175000017500000000030112156552156015767 0ustar ivoireivoireif [ -z `which sh` ] || [ -z `which kill` ] || [ -z `which grep` ] || [ -z `which cut` ]; then exit 125; fi ${PROOT} / sh -c 'kill -15 $(grep TracerPid /proc/self/status | cut -f 2 -d :)' proot-3.0.2/tests/test-c15999f9.sh0000644000175000017500000000041612156552156016021 0ustar ivoireivoireif [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which stat` ] || [ -z `which grep` ] || [ -z `which rm` ]; then exit 125; fi TMP=/tmp/$(mcookie) mkdir ${TMP} ${PROOT} -b /bin/true:${TMP}/true /bin/true stat -c %a ${TMP}/true | grep '^0$' rm -fr ${TMP} proot-3.0.2/tests/test-77777777.c.unreliable0000644000175000017500000000177212156552156017625 0ustar ivoireivoire#include #include #include #include #include #include int main() { int child_status; int status; pid_t pid; pid = fork(); switch (pid) { case -1: perror("fork()"); exit(EXIT_FAILURE); case 0: /* child */ status = raise(SIGSTOP); if (status != 0) { perror("raise(SIGSTOP)"); exit(EXIT_FAILURE); } sleep(1); exit(EXIT_FAILURE); default: /* parent */ status = waitpid(pid, &child_status, WUNTRACED); if (status < 0) { perror("waitpid()"); exit(EXIT_FAILURE); } if (WIFEXITED(child_status)) printf("exited, status=%d\n", WEXITSTATUS(child_status)); else if (WIFSIGNALED(child_status)) printf("killed by signal %d\n", WTERMSIG(child_status)); else if (WIFSTOPPED(child_status)) printf("stopped by signal %d\n", WSTOPSIG(child_status)); else if (WIFCONTINUED(child_status)) printf("continued\n"); if (WIFSTOPPED(child_status)) exit(EXIT_SUCCESS); else exit(EXIT_FAILURE); } } proot-3.0.2/tests/test-51943658.c0000644000175000017500000000240512156552156015457 0ustar ivoireivoire#include /* syscall(2), */ #include /* perror(3), fprintf(3), */ #include /* PATH_MAX, */ #include /* exit(3), */ #include /* openat(2), */ int main(void) { int dir_fd; int dir_fd1; int dir_fd2; ssize_t status; char path1[PATH_MAX]; char path2[PATH_MAX]; char fd_link[64]; /* Format the path to the "virtual" link. */ dir_fd = open("/", O_RDONLY); if (dir_fd < 0) { perror("open(2)"); exit(EXIT_FAILURE); } dir_fd1 = openat(dir_fd, ".", O_RDONLY); if (dir_fd1 < 0) { perror("openat(2)"); exit(EXIT_FAILURE); } dir_fd2 = openat(dir_fd, "..", O_RDONLY); if (dir_fd2 < 0) { perror("openat(2)"); exit(EXIT_FAILURE); } sprintf(fd_link, "/proc/self/fd/%d", dir_fd1); status = readlink(fd_link, path1, PATH_MAX - 1); if (status < 0) { perror("readlink(2)"); exit(EXIT_FAILURE); } path1[status] = '\0'; sprintf(fd_link, "/proc/self/fd/%d", dir_fd2); status = readlink(fd_link, path2, PATH_MAX - 1); if (status < 0) { perror("readlink(2)"); exit(EXIT_FAILURE); } path2[status] = '\0'; if (strcmp(path1, "/") != 0) { fprintf(stderr, "/. != /"); exit(EXIT_FAILURE); } if (strcmp(path2, "/") != 0) { fprintf(stderr, "/.. != /"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } proot-3.0.2/doc/0000755000175000017500000000000012156552156013010 5ustar ivoireivoireproot-3.0.2/doc/changelog.txt0000644000175000017500000005324412156552156015510 0ustar ivoireivoireRelease v3.0.2 ============== * Fix the search of the initial command: when the initial command is a symbolic link, PRoot has to dereference it in guest namespace, not in the host one. * Return error code EACCESS instead of EISDIR when trying to execute a directory. Some programs, such as "env", behave differently with respect to this error code. For example: ### setup $ mkdir -p /tmp/foo/python $ export PATH=/tmp/foo:$PATH ### before (PRoot v2.3 ... v3.0.1) before$ proot env python env: python: Is a directory ### now (PRoot v3.0.2 ...) $ proot env python Python 2.7.5 (default, May 29 2013, 02:28:51) [GCC 4.8.0] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> Release v3.0.1 ============== Fix support for bindings where the guest path is explicitly not dereferenced. Be careful, the syntax has changed: before$ proot -b /bin/bash:!/bin/sh now$ proot -b /bin/bash:/bin/sh! Release v3.0 ============ New features ------------ * PRoot can now use the kernel feature named "seccomp-filter", a.k.a "seccomp mode 2", to improve its own performance significantly. For examples on my workstation, the tables below show the time overhead induced by PRoot compared to a native execution: - when generating the Perl 5.16.1 package: =============== =========== ========== command seccomp off seccomp on =============== =========== ========== ./configure.gnu 75% 25% make -j4 70% 45% make -j4 check 25% 9% =============== =========== ========== - when generating the Coreutils 8.19 package: =============== =========== ========== command seccomp off seccomp on =============== =========== ========== ./configure 80% 33% make -j4 75% 33% make -j4 check 80% 8% =============== =========== ========== * It is now possible to explicitly not dereference the guest location of a binding by specifying ``!`` as the first character. For instance:: proot -b /bin/bash:!/bin/sh will not overlay ``/bin/dash`` when this latter is pointed to by ``/bin/sh`` (it's typically the case on Ubuntu and Debian). Fix --- * The initial command is not search in $PATH anymore when it starts with ``/`` or ``./``, and it doesn't exist. For instance:: $ rm test $ proot ./test proot warning: './test not found (root = /, cwd = /usr/local/cedric/git/proot) proot error: see `proot --help` or `man proot`. Thanks ------ Many thanks to Will Drewry and Indan Zupancic, who made possible to accelerate PTRACE_SYSCALL with seccomp-filter. Also, thanks to Paul Moore for his valuable set of seccomp tools. Notes ----- * Unlike what I said, this release is not shipped with a ptrace emulator. It's planned for the next one, though. * Seccomp-filter was first introduced in Linux 3.5 a year ago, it was also officially back-ported to Ubuntu 12.04 (Linux 3.2). To know if PRoot is actually using this accelerator on your system, check the verbose output. For instance:: $ proot -v 1 true ... proot info: ptrace acceleration (seccomp mode 2) enabled ... But first, be sure it was built with this support:: $ proot -V ... built-in accelerators: process_vm = yes, seccomp_filter = yes ... Release v2.4.1 ============== Fixes ----- * Fix all warnings reported by GCC-4.8 "-Wall -Wextra" and Coverity Prevent 4.5. * Fix Unix sockets path translation for some x86_64 systems. * Make the "kompat" extension (-k option) work again. * Fix spurious "can't delete /tmp/proot-$PID-XXXXX" messages. Release v2.4 ============ New architectures ----------------- * PRoot now works natively on Linux ARM64 systems (a.k.a AArch64). Note that PRoot/AArch64 doesn't support 32-bit binaries yet. * PRoot/x86_64 now supports x32 binaries/rootfs. Fixes ----- * Paths from Unix domain sockets are now translated. For example, it wasn't possible previously to use "tmux" in the guest rootfs if another instance were running in the host rootfs. * When a host path is bound to a nonexistent guest path, PRoot tries to create this latter in the guest rootfs, for some technical reasons. Previously, this "dummy" guest path was created with RWX permissions but this might cause troubles when re-using the rootfs for other purpose. Now, this "dummy" guest path is created with minimal permissions, and it is also possible to avoid its creation by defining the PROOT_DONT_POLLUTE_ROOTFS environment variable. Command-line interface changes ------------------------------ * The directory "/run" is removed from the list of recommended bindings (-B option) because this creates to much conflicts with programs that write in the "/run/var" directory. * The -0 option now makes user's files appear as if they were actually owned by root, and it also fakes the success of any mode changes (chmod* syscalls). This is typically useful to create packages where the files belong to the root user (it's almost always the case). Internal changes ---------------- * PRoot should be even more portable now. For instance, there's no need to worry about syscallee-saved registers anymore. Thanks ------ This release was made possible thanks to, in no special order: Yvan Roux, Jerôme Audu, Heehooman, Yann Droneaud, and James Le Cuirot. See "git log" for details. Release v2.3.1 ============== New feature ----------- * The "fake id0" feature was improved by Rémi Duraffort in order to support privileged write operations in read-only files/directories. Some package managers (Fedora, Debian, ...) relies on this special behavior:: # ls -ld /usr/lib dr-xr-xr-x 22 root root 40960 Jan 2 11:19 /usr/lib/ # install -v something.so /usr/lib/ removed ‘/usr/lib/something.so‘ ‘something.so‘ -> ‘/usr/lib/something.so‘ Fixes ----- * Fix bindings to a guest path that contains a symbolic link. For example when the given guest path ``/var/run/dbus`` is a symbolic link to ``/run/dbus``. * Fix a memory corruption when accessing files in "/proc/self/" Special thanks to Rémi Duraffort for the improved "fake id0" feature and for the bug reports. Release v2.3 ============ This release is intended more specifically to developers and advanced users, it was mostly driven by the requirements of an internal STMicroelectronics project named "Auto-Tuning Optimization Service". New features ------------ * There's now an extension mechanism in PRoot that allows developers to add their own features and/or to use PRoot as a Linux process instrumentation engine. The two following old features were moved to this new extension interface: "-k *string*" and "-0" (respectively: set the kernel release and compatibility level to *string*"; and force some syscalls to behave as if executed by "root"). * It is now possible to execute PRoot under PRoot, well somewhat. Actually the initial instance of PRoot detects that it is being called again and recomputes the configuration for the new process tree. This feature is still experimental and was way harder to implement than expected, however it was worth the effort since it enforced the consistency in PRoot. Just one example among many, in PRoot the "chroot" feature is now really equivalent to the "mount/bind" one, that is, ``chroot path/to/rootfs`` is similar to ``mount --bind path/to/rootfs /``. * The "current working directory" (chdir(2), getcwd(2), ...) is now fully emulated by PRoot. Sadly a minor regression was introduced: even if the current working directory has been removed, getcwd(2) returns a "correct" value. This should be fixed in the next release. Command-line interface changes ------------------------------ * The message "proot info: started/exited" isn't printed by default anymore since it might introduce noise when PRoot is used inside a test-suite that compares outputs. This message was initially added to know whether the guest program has exited immediately. * The "-u" and "-W" options have disappeared. The former wasn't really useful and the latter was definitely useless since the default "current working directory" is "." since v2.1, that means the three examples below are equivalent ("-W" was just an alias to "-b . -w ."):: proot -b . [...] proot -b . -w . [...] proot -W [...] Fixes ----- * The option ``-w .`` is now really equivalent to ``-w $PWD``. * A bug almost impossible to describe here has been fixed, it appeared only when specifying relative bindings, for instance: ``-b .``. Internal changes ---------------- * PRoot now relies on Talloc: a hierarchical, reference counted memory pool system with destructors. It is the core memory allocator used in Samba: http://talloc.samba.org. This is definitely a worthwhile dependency for the sake of development scalability and debuggability. For example, PRoot now has an explicit garbage collector (c.f. ``tracee->ctx``), and the full dynamic memory hierarchy can be printed by sending the USR1 signal to PRoot:: native-shell$ proot --mount=$HOME --mount=/proc --rootfs=./slackware-14/ prooted-shell$ kill -s USR1 $(grep Tracer /proc/self/status | cut -f 2) Tracee 0x6150c0 768 bytes 0 ref' (pid = 22495) talloc_new: ./tracee/tracee.c:97 0x615420 0 bytes 0 ref' $exe 0x61bef0 10 bytes 0 ref' ("/bin/bash") @cmdline 0x61bf60 16 bytes 0 ref' ("/bin/sh", ) /bin/sh 0x61bfd0 8 bytes 0 ref' $glue 0x61bae0 24 bytes 0 ref' ("/tmp/proot-22494-UfGAPh") FileSystemNameSpace 0x615480 32 bytes 0 ref' $cwd 0x61b880 13 bytes 0 ref' ("/home/cedric") Bindings 0x61b970 16 bytes 0 ref' (host) Binding 0x615570 8280 bytes 1 ref' (/home/cedric:/home/cedric) Binding 0x6176a0 8280 bytes 1 ref' (/proc:/proc) Binding 0x6197d0 8280 bytes 1 ref' (/usr/local/proot/slackware-14:/) Bindings 0x61b900 16 bytes 0 ref' (guest) Binding -> 0x6176a0 Binding -> 0x615570 Binding -> 0x6197d0 Release v2.2 ============ * This release brings some critical fixes so an upgrade is highly recommended, especially on x86_64 and Ubuntu. * PRoot is now a lot faster: the speed-up can be up to 50% depending on the kind of application. * PRoot can now mount/bind files anywhere in the guest rootfs, even if the mount point has no parent directory (and/or can't be created). With previous versions of PRoot, that would created kinda black hole in the filesystem hierarchy that might bug some programs like "cpio" or "rpm". For example, with the previous version of PRoot:: $ proot -b /etc/motd:/black/holes/and/revelations proot warning: can't create the guest path (binding) ... proot info: started $ find /black find: `/black: No such file or directory $ cat /black/holes/and/revelations Time has come to make things right -- Matthew Bellamy And now:: $ proot -b /etc/motd:/black/holes/and/revelations proot info: started $ find /black /black /black/holes /black/holes/and /black/holes/and/revelations $ cat /black/holes/and/revelations Time has come to make things right -- Matthew Bellamy * "/run" was added to the list of recommended bindings (-B/-Q). * SH4 and ARM architectures are now officially supported. Thanks ------ Huge thanks to Rémi DURAFFORT for all the tests, bug reports, fixes, and for hosting http://proot.me. Thanks to Thomas P. HIGDON for the advanced investigation on a really tricky bug (red zone corruption). Release v2.1 ============ New features ------------ * PRoot can now emulate some of the syscalls that are available in the kernel release specified by -k but that are missing in the host kernel. This allows the execution of guest programs expecting a kernel newer than the actual one, if you encountered the famous "FATAL: kernel too old" or "qemu: Unsupported syscall" messages. * The current working directory isn't changed anymore if it is still accessible in the guest environment (binding). Fixes ----- * Added support for architectures with no misalignment support (SH4). * Fix support for: link(2), linkat(2), symlink(2), and symlinkat(2). Release v2.0.1 ============== * Fix a compatibility issue with QEMU v1.1 * Fix the initialization of bindings that point to "/proc/self". These problems were reported by Alkino: https://github.com/cedric-vincent/PRoot/issues/3 Release v2.0 ============ New features ------------ * There's now a specific support to handle special symlinks in /proc. As of now, only the following ones are correctly handled: * /proc/self, it was already supported previously but now this is done consistently (i.e. not a hack); * /proc//exe, for any monitored by PRoot; and * /proc//fd/. * The list of supported syscalls was updated, as of Linux 3.4.1. Command-line interface changes ------------------------------ * The path to the guest rootfs can now be specified by the new -r/--rootfs option. This permits the use of shell aliases, for example: $ alias armedslack='proot -Q qemu-arm -r /path/to/armedslack' $ armedslack -v 1 -b ~/arm_cpuinfo:/proc/cpuinfo That wasn't possible previously because the path to the guest rootfs had to be the last option. * The -v/--verbose option now takes a parameter, and a negative integer makes PRoot quiet except on fatal errors. * The -h/--help option now prints a detailed message. * The -V/--version and -h/--help options now exit with success. Fix --- * Return correct errno if a non-final path component isn't a directory nor a symlink. * Fix the insertion of an item in the list of host/guest bindings. Internal changes ---------------- This section is dedicated to PRoot developers. * File-system information is now inherited from a traced process to its children. This permits the emulation of symlinks in /proc/self: cmdline, exe, cwd, root, ... * The execution of QEMU is now fully confined to the virtual rootfs namespace: it now relies on the "mixed-execution" feature, just like a regular host program. Release v1.9 ============ Fixes ----- * Be as transparent as possible with respect to SIGSTOP and SIGTRAP signals. For instance, the Open POSIX Test Suite now reports the same level of success whether it is run under PRoot or not (it depends on the kernel version though). * Ignore terminating signals and kill all tracees on abnormal termination signals (^\, segmentation fault, divide by zero, ...). This ensures no tracee will stay alive without being monitored anymore. * Force utsname.machine to "i686" -- instead of "i386" -- for 32-bit programs running on x86_64 systems. This improves the compatibility with package managers that deduce the current architecture from `uname -m`. * Fix x86_64 support for linkat() and fchownat(). * Fix mixed-execution support, LD_LIBRARY_PATH was defined twice for host programs. Release v1.8.4 ============== New feature ----------- * The -0 option now fakes success on ``chroot("/")``. This feature is required by some guest package managers, like ``pacman`` -- the Arch Linux Package Manager. Fix --- * Nested bindings are now correctly supported. For example with these bindings -- nested from the host point-of-view:: host$ proot -b /:/host-rootfs -b /tmp ... guest$ ln -s /tmp/bar /tmp/foo # ... points to "/tmp/bar" instead of "/host-rootfs/tmp/bar" and with these bindings -- nested from the guest point-of-view:: host$ proot -b /bin -b /usr/bin/find:/bin/find ... guest$ /bin/find # ... works instead of "no such file or directory" Internal changes ---------------- This section is dedicated to PRoot developers. * Functions to compare two pathes (equal, prefix, not comparable, ...) are now available, at last. * The "ignore ELF interpreter" option can be (dis|en)able with the ``PROOT_IGNORE_ELF_INTERPRETER`` environment variable and/or with the ``config.ignore_elf_interpreter`` internal variable. Release v1.8.3 ============== New features ------------ * The -0 option now fakes success on ownership changes. This improves the compatibility with package managers that abort if ``chown(2)`` fails. Note that this is quite limited compared to ``fakeroot``. * Force utsname.machine to "i386" for 32-bit programs running on x86_64 systems. This improves the compatibility with package managers that deduce the current architecture from `uname -m`. Fixes ----- * Fix a regression regarding the concatenation of the ``..`` with a path ending with ``.``. For intance you can now do ``ls foo`` where ``foo`` is a symbolic link to ``/bar/.``. * Don't return an error if the specified size for ``getcwd(2)`` and ``readlink(2)`` is greater than PATH_MAX. Technically the result may likely be shorter than this limit. Release v1.8.2 ============== * This is the first public release of PRoot, it's time to increase its maturity artificially ... Actually it's an homage to Blink 182 ;) * User manual finally published. * PRoot can now *mix* the execution of host programs and the execution of guest programs emulated by QEMU. This is useful to use programs that aren't available initially in the guest environment and to speed up build-time by using cross-compilation tools or any CPU independent program, like interpreters. * Absolute symlinks from bound directories that point to any bound directory are kept consistent: for example, given the host symlink ``/bin/sh -> /bin/bash``, and given the command-line option ``-b /bin:/foo``, the symlink will appeared as ``/foo/sh -> /foo/bash``. * Three command-line options are gone: * ``-p`` (don't block the ptrace syscall) wasn't really useful. * ``-e`` (don't use the ELF interpreter) isn't required anymore. * ``-a`` (disable the ASLR mechanism) is now the default. * Don't complain anymore when parent directories of a *recommended binding* (as enabled by ``-B``, ``-M`` and ``-Q`` options) can't be created. * Support job control under ptrace as introduced in Linux 3.0+. * ``LD_`` environment variables are now passed to the QEMUlated program, not to QEMU itself. It means ``ldd`` works (there's a bug in QEMU/ARM though). * Many fixes and improved compatibility thanks to the Open Build Service instantiated at http://build.opensuse.com * Note: v0.7.1 was an experimental release. Release v0.7.0 ============== * Search the guest program in $PATH relatively to the guest rootfs, for instance you can now just write:: proot /path/to/guest/rootfs/ perl instead of:: proot /path/to/guest/rootfs/ /usr/bin/perl * The command-line interface was re-written from scratch, the only incompatible change is that QEMU options are now separated by spaces:: proot -Q "qemu-arm -g 1234" ... instead of:: proot -Q qemu-arm,-g,1234 ... * New option "-0": force syscalls "get*id" to report identity 0, aka "root". * Many fixes, code refactoring, new testing framework, ... Special thanks to Claire ROBINE for her contribution. Release v0.6.2 ============== * Change the default command from $SHELL to "/bin/sh". The previous behaviour led to an unexpected error -- from user's point-of-view -- when $SHELL didn't exit in the new root file-system. * Fix *partially* support for readlink(2) when mirror pathes are in use. Prior this patch, any symbolic link -- that points to an absolute path which prefix is equal to the host-side of any mirror path -- was bugged. For instance, the command "proot -m /bin:/host $ROOTFS /usr/bin/readlink /usr/bin/ps" returned "/host" instead of "/bin/ps". * Add the option "-V" to print the version then exit. * Be more explicit when a wrong command-line argument is used. * Remove the SIGSEGV help message: it was too confusing to the user. * Use a new shining build-system (again :D). Special thanks go to those contributors: Yves JANIN, Remi Duraffort and Christophe GUILLON. Release v0.6.1 ============== * Add `/tmp` to the list of mirrored paths when using -M. * Fix the ELF interpreter extraction. * Rewrite the build system. Release v0.6 ============ New features ------------ * Added support for "asymmetric" path mirrors. The command-line option for mirrors was extended to support the syntax "-m :" where is the location of the mirror within the alternate rootfs and is the path to the real directory/file. For instance you can now mirror the whole host rootfs in the directory "/hostfs" within the alternate rootfs that way:: proot -m /:/hostfs ... * Added an option to disable ASLR (Address Space Layout Randomization). RHEL4 and Ubuntu 10.04 use an ASLR mechanism that creates conflicts between the layout of QEMU and the layout of the target program. This new option is automatically set when QEMU is used. * Added "/etc/resolv.conf" and $HOME to the list of mirrored paths when using the option -M or -Q. Fixes ----- * Fixed the detranslation of getcwd(2) and readlink(2). * Improved the build compatibility on old/broken distro. * Added support for pthread cancellation when QEMU is used. * Set QEMU's fake argv[0] to the program actually launched, not to the initial script name. * Create the path up to the mirror location to cheat "walking" programs. proot-3.0.2/doc/stylesheets/0000755000175000017500000000000012156552156015364 5ustar ivoireivoireproot-3.0.2/doc/stylesheets/article-html.txt0000644000175000017500000000026612156552156020516 0ustar ivoireivoire.. raw:: html proot-3.0.2/doc/stylesheets/website.css0000644000175000017500000000477512156552156017555 0ustar ivoireivoire* { padding: 0; margin: 0; color: #333333; line-height: 1.5em; font-family: sans; } html { background-color: #dddddd; } body { background-color: white; border: 1px solid #dddddd; border-radius: 1em; -moz-border-radius: 1em; max-width: 50em; min-width: 25em; margin-left: auto; margin-right: auto; margin-top: 1.5em; margin-bottom: 1.5em; -moz-box-shadow: 0 0 1.5em 0.5em #333333; -webkit-box-shadow: 0 0 1.5em 0.5em #333333; box-shadow: 0 0 1.5em 0.5em #333333; } #title { margin-left: auto; margin-right: auto; text-align: center; } h1 { color: orange; text-align: center; text-shadow: 2px 3px 3px #333333; font-size: 3em; display: inline; } h2 { margin: 1em; margin-bottom: 0.5em; border-bottom: 1px dotted gray; } h3 { margin-left: 1em; margin-top: 1em; } h3 tt { font-style: italic; } #contents { text-align: center; background-color: gray; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; margin-right: -1px; border-right: 1px solid gray; margin-left: -1px; border-left: 1px solid gray; padding-top: 0.5em; padding-bottom: 0.5em; } #contents ul { margin: 0; } #contents li { display: inline; margin-left: 3%; margin-right: 3%; } #contents a { color: white; text-decoration: none; font-weight: bold; border-bottom: none; } #contents a:hover { border-bottom: 2px solid orange; } a { text-decoration: none; border-bottom: 1px solid orange; } p { text-align: justify; margin-left: 2em; margin-right: 2em; margin-top: 1em; margin-bottom: 0.5em; } ol { margin-left: 5em; margin-right: 2em; margin-top: 0.5em; margin-bottom: 1em; } li { margin-bottom: 0.5em; } table { margin-left: 2em; margin-right: 2em; } .footnote { font-size: smaller; } pre { margin: 2em ; margin-top: 0.5em ; margin-bottom: 1em ; padding: 0.5em; background-color: #dddddd; color: black; font-family: monospace; white-space: pre-wrap; border-style: solid; border-width: 1px; border-color: gray; border-radius: 0.5em; -moz-border-radius: 0.5em; } pre:first-line { font-style: italic; } tt { font-family: monospace; } ul { margin-left: 3em; } li p, li pre { margin-left: 0; } @media print { * { background-color: transparent ! important; border: none ! important; } }proot-3.0.2/doc/stylesheets/cli.xsl0000644000175000017500000001100512156552156016660 0ustar ivoireivoire /* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ #ifndef CLI_H #define CLI_H #include <stddef.h> #include "tracee/tracee.h" #include "build.h" typedef struct { const char *name; char separator; const char *value; } Argument; typedef int (*option_handler_t)(Tracee *tracee, char *value); typedef struct { const char *class; option_handler_t handler; const char *description; const char *detail; Argument arguments[5]; } Option; #ifndef VERSION #define VERSION "" #endif static const char *version = VERSION; static Option options[] = { }; #endif /* CLI_H */ static const char * = " "; static const char *synopsis = " "; static const char *colophon = " "; static char *recommended_bindings[] = { NULL, }; " ", { .class = " ", .arguments = { { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_ , .description = " ", .detail = " ", }, * * { .name = " ", .separator = ' ', .value = " " \0', .value = NULL }, static int handle_option_ (Tracee *tracee, char *value); proot-3.0.2/doc/stylesheets/website.xsl0000644000175000017500000001133012156552156017554 0ustar ivoireivoire <xsl:value-of select="document/title" /> — <xsl:value-of select="document/subtitle" />

PRoot

By default XSLTproc converts tags with no content to self-closing tags

Support

Feel free to send your questions, bug reports, suggestions, and patchs to the mailing-list or to the forum, but please be sure that your answer isn't in the user manual first.

Also, Rémi Duraffort has written interesting articles on how to use a foreign Debian rootfs with PRoot in order to build and test VLC on this guest Linux distribution.

      
    
  • proot-3.0.2/doc/stylesheets/rpm-spec.xsl0000644000175000017500000000204612156552156017644 0ustar ivoireivoire %define version v Summary : Version : %{version} Release : 1 License : GPL2+ Group : Applications/System Source : proot-%{version}.tar.gz Buildroot : %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) Prefix : /usr Name : proot Requires: libtalloc BuildRequires: pkgconfig libtalloc-devel glibc-static %description %prep %setup -n proot-%{version} %build make -C src %install make -C src install PREFIX=%{buildroot}/%{prefix} install -D doc/proot.1 %{buildroot}/%{_mandir}/man1/proot.1 %check make -C tests %clean rm -rf %{buildroot} %files %defattr(-,root,root) %{prefix}/bin/proot %doc %{_mandir}/man1/proot.1* %doc COPYING %doc doc/* proot-3.0.2/doc/roadmap.txt0000644000175000017500000001344712156552156015205 0ustar ivoireivoire========= Roadmap ========= v3.1: ptrace emulation ====================== Highlight --------- Currently, it's not possible to execute GDB, strace, or any other program based on "ptrace" under PRoot since this latter is also based on this syscall and the Linux kernel allows only one ptracer per process. The solution is to emulate this syscall in PRoot. New features ------------ * Do not seccomp-trace any syscalls when there are no bindings. This should improve performance of ATOS extensions a alot. * Add a mechanism to add new [fake] entries in /proc, for instance ``/proc/proot/config``. * Add a way to get the reverse translation of a path:: proot [bindings] -x /usr/local/bin or maybe something like that:: proot [bindings] readlink /proc/proot/detranslate/usr/local/bin * Add an option to start a login shell instead of ``/bin/sh``. Fixes ----- * Improve code coverage of PRoot. v3.2: FS cache ============== Highlight --------- One core feature of PRoot is the path translation. This mechanism heavily relies on "stat", sadly this syscall is quite slow on some FS (like NFS). The idea is to cache the results of the path translation mechanism to avoid the use of "stat" as much as possible in order to speed-up PRoot. The internal structure of this FS cache could also be used to emulate the "getdents" syscall in order to add or hide entries. v3.3: custom loader =================== Highlight --------- Currently, PRoot relies on the loader embedded in the GNU dynamic linker, sadly this latter suffers from a couple of bugs and limitations that avoid some programs to run correctly under PRoot (and CARE). For examples: * programs that have a custom heap allocator might crash: a typical example is Bash. The current workaround in PRoot is to limit the stack size to 256MB, which in turn might make other programs crash. * programs that use constructors might crash: a typical example is QEMU. There's a workaround in PRoot for QEMU only. * programs that use "rpath" and "symlinks" might not start: a typical example is Java. There's no workaround in PRoot, the user has to by-pass the symlink manually. * programs that read the processes name are confused: typical examples are ps and top. There's no workaround in PRoot. * programs that expect a kernel newer than the actual one (the infamous "FATAL: kernel too old" error). The current workaround in PRoot (-k option) is not enough. * programs not monitored by PRoot get a wrong program name when reading /proc/$PID/cmdline, like "ld-linux.so.2" All these bugs and limitations can be fixed by writing a custom loader. Not yet scheduled ================= Fixes ----- * Fix ``mkdir foo; cd foo; rmdir ../foo; readlink -e .``. * Fix remaining bugs in sub-reconfiguration support:: ./src/proot -B /usr/local/proot/centos-5-x86 make -C tests/ * Forbid rename/unlink on a mount point: $ mv mount_point elsewhere mv: cannot move "mount_point" to "elsewhere": Device or resource busy * Add support for the string $ORIGIN (or equivalently ${ORIGIN}) in an rpath specification * Add support for /etc/ld.so.preload and /etc/ld.so.conf[.d] in mixed-mode. * Fix ``proot -k 1.2.3 proot -k 2.4.6 -k 3.4.5 uname -r | grep 3.4.5``. * Don't use the same prefix for glued path:: $ proot -b /etc/fstab -b /bin/readdir:/bin/readdir -r /tmp/empty-rootfs [...] proot info: binding = /tmp/proot-6738-CMr1hE:/bin proot info: binding = /tmp/proot-6738-CMr1hE:/etc [...] $ readdir /bin DT_DIR .. DT_DIR . DT_REG readdir DT_REG fstab $ readdir /etc DT_DIR .. DT_DIR . DT_REG readdir DT_REG fstab Features -------- * Make ``mount --bind`` change the tracee's configuration dynamically. * Make ``chroot`` change the tracee's configuration dynamically (not only of ``/``). * Add support for a special environment variable to add paths dynamically to the host LD_LIBRARY_PATH ("EXTRA_HOST_LD_LIBRARY_PATH"). * A host program that launches a guest program shouldn't propagate its host LD_LIBRARY_PATH environment variable. * Add a "blind" mode where: * unreadable executable can be executed:: proot mount: OK (rwsr-xr-x) proot ping: failed (rws--x--x) * unreadable directory can be accessed * Add command-line interface to set environment variables. Rename push_env() in change_env() and enhance it to support the "unset" feature. * Add support for coalesced options, for instance ``proot -eM`` * Allow a per-module verbose level * Emulate chown, chmod, and mknod when -0 (fake_id0) is enabled. Documentation ------------- * Explain bindings aren't exclusive, i.e. "-b /tmp:/foo" doesn't invalidate "-b /tmp:/bar". * Explain why PRoot does not work with setuid programs * Document the compatibility with the old command-line interface (without "-r"):: proot [options] [args...] Misc. ----- * Replace "readlink(XXX, path, PATH_MAX)" with "readlink(XXX, path, PATH_MAX - 1)" * read_string should return -ENAMETOOLONG when length(XXX) >= max_size * Check (in ld.so sources) if more than one RPATH/RUNPATH entry is allowed. * Ensure tracees' clone flags has CLONE_PTRACE & ~CLONE_UNTRACED. * Add a stealth mode where over-the-stack content is restored. * Try Scrashme (syscall fuzzer) against PRoot Performance ----------- * prefetch_mem(): cache write-through memory access (read_string, fetch_args). * Fallback to /proc//mem when process_vm_readv() isn't available. * Add a "multi-process" mode where there's one fork of PRoot per monitored process. Each time a new_tracee structure is created, PRoot forks itself. Be sure that the tracer of this new process really is the new forked PRoot! (Thanks Yves for this comment) proot-3.0.2/doc/proot.spec0000644000175000017500000000251412156552156015031 0ustar ivoireivoire%define version v3.0.2 Summary : chroot, mount --bind, and binfmt_misc without privilege/setup Version : %{version} Release : 1 License : GPL2+ Group : Applications/System Source : proot-%{version}.tar.gz Buildroot : %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) Prefix : /usr Name : proot Requires: libtalloc BuildRequires: pkgconfig libtalloc-devel glibc-static %description PRoot is a user-space implementation of chroot, mount --bind, and binfmt_misc. This means that users don't need any privileges or setup to do things like using an arbitrary directory as the new root filesystem, making files accessible somewhere else in the filesystem hierarchy, or executing programs built for another CPU architecture transparently through QEMU user-mode. Also, developers can add their own features or use PRoot as a Linux process instrumentation engine thanks to its extension mechanism. Technically PRoot relies on ptrace, an unprivileged system-call available in every Linux kernel. %prep %setup -n proot-%{version} %build make -C src %install make -C src install PREFIX=%{buildroot}/%{prefix} install -D doc/proot.1 %{buildroot}/%{_mandir}/man1/proot.1 %check make -C tests %clean rm -rf %{buildroot} %files %defattr(-,root,root) %{prefix}/bin/proot %doc %{_mandir}/man1/proot.1* %doc COPYING %doc doc/* proot-3.0.2/doc/proot.10000644000175000017500000004503512156552156014244 0ustar ivoireivoire.\" Man page generated from reStructuredText. . .TH PROOT 1 "2013-06-14" "3.0.2" "" .SH NAME PRoot \- chroot, mount --bind, and binfmt_misc without privilege/setup . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBproot\fP [\fIoption\fP] ... [\fIcommand\fP] .SH DESCRIPTION .sp PRoot is a user\-space implementation of \fBchroot\fP, \fBmount \-\-bind\fP, and \fBbinfmt_misc\fP. This means that users don\(aqt need any privileges or setup to do things like using an arbitrary directory as the new root filesystem, making files accessible somewhere else in the filesystem hierarchy, or executing programs built for another CPU architecture transparently through QEMU user\-mode. Also, developers can add their own features or use PRoot as a Linux process instrumentation engine thanks to its extension mechanism. Technically PRoot relies on \fBptrace\fP, an unprivileged system\-call available in every Linux kernel. .sp The new root file\-system, a.k.a \fIguest rootfs\fP, typically contains a Linux distribution. By default PRoot confines the execution of programs to the guest rootfs only, however users can use the built\-in \fImount/bind\fP mechanism to access files and directories from the actual root file\-system, a.k.a \fIhost rootfs\fP, just as if they were part of the guest rootfs. .sp When the guest Linux distribution is made for a CPU architecture incompatible with the host one, PRoot uses the CPU emulator QEMU user\-mode to execute transparently guest programs. It\(aqs a convenient way to develop, to build, and to validate any guest Linux packages seamlessly on users\(aq computer, just as if they were in a \fInative\fP guest environment. That way all of the cross\-compilation issues are avoided. .sp PRoot can also \fImix\fP the execution of host programs and the execution of guest programs emulated by QEMU user\-mode. This is useful to use host equivalents of programs that are missing from the guest rootfs and to speed up build\-time by using cross\-compilation tools or CPU\-independent programs, like interpreters. .sp It is worth noting that the guest kernel is never involved, regardless of whether QEMU user\-mode is used or not. Technically, when guest programs perform access to system resources, PRoot translates their requests before sending them to the host kernel. This means that guest programs can use host resources (devices, network, ...) just as if they were "normal" host programs. .SH OPTIONS .sp The command\-line interface is composed of two parts: first PRoot\(aqs options (optional), then the command to launch (\fB/bin/sh\fP if not specified). This section describes the options supported by PRoot, that is, the first part of its command\-line interface. .SS Regular options .INDENT 0.0 .TP .BI \-r \ path, \ \-\-rootfs\fB= path Use \fIpath\fP as the new guest root file\-system, default is \fB/\fP. .sp The specified \fIpath\fP typically contains a Linux distribution where all new programs will be confined. The default rootfs is \fB/\fP when none is specified, this makes sense when the bind mechanism is used to relocate host files and directories, see the \fB\-b\fP option and the \fBExamples\fP section for details. .TP .BI \-b \ path, \ \-\-bind\fB= path, \ \-m \ path, \ \-\-mount\fB= path Make the content of \fIpath\fP accessible in the guest rootfs. .sp This option makes any file or directory of the host rootfs accessible in the confined environment just as if it were part of the guest rootfs. By default the host path is bound to the same path in the guest rootfs but users can specify any other location with the syntax: \fB\-b *host_path*:*guest_location*\fP. If the guest location is a symbolic link, it is dereferenced to ensure the new content is accessible through all the symbolic links that point to the overlaid content. In most cases this default behavior shouldn\(aqt be a problem, although it is possible to explicitly not dereference the guest location by appending it the \fB!\fP character: \fB\-b *host_path*:*guest_location!*\fP. .TP .BI \-q \ command, \ \-\-qemu\fB= command Execute guest programs through QEMU as specified by \fIcommand\fP. .sp Each time a guest program is going to be executed, PRoot inserts the QEMU user\-mode \fIcommand\fP in front of the initial request. That way, guest programs actually run on a virtual guest CPU emulated by QEMU user\-mode. The native execution of host programs is still effective and the whole host rootfs is bound to \fB/host\-rootfs\fP in the guest environment. .sp This option is automatically enabled by the \fB\-Q\fP option. .TP .BI \-w \ path, \ \-\-pwd\fB= path, \ \-\-cwd\fB= path Set the initial working directory to \fIpath\fP. .sp Some programs expect to be launched from a given directory but do not perform any \fBchdir\fP by themselves. This option avoids the need for running a shell and then entering the directory manually. .TP .BI \-v \ value, \ \-\-verbose\fB= value Set the level of debug information to \fIvalue\fP. .sp The higher the integer \fIvalue\fP is, the more detailled debug information is printed to the standard error stream. A negative \fIvalue\fP makes PRoot quiet except on fatal errors. .TP .B \-V, \-\-version, \-\-about Print version, copyright, license and contact, then exit. .TP .B \-h, \-\-help, \-\-usage Print the version and the command\-line usage, then exit. .UNINDENT .SS Extension options .sp The following options enable built\-in extensions. Technically developers can add their own features to PRoot or use it as a Linux process instrumentation engine thanks to its extension mechanism, see the sources for further details. .INDENT 0.0 .TP .BI \-k \ string, \ \-\-kernel\-release\fB= string Set the kernel release and compatibility level to \fIstring\fP. .sp If a program is run on a kernel older than the one expected by its GNU C library, the following error is reported: "FATAL: kernel too old". To be able to run such programs, PRoot can emulate some of the syscalls that are available in the kernel release specified by \fIstring\fP but that are missing in the current kernel. .TP .B \-0, \-\-root\-id Force some syscalls to behave as if executed by "root". .sp Some programs will refuse to work if they are not run with "root" privileges, even if there is no technical reason for that. This is typically the case with package managers. This option allows users to bypass this kind of limitation by faking the user/group identity, and by faking the success of some operations like changing the ownership of files, changing the root directory to \fB/\fP, ... Note that this option is quite limited compared to \fBfakeroot\fP. .UNINDENT .SS Alias options .sp The following options are aliases for handy sets of options. .INDENT 0.0 .TP .B \-B, \-M Alias: \fB\-b\fP for each path of a recommended list .sp There are a couple of bindings that are needed for most guest programs to behave correctly regarding the configuration part of the host computer which is not specific to the host Linux distribution, such as: user/group information, network setup, run\-time information, users\(aq files, ... This highly recommended option enables the following bindings: .INDENT 7.0 .IP \(bu 2 /etc/host.conf .IP \(bu 2 /etc/hosts .IP \(bu 2 /etc/hosts.equiv .IP \(bu 2 /etc/mtab .IP \(bu 2 /etc/netgroup .IP \(bu 2 /etc/networks .IP \(bu 2 /etc/passwd .IP \(bu 2 /etc/group .IP \(bu 2 /etc/nsswitch.conf .IP \(bu 2 /etc/resolv.conf .IP \(bu 2 /etc/localtime .IP \(bu 2 /dev/ .IP \(bu 2 /sys/ .IP \(bu 2 /proc/ .IP \(bu 2 /tmp/ .IP \(bu 2 $HOME .UNINDENT .TP .BI \-Q \ command Alias: \fB\-q *command* \-B\fP .sp This option is highly recommended when using QEMU user\-mode; it enables all the recommended bindings. .UNINDENT .SH EXIT STATUS .sp If an internal error occurs, \fBproot\fP returns a non\-zero exit status, otherwise it returns the exit status of the last terminated program. When an error has occurred, the only way to know if it comes from the last terminated program or from \fBproot\fP itself is to have a look at the error message. .SH FILES .sp PRoot reads links in \fB/proc//fd/\fP to support \fIopenat(2)\fP\-like syscalls made by the guest programs. .SH EXAMPLES .sp In the following examples the directories \fB/mnt/slackware\-8.0\fP and \fB/mnt/armslack\-12.2/\fP contain a Linux distribution respectively made for x86 CPUs and ARM CPUs. .SS \fBchroot\fP equivalent .sp To execute a command inside a given Linux distribution, just give \fBproot\fP the path to the guest rootfs followed by the desired command. The example below executes the program \fBcat\fP to print the content of a file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/slackware\-8.0/ cat /etc/motd Welcome to Slackware Linux 8.0 .ft P .fi .UNINDENT .UNINDENT .sp The default command is \fB/bin/sh\fP when none is specified. Thus the shortest way to confine an interactive shell and all its sub\-programs is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/slackware\-8.0/ $ cat /etc/motd Welcome to Slackware Linux 8.0 .ft P .fi .UNINDENT .UNINDENT .SS \fBmount \-\-bind\fP equivalent .sp The bind mechanism permits to relocate files and directories. This is typically useful to cheat programs that perform access to hard\-coded locations, like some installation scripts: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-b /tmp/alternate_opt:/opt $ cd to/sources $ make install [...] install \-m 755 prog "/opt/bin" [...] # prog is installed in "/tmp/alternate_opt/bin" actually .ft P .fi .UNINDENT .UNINDENT .sp As shown in this example, it is possible to bind over files not even owned by the user. This can be used to \fIoverlay\fP system configuration files, for instance the DNS setting: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ls \-l /etc/hosts \-rw\-r\-\-r\-\- 1 root root 675 Mar 4 2011 /etc/hosts .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-b ~/alternate_hosts:/etc/hosts $ echo \(aq1.2.3.4 google.com\(aq > /etc/hosts $ resolveip google.com IP address of google.com is 1.2.3.4 $ echo \(aq5.6.7.8 google.com\(aq > /etc/hosts $ resolveip google.com IP address of google.com is 5.6.7.8 .ft P .fi .UNINDENT .UNINDENT .sp Another example: on most Linux distributions \fB/bin/sh\fP is a symbolic link to \fB/bin/bash\fP, whereas it points to \fB/bin/dash\fP on Debian and Ubuntu. As a consequence a \fB#!/bin/sh\fP script tested with Bash might not work with Dash. In this case, the binding mechanism of PRoot can be used to set non\-disruptively \fB/bin/bash\fP as the default \fB/bin/sh\fP on these two Linux distributions: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-b /bin/bash:/bin/sh [...] .ft P .fi .UNINDENT .UNINDENT .sp Because \fB/bin/sh\fP is initially a symbolic link to \fB/bin/dash\fP, the content of \fB/bin/bash\fP is actually bound over this latter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-b /bin/bash:/bin/sh $ md5sum /bin/sh 089ed56cd74e63f461bef0fdfc2d159a /bin/sh $ md5sum /bin/bash 089ed56cd74e63f461bef0fdfc2d159a /bin/bash $ md5sum /bin/dash 089ed56cd74e63f461bef0fdfc2d159a /bin/dash .ft P .fi .UNINDENT .UNINDENT .sp In most cases this shouldn\(aqt be a problem, but it is still possible to strictly bind \fB/bin/bash\fP over \fB/bin/sh\fP \-\- without dereferencing it \-\- by specifying the \fB!\fP character at the end: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-b \(aq/bin/bash:/bin/sh!\(aq $ md5sum /bin/sh 089ed56cd74e63f461bef0fdfc2d159a /bin/sh $ md5sum /bin/bash 089ed56cd74e63f461bef0fdfc2d159a /bin/bash $ md5sum /bin/dash c229085928dc19e8d9bd29fe88268504 /bin/dash .ft P .fi .UNINDENT .UNINDENT .SS \fBchroot\fP + \fBmount \-\-bind\fP equivalent .sp The two features above can be combined to make any file from the host rootfs accessible in the confined environment just as if it were initially part of the guest rootfs. It is sometimes required to run programs that rely on some specific files: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/slackware\-8.0/ $ ps \-o tty,command Error, do this: mount \-t proc none /proc .ft P .fi .UNINDENT .UNINDENT .sp works better with: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/slackware\-8.0/ \-b /proc $ ps \-o tty,command TT COMMAND ? \-bash ? proot \-b /proc /mnt/slackware\-8.0/ ? /lib/ld\-linux.so.2 /bin/sh ? /lib/ld\-linux.so.2 /usr/bin/ps \-o tty,command .ft P .fi .UNINDENT .UNINDENT .sp Actually there\(aqs a bunch of such specific files, that\(aqs why PRoot provides the option \fB\-B\fP to bind automatically a pre\-defined list of recommended paths: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/slackware\-8.0/ \-B $ ps \-o tty,command TT COMMAND pts/6 \-bash pts/6 proot \-B /mnt/slackware\-8.0/ pts/6 /lib/ld\-linux.so.2 /bin/sh pts/6 /lib/ld\-linux.so.2 /usr/bin/ps \-o tty,command .ft P .fi .UNINDENT .UNINDENT .SS \fBchroot\fP + \fBmount \-\-bind\fP + \fBbinfmt_misc\fP equivalent .sp PRoot uses QEMU user\-mode to execute programs built for a CPU architecture incompatible with the host one. From users\(aq point\-of\-view, guest programs handled by QEMU user\-mode are executed transparently, that is, just like host programs. To enable this feature users just have to specify which instance of QEMU user\-mode they want to use with the option \fB\-q\fP or \fB\-Q\fP (this latter implies \fB\-B\fP): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/armslack\-12.2/ \-Q qemu\-arm $ cat /etc/motd Welcome to ARMedSlack Linux 12.2 .ft P .fi .UNINDENT .UNINDENT .sp The parameter of the \fB\-q/\-Q\fP option is actually a whole QEMU user\-mode command, for instance to enable its GDB server on port 1234: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/armslack\-12.2/ \-Q "qemu\-arm \-g 1234" emacs .ft P .fi .UNINDENT .UNINDENT .sp PRoot allows to mix transparently the emulated execution of guest programs and the native execution of host programs in the same file\-system namespace. It\(aqs typically useful to extend the list of available programs and to speed up build\-time significantly. This mixed\-execution feature is enabled by default when using QEMU user\-mode, and the content of the host rootfs is made accessible through \fB/host\-rootfs\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/armslack\-12.2/ \-Q qemu\-arm $ file /bin/echo [...] ELF 32\-bit LSB executable, ARM [...] $ /bin/echo \(aqHello world!\(aq Hello world! $ file /host\-rootfs/bin/echo [...] ELF 64\-bit LSB executable, x86\-64 [...] $ /host\-rootfs/bin/echo \(aqHello mixed world!\(aq Hello mixed world! .ft P .fi .UNINDENT .UNINDENT .sp Since both host and guest programs use the guest rootfs as \fB/\fP, users may want to deactivate explicitly cross\-filesystem support found in most GNU cross\-compilation tools. For example with GCC configured to cross\-compile to the ARM target: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/armslack\-12.2/ \-Q qemu\-arm $ export CC=/host\-rootfs/opt/cross\-tools/arm\-linux/bin/gcc $ export CFLAGS="\-\-sysroot=/" # could be optional indeed $ ./configure; make .ft P .fi .UNINDENT .UNINDENT .sp As with regular files, a host instance of a program can be bound over its guest instance. Here is an example where the guest binary of \fBmake\fP is overlaid by the host one: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C proot \-r /mnt/armslack\-12.2/ \-Q qemu\-arm \-b /usr/bin/make $ which make /usr/bin/make $ make \-\-version # overlaid GNU Make 3.82 Built for x86_64\-slackware\-linux\-gnu .ft P .fi .UNINDENT .UNINDENT .sp It\(aqs worth mentioning that even when mixing the native execution of host programs and the emulated execution of guest programs, they still believe they are running in a native guest environment. As a demonstration, here is a partial output of a typical \fB./configure\fP script: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C checking whether the C compiler is a cross\-compiler... no .ft P .fi .UNINDENT .UNINDENT .SH DOWNLOADS .SS PRoot .sp The latest release of PRoot is packaged on \fI\%http://packages.proot.me\fP and sources are hosted on \fI\%http://github.proot.me\fP. Also, highly compatible binaries are available on \fI\%http://static.proot.me\fP for a couple of architectures. .SS Rootfs .sp Here follows a couple of URLs where some rootfs archives can be freely downloaded. Note that the errors reported by \fBtar\fP when extracting these archives can be safely ignored. Obviously these files are not required when PRoot is used as a \fBmount \-\-bind\fP equivalent only. .INDENT 0.0 .IP \(bu 2 Slackware, Arch, Fedora for ARM: .INDENT 2.0 .IP \(bu 2 \fI\%ftp://ftp.armedslack.org/slackwarearm/slackwarearm\-devtools/minirootfs/roots/\fP .IP \(bu 2 \fI\%http://archlinuxarm.org/developers/downloads\fP .IP \(bu 2 \fI\%http://ftp.linux.org.uk/pub/linux/arm/fedora/rootfs/\fP .UNINDENT .IP \(bu 2 CentOS, Debian, Fedora, Scientific, Suse, Ubuntu, ALT, Arch, CERN, Gentoo, OpenSuse, Openwall, Slackware, SLES, and etc. for x86 and x86_64 CPUs: .INDENT 2.0 .IP \(bu 2 \fI\%http://download.openvz.org/template/precreated/\fP .IP \(bu 2 \fI\%http://cdimage.ubuntu.com/ubuntu\-core/releases/\fP .UNINDENT .IP \(bu 2 Gentoo for a lot of architectures: .INDENT 2.0 .IP \(bu 2 \fI\%http://distfiles.gentoo.org/releases/\fP .UNINDENT .UNINDENT .sp Technically such rootfs archive can be created by running the following command on the expected Linux distribution: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C tar \-\-one\-file\-system \-\-create \-\-gzip \-\-file my_rootfs.tar.gz / .ft P .fi .UNINDENT .UNINDENT .SS QEMU user\-mode .sp QEMU user\-mode is required only if the guest rootfs was made for a CPU architecture incompatible with the host one, for instance when using a ARM rootfs on a x86_64 computer. This package can be installed either from \fI\%http://qemu.proot.me\fP or from the host package manager under the name of "qemu\-user" on most Linux distro. In case one would like to build QEMU user\-mode from sources, the \fB\-\-enable\-linux\-user\fP option has to be specified to the \fB./configure\fP script. .SH SEE ALSO .sp chroot(1), mount(8), binfmt_misc, ptrace(2), qemu(1), sb2(1), bindfs(1), fakeroot(1), fakechroot(1) .SH COLOPHON .sp Visit \fI\%http://proot.me\fP for help, bug reports, suggestions, patchs, ... Copyright (C) 2013 STMicroelectronics, licensed under GPL v2 or later. .\" Generated by docutils manpage writer. . proot-3.0.2/doc/manual.txt0000644000175000017500000004110012156552156015022 0ustar ivoireivoire======= PRoot ======= ------------------------------------------------------------------------- ``chroot``, ``mount --bind``, and ``binfmt_misc`` without privilege/setup ------------------------------------------------------------------------- :Date: 2013-06-14 :Version: 3.0.2 :Manual section: 1 Synopsis ======== **proot** [*option*] ... [*command*] Description =========== PRoot is a user-space implementation of ``chroot``, ``mount --bind``, and ``binfmt_misc``. This means that users don't need any privileges or setup to do things like using an arbitrary directory as the new root filesystem, making files accessible somewhere else in the filesystem hierarchy, or executing programs built for another CPU architecture transparently through QEMU user-mode. Also, developers can add their own features or use PRoot as a Linux process instrumentation engine thanks to its extension mechanism. Technically PRoot relies on ``ptrace``, an unprivileged system-call available in every Linux kernel. The new root file-system, a.k.a *guest rootfs*, typically contains a Linux distribution. By default PRoot confines the execution of programs to the guest rootfs only, however users can use the built-in *mount/bind* mechanism to access files and directories from the actual root file-system, a.k.a *host rootfs*, just as if they were part of the guest rootfs. When the guest Linux distribution is made for a CPU architecture incompatible with the host one, PRoot uses the CPU emulator QEMU user-mode to execute transparently guest programs. It's a convenient way to develop, to build, and to validate any guest Linux packages seamlessly on users' computer, just as if they were in a *native* guest environment. That way all of the cross-compilation issues are avoided. PRoot can also *mix* the execution of host programs and the execution of guest programs emulated by QEMU user-mode. This is useful to use host equivalents of programs that are missing from the guest rootfs and to speed up build-time by using cross-compilation tools or CPU-independent programs, like interpreters. It is worth noting that the guest kernel is never involved, regardless of whether QEMU user-mode is used or not. Technically, when guest programs perform access to system resources, PRoot translates their requests before sending them to the host kernel. This means that guest programs can use host resources (devices, network, ...) just as if they were "normal" host programs. Options ======= The command-line interface is composed of two parts: first PRoot's options (optional), then the command to launch (``/bin/sh`` if not specified). This section describes the options supported by PRoot, that is, the first part of its command-line interface. Regular options --------------- -r path, --rootfs=path Use *path* as the new guest root file-system, default is ``/``. The specified *path* typically contains a Linux distribution where all new programs will be confined. The default rootfs is ``/`` when none is specified, this makes sense when the bind mechanism is used to relocate host files and directories, see the ``-b`` option and the ``Examples`` section for details. -b path, --bind=path, -m path, --mount=path Make the content of *path* accessible in the guest rootfs. This option makes any file or directory of the host rootfs accessible in the confined environment just as if it were part of the guest rootfs. By default the host path is bound to the same path in the guest rootfs but users can specify any other location with the syntax: ``-b *host_path*:*guest_location*``. If the guest location is a symbolic link, it is dereferenced to ensure the new content is accessible through all the symbolic links that point to the overlaid content. In most cases this default behavior shouldn't be a problem, although it is possible to explicitly not dereference the guest location by appending it the ``!`` character: ``-b *host_path*:*guest_location!*``. -q command, --qemu=command Execute guest programs through QEMU as specified by *command*. Each time a guest program is going to be executed, PRoot inserts the QEMU user-mode *command* in front of the initial request. That way, guest programs actually run on a virtual guest CPU emulated by QEMU user-mode. The native execution of host programs is still effective and the whole host rootfs is bound to ``/host-rootfs`` in the guest environment. This option is automatically enabled by the ``-Q`` option. -w path, --pwd=path, --cwd=path Set the initial working directory to *path*. Some programs expect to be launched from a given directory but do not perform any ``chdir`` by themselves. This option avoids the need for running a shell and then entering the directory manually. -v value, --verbose=value Set the level of debug information to *value*. The higher the integer *value* is, the more detailled debug information is printed to the standard error stream. A negative *value* makes PRoot quiet except on fatal errors. -V, --version, --about Print version, copyright, license and contact, then exit. -h, --help, --usage Print the version and the command-line usage, then exit. Extension options ----------------- The following options enable built-in extensions. Technically developers can add their own features to PRoot or use it as a Linux process instrumentation engine thanks to its extension mechanism, see the sources for further details. -k string, --kernel-release=string Set the kernel release and compatibility level to *string*. If a program is run on a kernel older than the one expected by its GNU C library, the following error is reported: "FATAL: kernel too old". To be able to run such programs, PRoot can emulate some of the syscalls that are available in the kernel release specified by *string* but that are missing in the current kernel. -0, --root-id Force some syscalls to behave as if executed by "root". Some programs will refuse to work if they are not run with "root" privileges, even if there is no technical reason for that. This is typically the case with package managers. This option allows users to bypass this kind of limitation by faking the user/group identity, and by faking the success of some operations like changing the ownership of files, changing the root directory to ``/``, ... Note that this option is quite limited compared to ``fakeroot``. Alias options ------------- The following options are aliases for handy sets of options. -B, -M Alias: ``-b`` for each path of a recommended list There are a couple of bindings that are needed for most guest programs to behave correctly regarding the configuration part of the host computer which is not specific to the host Linux distribution, such as: user/group information, network setup, run-time information, users' files, ... This highly recommended option enables the following bindings: * /etc/host.conf * /etc/hosts * /etc/hosts.equiv * /etc/mtab * /etc/netgroup * /etc/networks * /etc/passwd * /etc/group * /etc/nsswitch.conf * /etc/resolv.conf * /etc/localtime * /dev/ * /sys/ * /proc/ * /tmp/ * $HOME -Q command Alias: ``-q *command* -B`` This option is highly recommended when using QEMU user-mode; it enables all the recommended bindings. Exit Status =========== If an internal error occurs, ``proot`` returns a non-zero exit status, otherwise it returns the exit status of the last terminated program. When an error has occurred, the only way to know if it comes from the last terminated program or from ``proot`` itself is to have a look at the error message. Files ===== PRoot reads links in ``/proc//fd/`` to support `openat(2)`-like syscalls made by the guest programs. Examples ======== In the following examples the directories ``/mnt/slackware-8.0`` and ``/mnt/armslack-12.2/`` contain a Linux distribution respectively made for x86 CPUs and ARM CPUs. ``chroot`` equivalent --------------------- To execute a command inside a given Linux distribution, just give ``proot`` the path to the guest rootfs followed by the desired command. The example below executes the program ``cat`` to print the content of a file:: proot -r /mnt/slackware-8.0/ cat /etc/motd Welcome to Slackware Linux 8.0 The default command is ``/bin/sh`` when none is specified. Thus the shortest way to confine an interactive shell and all its sub-programs is:: proot -r /mnt/slackware-8.0/ $ cat /etc/motd Welcome to Slackware Linux 8.0 ``mount --bind`` equivalent --------------------------- The bind mechanism permits to relocate files and directories. This is typically useful to cheat programs that perform access to hard-coded locations, like some installation scripts:: proot -b /tmp/alternate_opt:/opt $ cd to/sources $ make install [...] install -m 755 prog "/opt/bin" [...] # prog is installed in "/tmp/alternate_opt/bin" actually As shown in this example, it is possible to bind over files not even owned by the user. This can be used to *overlay* system configuration files, for instance the DNS setting:: ls -l /etc/hosts -rw-r--r-- 1 root root 675 Mar 4 2011 /etc/hosts :: proot -b ~/alternate_hosts:/etc/hosts $ echo '1.2.3.4 google.com' > /etc/hosts $ resolveip google.com IP address of google.com is 1.2.3.4 $ echo '5.6.7.8 google.com' > /etc/hosts $ resolveip google.com IP address of google.com is 5.6.7.8 Another example: on most Linux distributions ``/bin/sh`` is a symbolic link to ``/bin/bash``, whereas it points to ``/bin/dash`` on Debian and Ubuntu. As a consequence a ``#!/bin/sh`` script tested with Bash might not work with Dash. In this case, the binding mechanism of PRoot can be used to set non-disruptively ``/bin/bash`` as the default ``/bin/sh`` on these two Linux distributions:: proot -b /bin/bash:/bin/sh [...] Because ``/bin/sh`` is initially a symbolic link to ``/bin/dash``, the content of ``/bin/bash`` is actually bound over this latter:: proot -b /bin/bash:/bin/sh $ md5sum /bin/sh 089ed56cd74e63f461bef0fdfc2d159a /bin/sh $ md5sum /bin/bash 089ed56cd74e63f461bef0fdfc2d159a /bin/bash $ md5sum /bin/dash 089ed56cd74e63f461bef0fdfc2d159a /bin/dash In most cases this shouldn't be a problem, but it is still possible to strictly bind ``/bin/bash`` over ``/bin/sh`` -- without dereferencing it -- by specifying the ``!`` character at the end:: proot -b '/bin/bash:/bin/sh!' $ md5sum /bin/sh 089ed56cd74e63f461bef0fdfc2d159a /bin/sh $ md5sum /bin/bash 089ed56cd74e63f461bef0fdfc2d159a /bin/bash $ md5sum /bin/dash c229085928dc19e8d9bd29fe88268504 /bin/dash ``chroot`` + ``mount --bind`` equivalent ---------------------------------------- The two features above can be combined to make any file from the host rootfs accessible in the confined environment just as if it were initially part of the guest rootfs. It is sometimes required to run programs that rely on some specific files:: proot -r /mnt/slackware-8.0/ $ ps -o tty,command Error, do this: mount -t proc none /proc works better with:: proot -r /mnt/slackware-8.0/ -b /proc $ ps -o tty,command TT COMMAND ? -bash ? proot -b /proc /mnt/slackware-8.0/ ? /lib/ld-linux.so.2 /bin/sh ? /lib/ld-linux.so.2 /usr/bin/ps -o tty,command Actually there's a bunch of such specific files, that's why PRoot provides the option ``-B`` to bind automatically a pre-defined list of recommended paths:: proot -r /mnt/slackware-8.0/ -B $ ps -o tty,command TT COMMAND pts/6 -bash pts/6 proot -B /mnt/slackware-8.0/ pts/6 /lib/ld-linux.so.2 /bin/sh pts/6 /lib/ld-linux.so.2 /usr/bin/ps -o tty,command ``chroot`` + ``mount --bind`` + ``binfmt_misc`` equivalent ---------------------------------------------------------- PRoot uses QEMU user-mode to execute programs built for a CPU architecture incompatible with the host one. From users' point-of-view, guest programs handled by QEMU user-mode are executed transparently, that is, just like host programs. To enable this feature users just have to specify which instance of QEMU user-mode they want to use with the option ``-q`` or ``-Q`` (this latter implies ``-B``):: proot -r /mnt/armslack-12.2/ -Q qemu-arm $ cat /etc/motd Welcome to ARMedSlack Linux 12.2 The parameter of the ``-q/-Q`` option is actually a whole QEMU user-mode command, for instance to enable its GDB server on port 1234:: proot -r /mnt/armslack-12.2/ -Q "qemu-arm -g 1234" emacs PRoot allows to mix transparently the emulated execution of guest programs and the native execution of host programs in the same file-system namespace. It's typically useful to extend the list of available programs and to speed up build-time significantly. This mixed-execution feature is enabled by default when using QEMU user-mode, and the content of the host rootfs is made accessible through ``/host-rootfs``:: proot -r /mnt/armslack-12.2/ -Q qemu-arm $ file /bin/echo [...] ELF 32-bit LSB executable, ARM [...] $ /bin/echo 'Hello world!' Hello world! $ file /host-rootfs/bin/echo [...] ELF 64-bit LSB executable, x86-64 [...] $ /host-rootfs/bin/echo 'Hello mixed world!' Hello mixed world! Since both host and guest programs use the guest rootfs as ``/``, users may want to deactivate explicitly cross-filesystem support found in most GNU cross-compilation tools. For example with GCC configured to cross-compile to the ARM target:: proot -r /mnt/armslack-12.2/ -Q qemu-arm $ export CC=/host-rootfs/opt/cross-tools/arm-linux/bin/gcc $ export CFLAGS="--sysroot=/" # could be optional indeed $ ./configure; make As with regular files, a host instance of a program can be bound over its guest instance. Here is an example where the guest binary of ``make`` is overlaid by the host one:: proot -r /mnt/armslack-12.2/ -Q qemu-arm -b /usr/bin/make $ which make /usr/bin/make $ make --version # overlaid GNU Make 3.82 Built for x86_64-slackware-linux-gnu It's worth mentioning that even when mixing the native execution of host programs and the emulated execution of guest programs, they still believe they are running in a native guest environment. As a demonstration, here is a partial output of a typical ``./configure`` script:: checking whether the C compiler is a cross-compiler... no Downloads ========= PRoot ----- The latest release of PRoot is packaged on http://packages.proot.me and sources are hosted on http://github.proot.me. Also, highly compatible binaries are available on http://static.proot.me for a couple of architectures. Rootfs ------ Here follows a couple of URLs where some rootfs archives can be freely downloaded. Note that the errors reported by ``tar`` when extracting these archives can be safely ignored. Obviously these files are not required when PRoot is used as a ``mount --bind`` equivalent only. * Slackware, Arch, Fedora for ARM: * ftp://ftp.armedslack.org/slackwarearm/slackwarearm-devtools/minirootfs/roots/ * http://archlinuxarm.org/developers/downloads * http://ftp.linux.org.uk/pub/linux/arm/fedora/rootfs/ * CentOS, Debian, Fedora, Scientific, Suse, Ubuntu, ALT, Arch, CERN, Gentoo, OpenSuse, Openwall, Slackware, SLES, and etc. for x86 and x86_64 CPUs: * http://download.openvz.org/template/precreated/ * http://cdimage.ubuntu.com/ubuntu-core/releases/ * Gentoo for a lot of architectures: * http://distfiles.gentoo.org/releases/ Technically such rootfs archive can be created by running the following command on the expected Linux distribution:: tar --one-file-system --create --gzip --file my_rootfs.tar.gz / QEMU user-mode -------------- QEMU user-mode is required only if the guest rootfs was made for a CPU architecture incompatible with the host one, for instance when using a ARM rootfs on a x86_64 computer. This package can be installed either from http://qemu.proot.me or from the host package manager under the name of "qemu-user" on most Linux distro. In case one would like to build QEMU user-mode from sources, the ``--enable-linux-user`` option has to be specified to the ``./configure`` script. See Also ======== chroot(1), mount(8), binfmt_misc, ptrace(2), qemu(1), sb2(1), bindfs(1), fakeroot(1), fakechroot(1) Colophon ======== Visit http://proot.me for help, bug reports, suggestions, patchs, ... Copyright (C) 2013 STMicroelectronics, licensed under GPL v2 or later. proot-3.0.2/doc/howto-release.txt0000644000175000017500000000326112156552156016331 0ustar ivoireivoireHow to Make a Release of PRoot? =============================== This document summarizes the required checks that shall be done before releasing PRoot. Sanity Checks ------------- The PRoot's testsuite (`make -C test`) shall suffer no failure on: - x86_64 (all OBS distros + Slackware64) - x86 (all OBS distros) - arm (STLinux) - sh4 (STLinux) The PRoot's testsuite under Valgrind/Memcheck (`make -C tests memcheck`) shall suffer no failure on: - x86_64 (Slackware64) The PRoot's testsuite with AddressSanitizer enabled (`CFLAGS=-fsanitize=address LDFLAGS=-lasan`) shall suffer no failure on: - x86_64 (Slackware64) Talloc shall report no leak during all the PRoot's testsuite (`make -C tests V=1 2>&1 | grep talloc`) on: - x86_64 (Slackware64) Use-Case Checks --------------- The GNU CoreUtils package (`./configure; make; make check`) shall suffer no regression on: - x86_64 - x86_64 with a ARM rootfs (QEMU) The Perl package (`./Configure -de; make; make check`) shall suffer no regression on: - x86_64 - x86_64 with a x86 rootfs - x86_64 with a ARM rootfs (QEMU) - x86_64 with a ARM rootfs (QEMU) under a x86 rootfs (sub-reconf') The Open POSIX Test Suite package (`make`) shall suffer no regression on: - x86_64 Performance Checks ------------------ The following command shall suffer no non-undestood performance regression:: time proot -B perl -e 'system("/usr/bin/true") for (1..10000)' where "/usr/bin/true" is a symlink to "/bin/true". Documentation Update -------------------- 1. update the release number in "doc/manual.txt" 2. regenerate the documentation: `make -C doc` 3. regenerate the command-line interface: `cp doc/cli.h src/cli.h; $EDITOR src/cli.h` proot-3.0.2/doc/GNUmakefile0000644000175000017500000000100612156552156015057 0ustar ivoireivoireall: proot.1 cli.h proot.spec index.html proot.1: manual.txt rst2man $< $@ %.xml: %.txt rst2xml --no-doctype $< $@ %.html: %.txt rst2html $< $@ # Workaround to avoid unescaped C character. manual-quoted.txt: manual.txt sed 's/"/\\\\"/g' $^ > $@ cli.h: stylesheets/cli.xsl manual-quoted.xml xsltproc --output $@ $^ proot.spec: stylesheets/rpm-spec.xsl manual.xml xsltproc --output $@ $^ index.html: stylesheets/website.xsl manual.xml xsltproc --output $@ $^ clean: rm -f *.xml manual-quoted.txt cli.h proot-3.0.2/doc/articles/0000755000175000017500000000000012156552156014616 5ustar ivoireivoireproot-3.0.2/doc/articles/howto_debian_rootfs.txt0000644000175000017500000001566512156552156021432 0ustar ivoireivoire.. include:: ../stylesheets/article-html.txt ======================================= How to Set Up a Debian RootFS for PRoot ======================================= :Original Title: PRoot sorcery :URL: http://ivoire.dinauz.org/blog/post/2012/04/16/PRoot-sorcery :Part of: http://ivoire.dinauz.org/blog/tag/PRoot :Author: Rémi Duraffort :PRoot Version: 1.8.3 :Abstract: A good practice for software developer is to provide a test suite while developing a software. When developing for Linux, it's also a good practice to compile the software and run the test suite on many distributions like Debian, Ubuntu, Fedora, ArchLinux, Centos, Slackware and for both i386 and x86_64. Usually, softwares are compiled and tested on only one distribution because setting up the right environment is long and painful. Most of the time root privileges are also required to setup such environment. In this article and the following one, I will show that using PRoot_, such testing is quite handy and can be done by any users. .. _PRoot: http://proot.me Getting PRoot up and running ============================ In order to test PRoot, you can download the latest version on the `official website`_ and compile it. You can also grab a package for your distribution on the `Open Build Service`_. .. _official website: http://proot.me .. _Open Build Service: http://software.opensuse.org/download.html?project=home:cedric-vincent&package=proot If you choose to compile PRoot, that's just a matter of:: #!/bin/sh git clone git://github.com/cedric-vincent/PRoot.git cd PRoot/src make [...] ./proot Grabbing a RootFS ================= The second step to test VLC media player in different distributions is to get a root file system for every distribution we want to try. The first and easy way to have a working root file system is to download it from `OpenVZ repository`_ or `OpenVZ contribs`_. .. _OpenVZ repository: http://download.openvz.org/template/precreated/ .. _OpenVZ contribs: http://download.openvz.org/template/precreated/contrib/ It's also possible, under Debian and Ubuntu, to create a root file system using debootstrap, but let's take the easy way for today:: #!/bin/sh % mkdir debian-6.0-x86_64 % cd debian-6.0-x86_64 % wget http://download.openvz.org/template/precreated/debian-6.0-x86_64.tar.gz [...] % tar xf debian-6.0-x86_64.tar.gz % cd .. As we will see later, you can safely ignore the warnings printed by tar when extracting the file system. Let us note that everything is run as a normal user. Now you can "jump" into this new root file system using PRoot:: #!/bin/sh % cat /etc/debian_version wheezy/sid % proot debian-6.0-x86_64 ~ cat /etc/debian_version 6.0.4 For now on, the root file system is the one you just downloaded. For instance:: #!/bin/sh ~ gcc --version gcc (Debian 4.4.5-8) 4.4.5 ~ logout % gcc --version gcc (Debian 4.6.3-1) 4.6.3 You just tested the first feature provided by PRoot: * Changing the root file system of a process As you may have noticed, I used ``%`` for the host file system shell prompt (a Debian Sid) and ``~`` for the PRooted one (a Debian Squeeze). Setting up the new RootFS ========================= We now have a basic and working root file systems but some configuration has to be done before any real usages: * Adding the right users and groups to /etc/passwd and /etc/group * Configuring DNS resolution * Binding some special directories * Updating the system Normally, all this tasks can only be done by root as they will modifies files owned by root. As we extracted the archive as a normal user, the current user can modify any files in the root file system though making root privileges pointless. Adding some user and groups --------------------------- In order to keep the same user inside the PRooted file system, you just have to copy the right lines from ``/etc/passwd`` to the corresponding file in the PRooted file system. You can do the same thing for groups in ``/etc/group``. Configuring DNS resolution -------------------------- Just copy ``/etc/resolv.conf`` from the host root file system to the PRooted one. This way the same mechanism will be used in the host system and in the PRooted one. Another solution is to ask PRoot to do the job for you: binding /etc/resolv.conf to the same file in the PRooted file system. Adding ``-b /etc/resolv.conf`` to the PRoot command line will do the trick:: #!/bin/sh % proot debian-6.0-x86_66 ~ cat /etc/resolv.conf cat: /etc/resolv.conf: No such file or directory ~ logout % proot -b /etc/resolv.conf debian-6.0-x86_64 ~ cat /etc/resolv.conf [...]same file as /etc/resolv.conf on the host[...] You just tested the second feature provided by PRoot: * Binding some files to another location in the file system. In this case you bound /etc/resolv.conf to the same location inside the PRooted environment. It's also possible to bind it somewhere else with: ``-b /etc/resolv.conf:/Somewere_else_in_the_PRooted_FS``. Binding some special directories -------------------------------- For the moment, the ``/dev`` and ``/proc`` directories are empty. However some programs need it in order to work correctly. For example ssh-keygen will refuse to work without ``/dev/random``. We should bind the real /dev and /proc in the new root file system. Adding ``-b /dev -b /proc`` to the PRoot command line will solve this issue. Updating the system ------------------- We already noticed that the current user can modify any file on the PRooted file system because it was extracted by this user. However most tools like dpkg required the current user id to be root in order to work. For this reason, PRoot can be launched with the ``-0`` (zero) option which fake some syscalls and makes the programs think the current user is root:: #!/bin/sh % proot -b /etc/resolv.conf -0 debian-6.0-x86_64 ~ id -a uid=0(root) gid=0(root) groupes=0(root) ~ cat /etc/apt/source.list deb http://ftp.debian.org/debian squeeze main contrib non-free deb http://security.debian.org squeeze/updates main contrib non-free ~ apt-get update Hit http://ftp.debian.org squeeze Release.gpg Ign http://ftp.debian.org/debian/ squeeze/contrib Translation-en [...] Reading package lists... Done ~ apt-get upgrade [...] You can manage this root file system like a classical one. Pay attention that some services that really required root privileges to work (like apache or some daemons) could not run correctly under PRoot as we only fake root privileges. Future work =========== This article is beginning to be really long so I will finish here for today. We saw a simple way to get a working Debian root file systems that we can manage without real root privileges. This work will be useful for the next article which will cover the compilation and testing of VLC media player in this new root file system. proot-3.0.2/doc/articles/extending_qemu.txt0000644000175000017500000002300312156552156020371 0ustar ivoireivoire.. include:: ../stylesheets/article-html.txt ================================================ Extending QEMU User-Mode with PRoot: Why and How ================================================ :Original Title: PRoot: a Step Forward for QEMU User-Mode :Published at: 1st International QEMU Users' Forum :Authors: Cédric Vincent & Yves Janin :PRoot Version: 0.5 :Abstract: This paper introduces PRoot, a new tool specifically designed to extend QEMU user-mode. We detail our motivations, compare with other similar solutions and give first results. Extending QEMU User-Mode ======================== QEMU [C1]_ is an open-source hardware emulator with two main usages: in *system-mode* a whole guest machine is emulated to run a full operating system. It can benefit from virtualization support like KVM to avoid the emulation of most CPU operations. By contrast, QEMU *user-mode* only emulates the CPU to run one guest process at a time, and communications with the host kernel are converted by a thin layer. Our initial motivation was to use QEMU to ease the development of embedded Linux applications by emulating their build system and test-suites directly on developers' workstations. During our experiments we found out that the system-mode was not fast enough and we decided to focus on the user-mode. Unfortunately this latter cannot execute a tree of guest processes within a guest Linux distribution for reasons we detail in the next section. To get the best of these two modes we developed PRoot, a tool fully compatible with QEMU that can be seen as an *extension* of the user-mode. Overcoming QEMU user-mode limitations with PRoot ------------------------------------------------ As illustrated by Figure 1 (a) and (b), QEMU should run faster for a given process in user-mode than in system-mode since it does not emulate the peripherals nor the guest kernel. However the boundary between the guest environment and the host becomes *thinner*, and the host kernel now has to support some of the operations that were devoted to the guest kernel. We can define three requirements that will guide us throughout the rest of this paper: R1 an efficient and correct path translation mechanism between the guest and the host. This feature is typically required to emulate programs that perform absolute accesses to architecture specific files such as the dynamic linker ``/lib/ld-linux.so``. Note that a correct translation scheme must support relative paths and symbolic links too. R2 the ability to spawn a new process and keep emulation running for this new process. R3 the ability to run a host process program. Strictly speaking R3 is not mandatory, but using *un-emulated* cross-compilers and interpreters is particularly useful to speed-up build process and test-suites. QEMU user-mode status for R1 and R2 is currently not satisfying. For R1, QEMU user-mode has the ability to redirect file operations made by the guest process but it can take several minutes at each process creation to scan the guest Linux distribution. Moreover there is no support for relative paths and symbolic links. Let us now consider R2. From the point-of-view of the host kernel, an emulated process image P1 is actually an image of QEMU |Q1P1|. When |Q1P1| executes a new program, the resultant process P2 is totally out of the control of QEMU. As a conclusion R2 is not supported either. .. |Q1P1| replace:: Q1\ :sub:`P1` .. figure:: extending_qemu-fig1.svg Figure 1: Guest process[es] running under QEMU, arrows are system calls flow The ambition of PRoot is to fulfill R1, R2 and later on R3, while keeping design, implementation and use as simple as possible. Since PRoot is also used for validation purpose, it has to reproduce kernel results and errors as if the emulated applications were run in a native environment. PRoot is based on ``ptrace``, a Linux system call [C2]_ that is mainly used by debuggers or system call tracers such as ``strace`` and the GNU Debugger ``gdb``. However since it provides unique facility to monitor and control processes, it can also be applied to a much wider range of applications bringing kernel-like features in user-space [C3]_ [C4]_. With ``ptrace``, PRoot is able to catch every system call emitted by a QEMUlated process and dynamically redirect them as shown in Figure 2. One of the main task in PRoot is the path canonicalization that translates paths from the guest world to the host world and vice versa (R1). Additionally, when PRoot detects a new process about to be created, it can change the initial request and get a new instance of QEMU running the new process (R2) as shown in Figure 1 (c). .. figure:: extending_qemu-fig2.svg Figure 2: Control flow of a syscall done by a guest process under QEMU+PRoot Results ======= Experiments presented in this section were performed on a Pentium-4 2.4GHz workstation with 1GiB of RAM running Slackware-13.1. Concerning the guest system, we used ARMedSlack-12.2 with QEMU-0.12.5 configured to provide 256MiB of RAM when used in system-mode. Functional Comparison With Similar Tools ---------------------------------------- Table 1 exemplifies two Linux packages that are representative of the complexity of an embedded Linux distribution; Perl-5 requires a guest environment for its bootstrap whereas the Coreutils test-suite exhibits numerous limit configurations that can help to find unexpected behaviors or bugs. Many path translation tools already exist but few pass these tests (R1). Fakechroot-2.14 and Fakeroot-Ng-0.17 fail. Plash is a modified GNU C library and was therefore not considered, and we did not consider ``chroot`` either since it requires administration privileges. Generally speaking, all these tools were not designed to support the execution of processes through an emulator (R2). Only PRoot and ScratchBox2 [C5]_ proved complete enough to configure, build and validate the two complex Linux packages. Table 1 gives the results of their test-suites run with PRoot and ScratchBox2, it also gives system-mode results as a reference. .. table:: Table 1: Results of the packages' test-suites ==================== =============== ========== =========== Package ScratchBox-2.2 PRoot-0.5 system-mode ==================== =============== ========== =========== Perl-5.10.0 99.6% 99.6% 99.8% GNU Coreutils-6.12 94.9% 97.3% 96.7% ==================== =============== ========== =========== ScratchBox2 and PRoot have similar path canonicalization algorithms but differ in their design [#]_ and usage. Unlike ScratchBox2, PRoot does not need any configuration and does not assume anything about the guest and host environments. By contrast, ScratchBox2 is a highly flexible tool with scripting facility. A complete technical comparison between ScratchBox2 and PRoot is beyond the scope of this paper, but we can safely conclude that both are functional enough to be already used in an industrial environment. Choosing one instead another one depends on your expectations: simplicity with PRoot vs powerful customization with ScratchBox2. .. [#] ScratchBox2 relies on the dynamic linker to override system call wrappers in the C library. Performance Comparison With QEMU System-Mode -------------------------------------------- Table 2 compares performance of QEMU-0.12.5, first in user-mode with PRoot-0.5 and then in system-mode. Timings were measured with the command ``time`` and we checked that the system-mode results were coherent with the host clock. .. table:: Table 2: Performance of PRoot+QEMU user-mode vs QEMU system-mode ================== =============== =============== stage Perl-5.10.0 Coreutils-6.12 ================== =============== =============== archive extraction 3.6x faster 2.7x faster configuration 2.0x faster 4.0x faster build 2.9x faster 3.5x faster validation 4.1x faster 3.6x faster ================== =============== =============== The speed-up with QEMU user-mode is quite impressive and comes from the fact that the guest kernel and the hardware are not emulated. We think that all these results will be further improved when PRoot handles *un-emulated* cross-compilers and interpreters (R3). This feature is still under development and looks promising. Conclusion ========== The simple fact that PRoot requires no privilege or setup is a great advantage that should ease the usage of this *extended* user-mode. It is worth mentioning that PRoot should work with any version of QEMU user-mode since no patch is needed, and in any Linux environment as well. Finally, the current version of PRoot is mature enough to be already used in an industrial environment. References ========== .. [C1] Bellard, F.: QEMU, a Fast and Portable Dynamic Translator. USENIX Annual Technical Conference, FREENIX Track (2005) 41--46 .. [C2] Kerrisk, M.: The Linux Programming Interface. No Starch Press (2010) 23, 43--46, 367--370 .. [C3] Spillane, R. P., Wright, C. P., Sivathanu, G., Zadok, E.: Rapid File System Development Using ptrace. Proceedings of the 2007 Workshop on Experimental Computer Science (ExpCS'07), 22. ACM (2007) .. [C4] Dike, J.: User-mode Linux. Proceedings of the 5th Annual Linux Showcase & Conference - Volume 5 (ALS'01). USENIX Association (2001) .. [C5] ScratchBox2. http://www.freedesktop.org/wiki/Software/sbox2 proot-3.0.2/doc/articles/extending_qemu-fig1.svg0000644000175000017500000012460012156552156021202 0ustar ivoireivoire image/svg+xml process 1 QEMU PRoot host kernel ... process N QEMU process 1 QEMU host kernel host kernel ... QEMU process 1 guest kernel process N (a) system-mode (b) user-mode (c) user-mode + PRoot proot-3.0.2/doc/articles/extending_qemu-fig2.svg0000644000175000017500000002405312156552156021204 0ustar ivoireivoire image/svg+xml QEMU: PRoot: host kernel: translation kernel operation system call result & errno t proot-3.0.2/AUTHORS0000644000175000017500000000163212156552156013315 0ustar ivoireivoireThe copyright holder for PRoot is STMicroelectronics, this tool is developed in the Compilation Expertise Center by: Cédric VINCENT Original author, maintainer. Rémi DURAFFORT Improved the -0 feature, many experiments, bug reports, fixes, and articles. Yves JANIN Support for the i386 architecture, many experiments and bug reports. Claire ROBINE Many bug fixes. Clément BAZIN Plugins interface, dependency tracker plugin, and bug reports. Christophe GUILLON Relocatable build directory, plugin experiments, and bug reports. Christian BERTIN Valuable support. Denis FERRANTI Valuable support. Paul GHALEB User manual review. proot-3.0.2/COPYING0000644000175000017500000004325412156552156013306 0ustar ivoireivoire GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. proot-3.0.2/src/0000755000175000017500000000000012156552156013032 5ustar ivoireivoireproot-3.0.2/src/.check_readlinkat.c0000644000175000017500000000016012156552156016524 0ustar ivoireivoire#define _ATFILE_SOURCE #include int main(void) { char buf[1]; return readlinkat(0, "", buf, 0); } proot-3.0.2/src/extension/0000755000175000017500000000000012156552156015046 5ustar ivoireivoireproot-3.0.2/src/extension/extension.c0000644000175000017500000001063612156552156017234 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* assert(3), */ #include /* talloc_*, */ #include /* LIST_*, */ #include /* bzero(3), */ #include "extension/extension.h" #include "notice.h" #include "build.h" #include "compat.h" /** * Remove an @extension from its tracee's list, then send it the * "REMOVED" event. * * Note: this is a Talloc destructor. */ static int remove_extension(Extension *extension) { LIST_REMOVE(extension, link); extension->callback(extension, REMOVED, 0, 0); bzero(extension, sizeof(Extension)); return 0; } /** * Allocate a new extension for the given @callback then attach it to * its @tracee. This function returns NULL on error, otherwise the * new extension. */ static Extension *new_extension(Tracee *tracee, extension_callback_t callback) { Extension *extension; /* Lazy allocation of the list head. */ if (tracee->extensions == NULL) { tracee->extensions = talloc_zero(tracee, Extensions); if (tracee->extensions == NULL) return NULL; } /* Allocate a new extension. */ extension = talloc_zero(tracee->extensions, Extension); if (extension == NULL) return NULL; extension->callback = callback; /* Attach it to its tracee. */ LIST_INSERT_HEAD(tracee->extensions, extension, link); talloc_set_destructor(extension, remove_extension); return extension; } /** * Initialize a new extension for the given @callback then attach it * to its @tracee. The parameter @cli is its argument that was passed * to the command-line interface. This function return -1 if an error * occurred, otherwise 0. */ int initialize_extension(Tracee *tracee, extension_callback_t callback, const char *cli) { Extension *extension; int status; extension = new_extension(tracee, callback); if (extension == NULL) { notice(tracee, WARNING, INTERNAL, "can't create a new extension"); return -1; } /* Remove the new extension if its initialized has failed. */ status = extension->callback(extension, INITIALIZATION, (intptr_t) cli, 0); if (status < 0) { TALLOC_FREE(extension); return status; } #if defined(HAVE_SECCOMP_FILTER) if (tracee->reconf.tracee != NULL && getenv("PROOT_NO_SECCOMP") == NULL) notice(tracee, WARNING, INTERNAL, "define the env. var. PROOT_NO_SECCOMP before executing the " "outer PRoot to ensure the inner extensions will correctly work"); #endif return 0; } /** * Rebuild a new list of extensions for this @child from its @parent. * The inheritance model is controlled by the @parent. */ void inherit_extensions(Tracee *child, Tracee *parent, bool sub_reconf) { Extension *parent_extension; Extension *child_extension; int status; if (parent->extensions == NULL) return; /* Sanity check. */ assert(child->extensions == NULL || sub_reconf); LIST_FOREACH(parent_extension, parent->extensions, link) { /* Ask the parent how this extension is * inheritable. */ status = parent_extension->callback(parent_extension, INHERIT_PARENT, (intptr_t)child, sub_reconf); /* Not inheritable. */ if (status < 0) continue; /* Inheritable... */ child_extension = new_extension(child, parent_extension->callback); if (child_extension == NULL) { notice(parent, WARNING, INTERNAL, "can't create a new extension for pid %d", child->pid); continue; } if (status == 0) { /* ... with a shared config or ... */ child_extension->config = talloc_reference(child_extension, parent_extension->config); } else { /* ... with another inheritance model. */ child_extension->callback(child_extension, INHERIT_CHILD, (intptr_t)parent_extension, sub_reconf); } } } proot-3.0.2/src/extension/kompat/0000755000175000017500000000000012156552156016341 5ustar ivoireivoireproot-3.0.2/src/extension/kompat/exit.c0000644000175000017500000000324412156552156017461 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ switch (peek_reg(tracee, ORIGINAL, SYSARG_NUM)) { case PR_uname: { struct utsname utsname; word_t address; word_t result; size_t size; assert(config->release != NULL); result = peek_reg(tracee, CURRENT, SYSARG_RESULT); /* Error reported by the kernel. */ if ((int) result < 0) return 0; address = peek_reg(tracee, ORIGINAL, SYSARG_1); status = read_data(tracee, &utsname, address, sizeof(utsname)); if (status < 0) return status; /* Note: on x86_64, we can handle the two modes (32/64) with * the same code since struct utsname has always the same * layout. */ size = sizeof(utsname.release); strncpy(utsname.release, config->release, size); utsname.release[size - 1] = '\0'; status = write_data(tracee, address, &utsname, sizeof(utsname)); if (status < 0) return status; return 0; } default: return 0; } proot-3.0.2/src/extension/kompat/kompat.c0000644000175000017500000001475212156552156020011 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* intptr_t, */ #include /* strtoul(3), */ #include /* KERNEL_VERSION, */ #include /* assert(3), */ #include /* uname(2), utsname, */ #include /* strncpy(3), */ #include /* talloc_*, */ #include /* AT_*, */ #include /* linux.git:c0a3a20b */ #include /* AUDIT_ARCH_*, */ #include "extension/extension.h" #include "syscall/seccomp.h" #include "tracee/tracee.h" #include "tracee/reg.h" #include "tracee/abi.h" #include "tracee/mem.h" #include "arch.h" #include "attribute.h" #include "compat.h" #define MAX_ARG_SHIFT 2 typedef struct { int expected_release; word_t new_sysarg_num; struct { Reg sysarg; /* first argument to be moved. */ size_t nb_args; /* number of arguments to be moved. */ int offset; /* offset to be applied. */ } shifts[MAX_ARG_SHIFT]; } Modif; typedef struct { const char *release; int emulated_release; int actual_release; } Config; /** * Modify the current syscall of @tracee as described by @modif * regarding the given @config. */ static void modify_syscall(Tracee *tracee, const Config *config, const Modif *modif) { size_t i, j; assert(config != NULL); /* Emulate only syscalls that are available in the expected * release but that are missing in the actual one. */ if ( modif->expected_release <= config->actual_release || modif->expected_release > config->emulated_release) return; poke_reg(tracee, SYSARG_NUM, modif->new_sysarg_num); /* Shift syscall arguments. */ for (i = 0; i < MAX_ARG_SHIFT; i++) { Reg sysarg = modif->shifts[i].sysarg; size_t nb_args = modif->shifts[i].nb_args; int offset = modif->shifts[i].offset; for (j = 0; j < nb_args; j++) { word_t arg = peek_reg(tracee, CURRENT, sysarg + j); poke_reg(tracee, sysarg + j + offset, arg); } } return; } /** * Return the numeric value for the given kernel @release. */ static int parse_kernel_release(const char *release) { unsigned long major = 0; unsigned long minor = 0; unsigned long revision = 0; char *cursor = (char *)release; major = strtoul(cursor, &cursor, 10); if (*cursor == '.') { cursor++; minor = strtoul(cursor, &cursor, 10); } if (*cursor == '.') { cursor++; revision = strtoul(cursor, &cursor, 10); } return KERNEL_VERSION(major, minor, revision); } /* List of syscalls handled by this extensions. */ #if defined(ARCH_X86_64) static const FilteredSyscall syscalls64[] = { #include SYSNUM_HEADER #include "extension/kompat/filter.h" #include SYSNUM_HEADER3 #include "extension/kompat/filter.h" FILTERED_SYSCALL_END }; static const FilteredSyscall syscalls32[] = { #include SYSNUM_HEADER2 #include "extension/kompat/filter.h" FILTERED_SYSCALL_END }; static const Filter filters[] = { { .architecture = AUDIT_ARCH_X86_64, .syscalls = syscalls64 }, { .architecture = AUDIT_ARCH_I386, .syscalls = syscalls32 }, { 0 } }; #elif defined(AUDIT_ARCH_NUM) static const FilteredSyscall syscalls[] = { #include SYSNUM_HEADER #include "extension/kompat/filter.h" FILTERED_SYSCALL_END }; static const Filter filters[] = { { .architecture = AUDIT_ARCH_NUM, .syscalls = syscalls }, { 0 } }; #else static const Filter filters[] = { 0 }; #endif #include "syscall/sysnum-undefined.h" /** * Handler for this @extension. It is triggered each time an @event * occured. See ExtensionEvent for the meaning of @data1 and @data2. */ int kompat_callback(Extension *extension, ExtensionEvent event, intptr_t data1, intptr_t data2 UNUSED) { int status; switch (event) { case INITIALIZATION: { struct utsname utsname; Config *config; extension->config = talloc_zero(extension, Config); if (extension->config == NULL) return -1; config = extension->config; config->release = talloc_strdup(config, (const char *)data1); if (config->release == NULL) return -1; talloc_set_name_const(config->release, "$release"); config->emulated_release = parse_kernel_release(config->release); status = uname(&utsname); if (status < 0 || getenv("PROOT_FORCE_SYSCALL_COMPAT") != NULL) config->actual_release = 0; else config->actual_release = parse_kernel_release(utsname.release); extension->filters = filters; return 0; } case SYSCALL_ENTER_END: { Tracee *tracee = TRACEE(extension); Config *config = talloc_get_type_abort(extension->config, Config); /* Nothing to do if this syscall is being discarded * (because of an error detected by PRoot). */ if ((int) data1 < 0) return 0; switch (get_abi(tracee)) { case ABI_DEFAULT: { #include SYSNUM_HEADER #include "extension/kompat/enter.c" return 0; } #ifdef SYSNUM_HEADER2 case ABI_2: { #include SYSNUM_HEADER2 #include "extension/kompat/enter.c" return 0; } #endif #ifdef SYSNUM_HEADER3 case ABI_3: { #include SYSNUM_HEADER3 #include "extension/kompat/enter.c" return 0; } #endif default: assert(0); } #include "syscall/sysnum-undefined.h" return 0; } case SYSCALL_EXIT_END: { Tracee *tracee = TRACEE(extension); Config *config = talloc_get_type_abort(extension->config, Config); switch (get_abi(tracee)) { case ABI_DEFAULT: { #include SYSNUM_HEADER #include "extension/kompat/exit.c" return 0; } #ifdef SYSNUM_HEADER2 case ABI_2: { #include SYSNUM_HEADER2 #include "extension/kompat/exit.c" return 0; } #endif #ifdef SYSNUM_HEADER3 case ABI_3: { #include SYSNUM_HEADER3 #include "extension/kompat/exit.c" return 0; } #endif default: assert(0); } #include "syscall/sysnum-undefined.h" return 0; } default: return 0; } } proot-3.0.2/src/extension/kompat/filter.h0000644000175000017500000000123712156552156020002 0ustar ivoireivoire/* This file was generated thanks to the following command: * * grep '^case PR_' enter.c exit.c | cut -f 2 -d ' ' | cut -f 1 -d : | sort -u */ { PR_accept4, 0 }, { PR_dup3, 0 }, { PR_epoll_create1, 0 }, { PR_epoll_pwait, 0 }, { PR_eventfd2, 0 }, { PR_faccessat, 0 }, { PR_fchmodat, 0 }, { PR_fchownat, 0 }, { PR_fstatat64, 0 }, { PR_futimesat, 0 }, { PR_inotify_init1, 0 }, { PR_linkat, 0 }, { PR_mkdirat, 0 }, { PR_mknodat, 0 }, { PR_newfstatat, 0 }, { PR_openat, 0 }, { PR_pipe2, 0 }, { PR_pselect6, 0 }, { PR_readlinkat, 0 }, { PR_renameat, 0 }, { PR_signalfd4, 0 }, { PR_symlinkat, 0 }, { PR_uname, 0 }, { PR_unlinkat, 0 }, proot-3.0.2/src/extension/kompat/enter.c0000644000175000017500000001720612156552156017630 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ /* Note: syscalls like "openat" can be replaced by "open" since PRoot * has canonicalized "fd + path" into "path". */ switch (peek_reg(tracee, ORIGINAL, SYSARG_NUM)) { case PR_accept4: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,28), .new_sysarg_num = PR_accept, .shifts = {{0}} }; modify_syscall(tracee, config, &modif); return 0; } case PR_dup3: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_dup2, .shifts = {{0}} }; modify_syscall(tracee, config, &modif); return 0; } case PR_epoll_create1: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_epoll_create, .shifts = {{0}} }; poke_reg(tracee, SYSARG_1, 0); /* Force "size" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_epoll_pwait: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,19), .new_sysarg_num = PR_epoll_wait, .shifts = {{0}} }; poke_reg(tracee, SYSARG_5, 0); /* Force "sigmask" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_eventfd2: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_eventfd, .shifts = {{0}} }; poke_reg(tracee, SYSARG_2, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_faccessat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_access, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_fchmodat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_chmod, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_fchownat: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 3, .offset = -1 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_5); modif.new_sysarg_num = ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? PR_lchown : PR_chown); poke_reg(tracee, SYSARG_5, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_newfstatat: case PR_fstatat64: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_4); modif.new_sysarg_num = ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? PR_lstat #if defined(ARCH_X86_64) : PR_stat); #else : PR_stat64); #endif poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_futimesat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_utimes, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_inotify_init1: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_inotify_init, .shifts = {{0}} }; poke_reg(tracee, SYSARG_1, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_linkat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_link, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 1, .offset = -1 }, [1] = { .sysarg = SYSARG_4, .nb_args = 1, .offset = -2 } } }; poke_reg(tracee, SYSARG_5, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_mkdirat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_mkdir, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_mknodat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_mknod, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 3, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_openat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_open, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 3, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_pipe2: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_pipe, .shifts = {{0}} }; poke_reg(tracee, SYSARG_2, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_pselect6: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,19), #if defined(ARCH_X86_64) .new_sysarg_num = PR_select, #else .new_sysarg_num = PR__newselect, #endif .shifts = {{0}} }; poke_reg(tracee, SYSARG_6, 0); /* Force "sigmask" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_readlinkat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_readlink, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 3, .offset = -1} } }; modify_syscall(tracee, config, &modif); return 0; } case PR_renameat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_rename, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 1, .offset =-1 }, [1] = { .sysarg = SYSARG_4, .nb_args = 1, .offset = -2 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_signalfd4: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,17), .new_sysarg_num = PR_signalfd, .shifts = {{0}} }; poke_reg(tracee, SYSARG_3, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_symlinkat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_symlink, .shifts = { [0] = { .sysarg = SYSARG_3, .nb_args = 1, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_unlinkat: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 1, .offset = -1 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_3); modif.new_sysarg_num = ((flags & AT_REMOVEDIR) != 0 ? PR_rmdir : PR_unlink); poke_reg(tracee, SYSARG_3, 0); /* Force "flags" to 0. */ modify_syscall(tracee, config, &modif); return 0; } case PR_uname: /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; return 0; default: return 0; } proot-3.0.2/src/extension/fake_id0/0000755000175000017500000000000012156552156016510 5ustar ivoireivoireproot-3.0.2/src/extension/fake_id0/exit.c0000644000175000017500000000713112156552156017627 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ syscall_number = peek_reg(tracee, ORIGINAL, SYSARG_NUM); switch (syscall_number) { case PR_chroot: { char path[PATH_MAX]; word_t result; word_t input; int status; /* Override only permission errors. */ result = peek_reg(tracee, CURRENT, SYSARG_RESULT); if ((int) result != -EPERM) return 0; input = peek_reg(tracee, MODIFIED, SYSARG_1); status = read_string(tracee, path, input, PATH_MAX); if (status < 0) return status; /* Only "new rootfs == current rootfs" is supported yet. */ status = compare_paths(get_root(tracee), path); if (status != PATHS_ARE_EQUAL) return 0; /* Force success. */ poke_reg(tracee, SYSARG_RESULT, 0); return 0; } case PR_chmod: case PR_chown: case PR_fchmod: case PR_fchown: case PR_lchown: case PR_chown32: case PR_fchown32: case PR_lchown32: case PR_fchmodat: case PR_fchownat: { word_t result; /* Override only permission errors. */ result = peek_reg(tracee, CURRENT, SYSARG_RESULT); if ((int) result != -EPERM) return 0; /* Force success. */ poke_reg(tracee, SYSARG_RESULT, 0); return 0; } case PR_fstatat64: case PR_newfstatat: case PR_stat64: case PR_lstat64: case PR_fstat64: case PR_stat: case PR_lstat: case PR_fstat: { word_t result; word_t address; word_t uid, gid; Reg sysarg; /* Override only if it succeed. */ result = peek_reg(tracee, CURRENT, SYSARG_RESULT); if (result != 0) return 0; /* Get the address of the 'stat' structure. */ if (syscall_number == PR_fstatat64 || syscall_number == PR_newfstatat) sysarg = SYSARG_3; else sysarg = SYSARG_2; address = peek_reg(tracee, ORIGINAL, sysarg); /* Get the uid & gid values from the 'stat' structure. */ uid = peek_mem(tracee, address + offsetof_stat_uid(tracee)); if (errno != 0) uid = 0; /* Not fatal. */ gid = peek_mem(tracee, address + offsetof_stat_gid(tracee)); if (errno != 0) gid = 0; /* Not fatal. */ /* These values are 32-bit width, even on 64-bit architecture. */ uid &= 0xFFFFFFFF; gid &= 0xFFFFFFFF; /* Override only if the file is owned by the current user. * Errors are not fatal here. */ if (uid == getuid()) poke_mem(tracee, address + offsetof_stat_uid(tracee), 0); if (gid == getgid()) poke_mem(tracee, address + offsetof_stat_gid(tracee), 0); return 0; } case PR_getuid: case PR_getgid: case PR_getegid: case PR_geteuid: case PR_getuid32: case PR_getgid32: case PR_geteuid32: case PR_getegid32: case PR_setuid: case PR_setgid: case PR_setfsuid: case PR_setfsgid: case PR_setuid32: case PR_setgid32: case PR_setfsuid32: case PR_setfsgid32: /* Force success. */ poke_reg(tracee, SYSARG_RESULT, 0); return 0; case PR_setresuid: case PR_setresgid: case PR_setresuid32: case PR_setresgid32: case PR_getresuid: case PR_getresuid32: case PR_getresgid: case PR_getresgid32: /* TODO. */ default: return 0; } proot-3.0.2/src/extension/fake_id0/fake_id0.c0000644000175000017500000001135012156552156020316 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* assert(3), */ #include /* intptr_t, */ #include /* E*, */ #include /* chmod(2), stat(2) */ #include /* get*id(2), */ #include /* linux.git:c0a3a20b */ #include /* AUDIT_ARCH_*, */ #include "extension/extension.h" #include "syscall/syscall.h" #include "syscall/seccomp.h" #include "tracee/tracee.h" #include "tracee/abi.h" #include "tracee/mem.h" #include "path/binding.h" #include "notice.h" #include "arch.h" typedef struct { char *path; mode_t mode; } ModifiedNode; /** * Restore the @node->mode for the given @node->path. * * Note: this is a Talloc destructor. */ static int restore_mode(ModifiedNode *node) { (void) chmod(node->path, node->mode); return 0; } /* List of syscalls handled by this extensions. */ #if defined(ARCH_X86_64) static const FilteredSyscall syscalls64[] = { #include SYSNUM_HEADER #include "extension/fake_id0/filter.h" #include SYSNUM_HEADER3 #include "extension/fake_id0/filter.h" FILTERED_SYSCALL_END }; static const FilteredSyscall syscalls32[] = { #include SYSNUM_HEADER2 #include "extension/fake_id0/filter.h" FILTERED_SYSCALL_END }; static const Filter filters[] = { { .architecture = AUDIT_ARCH_X86_64, .syscalls = syscalls64 }, { .architecture = AUDIT_ARCH_I386, .syscalls = syscalls32 }, { 0 } }; #elif defined(AUDIT_ARCH_NUM) static const FilteredSyscall syscalls[] = { #include SYSNUM_HEADER #include "extension/fake_id0/filter.h" FILTERED_SYSCALL_END }; static const Filter filters[] = { { .architecture = AUDIT_ARCH_NUM, .syscalls = syscalls }, { 0 } }; #else static const Filter filters[] = { 0 }; #endif #include "syscall/sysnum-undefined.h" /** * Handler for this @extension. It is triggered each time an @event * occurred. See ExtensionEvent for the meaning of @data1 and @data2. */ int fake_id0_callback(Extension *extension, ExtensionEvent event, intptr_t data1, intptr_t data2) { switch (event) { case INITIALIZATION: extension->filters = filters; return 0; case INHERIT_PARENT: /* Inheritable for sub reconfiguration ... */ return 1; case INHERIT_CHILD: /* ... but there's nothing else to do. */ return 0; case HOST_PATH: { Tracee *tracee = TRACEE(extension); switch (get_abi(tracee)) { case ABI_DEFAULT: { #include SYSNUM_HEADER #include "extension/fake_id0/host_path.c" return 0; } #ifdef SYSNUM_HEADER2 case ABI_2: { #include SYSNUM_HEADER2 #include "extension/fake_id0/host_path.c" return 0; } #endif #ifdef SYSNUM_HEADER3 case ABI_3: { #include SYSNUM_HEADER3 #include "extension/fake_id0/host_path.c" return 0; } #endif default: assert(0); } #include "syscall/sysnum-undefined.h" return 0; } case SYSCALL_ENTER_END: { Tracee *tracee = TRACEE(extension); word_t syscall_number; switch (get_abi(tracee)) { case ABI_DEFAULT: { #include SYSNUM_HEADER #include "extension/fake_id0/enter.c" return 0; } #ifdef SYSNUM_HEADER2 case ABI_2: { #include SYSNUM_HEADER2 #include "extension/fake_id0/enter.c" return 0; } #endif #ifdef SYSNUM_HEADER3 case ABI_3: { #include SYSNUM_HEADER3 #include "extension/fake_id0/enter.c" return 0; } #endif default: assert(0); } #include "syscall/sysnum-undefined.h" return 0; } case SYSCALL_EXIT_END: { Tracee *tracee = TRACEE(extension); word_t syscall_number; switch (get_abi(tracee)) { case ABI_DEFAULT: { #include SYSNUM_HEADER #include "extension/fake_id0/exit.c" return 0; } #ifdef SYSNUM_HEADER2 case ABI_2: { #include SYSNUM_HEADER2 #include "extension/fake_id0/exit.c" return 0; } #endif #ifdef SYSNUM_HEADER3 case ABI_3: { #include SYSNUM_HEADER3 #include "extension/fake_id0/exit.c" return 0; } #endif default: assert(0); } #include "syscall/sysnum-undefined.h" return 0; } default: return 0; } } proot-3.0.2/src/extension/fake_id0/host_path.c0000644000175000017500000000524212156552156020650 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ { ModifiedNode *node; struct stat perms; const char *path; mode_t new_mode; bool is_final; int status; path = (char*) data1; is_final = (bool) data2; /* Get the meta-data */ status = stat(path, &perms); if (status < 0) return 0; /* Copy the current permissions */ new_mode = perms.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); /* Add read and write permissions to everything. */ new_mode |= (S_IRUSR | S_IWUSR); /* Always add 'x' bit to directories */ if (S_ISDIR(perms.st_mode)) new_mode |= S_IXUSR; /* Patch the permissions only if needed. */ if (new_mode == (perms.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) return 0; node = talloc_zero(tracee->ctx, ModifiedNode); if (node == NULL) return 0; if (!is_final) { /* Restore the previous mode of any non final components. */ node->mode = perms.st_mode; } else { switch (peek_reg(tracee, ORIGINAL, SYSARG_NUM)) { /* For chmod syscalls: restore the new mode of the final component. */ case PR_chmod: node->mode = peek_reg(tracee, ORIGINAL, SYSARG_2); break; case PR_fchmodat: node->mode = peek_reg(tracee, ORIGINAL, SYSARG_3); break; /* For stat syscalls: don't touch the mode of the final component. */ case PR_fstatat64: case PR_lstat: case PR_lstat64: case PR_newfstatat: case PR_oldlstat: case PR_oldstat: case PR_stat: case PR_stat64: case PR_statfs: case PR_statfs64: return 0; /* Otherwise: restore the previous mode of the final component. */ default: node->mode = perms.st_mode; break; } } node->path = talloc_strdup(node, path); if (node->path == NULL) { /* Keep only consistent nodes. */ TALLOC_FREE(node); return 0; } /* The mode restoration works because Talloc destructors are * called in reverse order. */ talloc_set_destructor(node, restore_mode); (void) chmod(path, new_mode); } proot-3.0.2/src/extension/fake_id0/filter.h0000644000175000017500000000221512156552156020146 0ustar ivoireivoire/* This file was generated thanks to the following command: * * grep '^case PR_' enter.c exit.c host_path.c | cut -f 2 -d ' ' | cut -f 1 -d : | sort -u */ { PR_chmod, 0 }, { PR_chown, 0 }, { PR_chown32, 0 }, { PR_chroot, 0 }, { PR_fchmod, 0 }, { PR_fchmodat, 0 }, { PR_fchown, 0 }, { PR_fchown32, 0 }, { PR_fchownat, 0 }, { PR_fchownat, 0 }, { PR_fstat, 0 }, { PR_fstat, 0 }, { PR_fstat64, 0 }, { PR_fstatat64, 0 }, { PR_getegid, 0 }, { PR_getegid32, 0 }, { PR_geteuid, 0 }, { PR_geteuid32, 0 }, { PR_getgid, 0 }, { PR_getgid32, 0 }, { PR_getresgid, 0 }, { PR_getresgid32, 0 }, { PR_getresuid, 0 }, { PR_getresuid32, 0 }, { PR_getuid, 0 }, { PR_getuid32, 0 }, { PR_lchown, 0 }, { PR_lchown32, 0 }, { PR_lstat, 0 }, { PR_lstat64, 0 }, { PR_newfstatat, 0 }, { PR_oldlstat, 0 }, { PR_oldstat, 0 }, { PR_setfsgid, 0 }, { PR_setfsgid32, 0 }, { PR_setfsuid, 0 }, { PR_setfsuid32, 0 }, { PR_setgid, 0 }, { PR_setgid32, 0 }, { PR_setresgid, 0 }, { PR_setresgid32, 0 }, { PR_setresuid, 0 }, { PR_setresuid32, 0 }, { PR_setuid, 0 }, { PR_setuid32, 0 }, { PR_stat, 0 }, { PR_stat64, 0 }, { PR_statfs, 0 }, { PR_statfs64, 0 }, proot-3.0.2/src/extension/fake_id0/enter.c0000644000175000017500000000341612156552156017775 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ syscall_number = peek_reg(tracee, ORIGINAL, SYSARG_NUM); switch (syscall_number) { case PR_chroot: case PR_chmod: case PR_chown: case PR_fchmod: case PR_fchown: case PR_lchown: case PR_chown32: case PR_fchown32: case PR_lchown32: case PR_fchmodat: case PR_fchownat: case PR_fstatat64: case PR_newfstatat: case PR_stat64: case PR_lstat64: case PR_fstat64: case PR_stat: case PR_lstat: case PR_fstat: case PR_getuid: case PR_getgid: case PR_getegid: case PR_geteuid: case PR_getuid32: case PR_getgid32: case PR_geteuid32: case PR_getegid32: case PR_setuid: case PR_setgid: case PR_setfsuid: case PR_setfsgid: case PR_setuid32: case PR_setgid32: case PR_setfsuid32: case PR_setfsgid32: case PR_setresuid: case PR_setresgid: case PR_setresuid32: case PR_setresgid32: case PR_getresuid: case PR_getresuid32: case PR_getresgid: case PR_getresgid32: /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; default: return 0; } proot-3.0.2/src/extension/extension.h0000644000175000017500000001331512156552156017236 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef EXTENSION_H #define EXTENSION_H #include /* LIST_, */ #include /* intptr_t, */ #include /* bool, */ #include "tracee/tracee.h" #include "syscall/seccomp.h" /* List of possible events. */ typedef enum { /* A guest path passed as an argument of the current syscall * is about to be translated: "(char *) data1" is the base for * "(char *) data2" -- the guest path -- if this latter is * relative. If the extension returns > 0, then PRoot skips * its own handling. If the extension returns < 0, then PRoot * reports this errno as-is. */ GUEST_PATH, /* A canonicalized host path is being accessed during the * translation of a guest path: "(char *) data1" is the * canonicalized host path and "(bool) data2" is true if it is * the final path. Note that several host paths are accessed * for a given guest path since PRoot has to walk along all * parent directories and symlinks in order to translate it. * If the extension returns < 0, then PRoot reports this errno * as-is. */ HOST_PATH, /* The tracee enters a syscall, and PRoot hasn't do anything * yet. If the extension returns > 0, then PRoot skips its * own handling. If the extension returns < 0, then PRoot * reports this errno to the tracee. */ SYSCALL_ENTER_START, /* The tracee enters a syscall, and PRoot has already handled * it: "(int) data1" is the current status, it is < 0 when * something went wrong. If the extension returns < 0, then * PRoot reports this errno to the tracee. */ SYSCALL_ENTER_END, /* The tracee exits a syscall, and PRoot hasn't do anything * yet. If the extension returns > 0, then PRoot skips its * own handling. If the extension returns < 0, then PRoot * reports this errno to the tracee. */ SYSCALL_EXIT_START, /* The tracee exits a syscall, and PRoot has already handled * it. If the extension returns < 0, then PRoot reports this * errno to the tracee. */ SYSCALL_EXIT_END, /* The tracee is stopped either because of a syscall or a * signal: "(int) data1" is its new status as reported by * waitpid(2). If the extension returns != 0, then PRoot * skips its own handling. */ NEW_STATUS, /* Ask how this extension is inheritable: "(Tracee *) data1" is the * child tracee and "(bool) data2" specifies if it's a * sub-reconfiguration. The meaning of the returned value is: * * < 0 : not inheritable * == 0 : inheritable + shared configuration. * > 0 : inheritable + call INHERIT_CHILD. */ INHERIT_PARENT, /* Control the inheritance: "(Extension *) data1" is the extension of * the parent and "(bool) data2" specifies if it's a * sub-reconfiguration. For instance the extension in the child could * create a new configuration depending on the parent's * configuration. */ INHERIT_CHILD, /* Initialize the extension: "(const char *) data1" is its * argument that was passed to the command-line interface. If * the extension returns < 0, then PRoot removed it. */ INITIALIZATION, /* The extension is not attached to its tracee anymore * (destructor). */ REMOVED, /* Print the current configuration of the extension. See * print_config() as an example. */ PRINT_CONFIG, /* Print the usage of the extension: "(bool) data1" is true * for a detailed usage. See print_usage() as an example. */ PRINT_USAGE, } ExtensionEvent; struct extension; typedef int (*extension_callback_t)(struct extension *extension, ExtensionEvent event, intptr_t data1, intptr_t data2); typedef struct extension { /* Function to be called when any event occured. */ extension_callback_t callback; /* A chunk of memory allocated by any talloc functions. * Mainly useful to store a configuration. */ TALLOC_CTX *config; /* List of syscalls handled by this extension. */ const Filter *filters; /* Link to the next and previous extensions. Note the order * is *never* garantee. */ LIST_ENTRY(extension) link; } Extension; typedef LIST_HEAD(extensions, extension) Extensions; extern int initialize_extension(Tracee *tracee, extension_callback_t callback, const char *cli); extern void inherit_extensions(Tracee *child, Tracee *parent, bool sub_reconf); /** * Notify all extensions of @tracee that the given @event occured. * See ExtensionEvent for the meaning of @data1 and @data2. */ static inline int notify_extensions(Tracee *tracee, ExtensionEvent event, intptr_t data1, intptr_t data2) { Extension *extension; if (tracee->extensions == NULL) return 0; LIST_FOREACH(extension, tracee->extensions, link) { int status = extension->callback(extension, event, data1, data2); if (status != 0) return status; } return 0; } /* Built-in extensions. */ extern int kompat_callback(Extension *extension, ExtensionEvent event, intptr_t d1, intptr_t d2); extern int fake_id0_callback(Extension *extension, ExtensionEvent event, intptr_t d1, intptr_t d2); #endif /* EXTENSION_H */ proot-3.0.2/src/compat.h0000644000175000017500000000703512156552156014473 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef COMPAT_H #define COMPAT_H /* Local definitions for compatibility with old and/or broken distros... */ # ifndef AT_FDCWD # define AT_FDCWD -100 # endif # ifndef AT_SYMLINK_FOLLOW # define AT_SYMLINK_FOLLOW 0x400 # endif # ifndef AT_REMOVEDIR # define AT_REMOVEDIR 0x200 # endif # ifndef AT_SYMLINK_NOFOLLOW # define AT_SYMLINK_NOFOLLOW 0x100 # endif # ifndef IN_DONT_FOLLOW # define IN_DONT_FOLLOW 0x02000000 # endif # ifndef WIFCONTINUED # define WIFCONTINUED(status) ((status) == 0xffff) # endif # ifndef PTRACE_SETOPTIONS # define PTRACE_SETOPTIONS 0x4200 # endif # ifndef PTRACE_GETEVENTMSG # define PTRACE_GETEVENTMSG 0x4201 # endif # ifndef PTRACE_O_TRACESYSGOOD # define PTRACE_O_TRACESYSGOOD 0x00000001 # endif # ifndef PTRACE_O_TRACEFORK # define PTRACE_O_TRACEFORK 0x00000002 # endif # ifndef PTRACE_O_TRACEVFORK # define PTRACE_O_TRACEVFORK 0x00000004 # endif # ifndef PTRACE_O_TRACECLONE # define PTRACE_O_TRACECLONE 0x00000008 # endif # ifndef PTRACE_O_TRACEEXEC # define PTRACE_O_TRACEEXEC 0x00000010 # endif # ifndef PTRACE_O_TRACEVFORKDONE # define PTRACE_O_TRACEVFORKDONE 0x00000020 # endif # ifndef PTRACE_O_TRACEEXIT # define PTRACE_O_TRACEEXIT 0x00000040 # endif # ifndef PTRACE_O_TRACESECCOMP # define PTRACE_O_TRACESECCOMP 0 /* Optional */ # endif # ifndef PTRACE_EVENT_FORK # define PTRACE_EVENT_FORK 1 # endif # ifndef PTRACE_EVENT_VFORK # define PTRACE_EVENT_VFORK 2 # endif # ifndef PTRACE_EVENT_CLONE # define PTRACE_EVENT_CLONE 3 # endif # ifndef PTRACE_EVENT_EXEC # define PTRACE_EVENT_EXEC 4 # endif # ifndef PTRACE_EVENT_VFORK_DONE # define PTRACE_EVENT_VFORK_DONE 5 # endif # ifndef PTRACE_EVENT_EXIT # define PTRACE_EVENT_EXIT 6 # endif # ifndef PTRACE_EVENT_SECCOMP # define PTRACE_EVENT_SECCOMP 7 # endif # ifndef PTRACE_EVENT_SECCOMP2 # define PTRACE_EVENT_SECCOMP2 7 # endif # ifndef ADDR_NO_RANDOMIZE # define ADDR_NO_RANDOMIZE 0x0040000 # endif # ifndef SYS_ACCEPT4 # define SYS_ACCEPT4 18 # endif # ifndef TALLOC_FREE # define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx = NULL; } while(0) # endif # ifndef PR_SET_NO_NEW_PRIVS # define PR_SET_NO_NEW_PRIVS 38 # endif # ifndef PR_SET_SECCOMP # define PR_SET_SECCOMP 22 # endif # ifndef SECCOMP_MODE_FILTER # define SECCOMP_MODE_FILTER 2 # endif # ifndef talloc_get_type_abort # define talloc_get_type_abort talloc_get_type # endif #endif /* COMPAT_H */ proot-3.0.2/src/cli.c0000644000175000017500000004067512156552156013761 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _GNU_SOURCE /* get_current_dir_name(3), */ #include /* printf(3), */ #include /* string(3), */ #include /* exit(3), strtol(3), */ #include /* bool, true, false, */ #include /* assert(3), */ #include /* wait(2), */ #include /* stat(2), */ #include /* stat(2), */ #include /* stat(2), */ #include /* errno(3), */ #include /* PATH_MAX, ARG_MAX, */ #include /* LIST_*, */ #include /* talloc_*, */ #include "cli.h" #include "notice.h" #include "path/path.h" #include "path/canon.h" #include "path/binding.h" #include "tracee/tracee.h" #include "tracee/event.h" #include "extension/extension.h" #include "build.h" #include "attribute.h" #include "compat.h" static int handle_option_r(Tracee *tracee, char *value) { Binding *binding; /* ``chroot $PATH`` is semantically equivalent to ``mount * --bind $PATH /``. */ binding = new_binding(tracee, value, "/", true); if (binding == NULL) return -1; return 0; } static int handle_option_b(Tracee *tracee, char *value) { char *ptr = strchr(value, ':'); if (ptr != NULL) { *ptr = '\0'; ptr++; } new_binding(tracee, value, ptr, true); return 0; } static int handle_option_q(Tracee *tracee, char *value) { size_t nb_args; char *ptr; size_t i; nb_args = 0; ptr = value; while (1) { nb_args++; /* Keep consecutive non-space characters. */ while (*ptr != ' ' && *ptr != '\0') ptr++; /* End-of-string ? */ if (*ptr == '\0') break; /* Skip consecutive space separators. */ while (*ptr == ' ' && *ptr != '\0') ptr++; /* End-of-string ? */ if (*ptr == '\0') break; } tracee->qemu = talloc_zero_array(tracee, char *, nb_args + 1); if (tracee->qemu == NULL) return -1; talloc_set_name_const(tracee->qemu, "@qemu"); i = 0; ptr = value; while (1) { tracee->qemu[i] = ptr; i++; /* Keep consecutive non-space characters. */ while (*ptr != ' ' && *ptr != '\0') ptr++; /* End-of-string ? */ if (*ptr == '\0') break; /* Remove consecutive space separators. */ while (*ptr == ' ' && *ptr != '\0') *ptr++ = '\0'; /* End-of-string ? */ if (*ptr == '\0') break; } assert(i == nb_args); new_binding(tracee, "/", HOST_ROOTFS, true); new_binding(tracee, "/dev/null", "/etc/ld.so.preload", false); return 0; } static int handle_option_w(Tracee *tracee, char *value) { tracee->fs->cwd = talloc_strdup(tracee->fs, value); if (tracee->fs->cwd == NULL) return -1; talloc_set_name_const(tracee->fs->cwd, "$cwd"); return 0; } static int handle_option_k(Tracee *tracee, char *value) { int status; status = initialize_extension(tracee, kompat_callback, value); if (status < 0) notice(tracee, WARNING, INTERNAL, "option \"-k %s\" discarded", value); return 0; } static int handle_option_0(Tracee *tracee, char *value) { (void) initialize_extension(tracee, fake_id0_callback, value); return 0; } static int handle_option_v(Tracee *tracee, char *value) { char *end_ptr = NULL; errno = 0; tracee->verbose = strtol(value, &end_ptr, 10); if (errno != 0 || end_ptr == value) { notice(tracee, ERROR, USER, "option `-v` expects an integer value."); return -1; } return 0; } static bool exit_failure = true; static int handle_option_V(Tracee *tracee UNUSED, char *value UNUSED) { printf("PRoot %s: %s.\n", version, subtitle); printf("built-in accelerators: process_vm = %s, seccomp_filter = %s", #if defined(HAVE_PROCESS_VM) "yes", #else "no", #endif #if defined(HAVE_SECCOMP_FILTER) "yes" #else "no" #endif ); printf("\n%s\n", colophon); exit_failure = false; return -1; } static void print_usage(Tracee *, bool); static int handle_option_h(Tracee *tracee, char *value UNUSED) { print_usage(tracee, true); exit_failure = false; return -1; } static int handle_option_B(Tracee *tracee, char *value UNUSED) { int i; for (i = 0; recommended_bindings[i] != NULL; i++) new_binding(tracee, recommended_bindings[i], NULL, false); return 0; } static int handle_option_Q(Tracee *tracee, char *value) { int status; status = handle_option_q(tracee, value); if (status < 0) return status; status = handle_option_B(tracee, NULL); if (status < 0) return status; return 0; } #define NB_OPTIONS (sizeof(options) / sizeof(Option)) /** * Print a (@detailed) usage of PRoot. */ static void print_usage(Tracee *tracee, bool detailed) { const char *current_class = "none"; size_t i, j; #define DETAIL(a) if (detailed) a DETAIL(printf("PRoot %s: %s.\n\n", version, subtitle)); printf("Usage:\n %s\n", synopsis); DETAIL(printf("\n")); for (i = 0; i < NB_OPTIONS; i++) { for (j = 0; ; j++) { Argument *argument = &(options[i].arguments[j]); if (!argument->name || (!detailed && j != 0)) { DETAIL(printf("\n")); printf("\t%s\n", options[i].description); if (detailed) { if (options[i].detail[0] != '\0') printf("\n%s\n\n", options[i].detail); else printf("\n"); } break; } if (strcmp(options[i].class, current_class) != 0) { current_class = options[i].class; printf("\n%s:\n", current_class); } if (j == 0) printf(" %s", argument->name); else printf(", %s", argument->name); if (argument->separator != '\0') printf("%c*%s*", argument->separator, argument->value); else if (!detailed) printf("\t"); } } notify_extensions(tracee, PRINT_USAGE, detailed, 0); if (detailed) printf("%s\n", colophon); } static void print_execve_help(const Tracee *tracee, const char *argv0) { notice(tracee, WARNING, SYSTEM, "execve(\"%s\")", argv0); notice(tracee, INFO, USER, "possible causes:\n" " * is a script but its interpreter (eg. /bin/sh) was not found;\n" " * is an ELF but its interpreter (eg. ld-linux.so) was not found;\n" " * is a foreign binary but no was specified;\n" " * does not work correctly (if specified)."); } static void print_error_separator(const Tracee *tracee, Argument *argument) { if (argument->separator == '\0') notice(tracee, ERROR, USER, "option '%s' expects no value.", argument->name); else notice(tracee, ERROR, USER, "option '%s' and its value must be separated by '%c'.", argument->name, argument->separator); } static void print_argv(const Tracee *tracee, const char *prompt, char **argv) { char string[ARG_MAX] = ""; size_t i; if (!argv) return; void append(const char *post) { ssize_t length = sizeof(string) - (strlen(string) + strlen(post)); if (length <= 0) return; strncat(string, post, length); } append(prompt); append(" ="); for (i = 0; argv[i] != NULL; i++) { append(" "); append(argv[i]); } string[sizeof(string) - 1] = '\0'; notice(tracee, INFO, USER, "%s", string); } static void print_config(Tracee *tracee) { assert(tracee != NULL); if (tracee->verbose <= 0) return; if (tracee->qemu) notice(tracee, INFO, USER, "host rootfs = %s", HOST_ROOTFS); if (tracee->glue) notice(tracee, INFO, USER, "glue rootfs = %s", tracee->glue); notice(tracee, INFO, USER, "exe = %s", tracee->exe); print_argv(tracee, "command", tracee->cmdline); print_argv(tracee, "qemu", tracee->qemu); notice(tracee, INFO, USER, "initial cwd = %s", tracee->fs->cwd); notice(tracee, INFO, USER, "verbose level = %d", tracee->verbose); notify_extensions(tracee, PRINT_CONFIG, 0, 0); } /** * Initialize @tracee's current working directory. This function * returns -1 if an error occurred, otherwise 0. */ static int initialize_cwd(Tracee *tracee) { char path2[PATH_MAX]; char path[PATH_MAX]; int status; /* Default to "." if none were specified. */ if (tracee->fs->cwd == NULL) { status = handle_option_w(tracee, "."); if (status < 0) return -1; } /* Compute the base directory. */ if (tracee->fs->cwd[0] != '/') { status = getcwd2(tracee->reconf.tracee, path); if (status < 0) { notice(tracee, ERROR, INTERNAL, "getcwd: %s", strerror(-status)); return -1; } } else strcpy(path, "/"); /* The ending "." ensures canonicalize() will report an error * if tracee->fs->cwd does not exist or if it is not a * directory. */ status = join_paths(3, path2, path, tracee->fs->cwd, "."); if (status < 0) { notice(tracee, ERROR, INTERNAL, "getcwd: %s", strerror(-status)); return -1; } /* Initiale state for canonicalization. */ strcpy(path, "/"); status = canonicalize(tracee, path2, true, path, 0); if (status < 0) { notice(tracee, WARNING, USER, "can't chdir(\"%s\") in the guest rootfs: %s", path2, strerror(-status)); notice(tracee, INFO, USER, "default working directory is now \"/\""); strcpy(path, "/"); } chop_finality(path); /* Replace with the canonicalized working directory. */ TALLOC_FREE(tracee->fs->cwd); tracee->fs->cwd = talloc_strdup(tracee->fs, path); if (tracee->fs->cwd == NULL) return -1; talloc_set_name_const(tracee->fs->cwd, "$cwd"); return 0; } /** * Initialize @tracee->exe and @tracee->cmdline from @cmdline, and resolve the * full host path to @command@tracee->qemu[0]. */ static int initialize_command(Tracee *tracee, char *const *cmdline) { char path[PATH_MAX]; int status; int i; /* How many arguments? */ for (i = 0; cmdline[i] != NULL; i++) ; tracee->cmdline = talloc_zero_array(tracee, char *, i + 1); if (tracee->cmdline == NULL) { notice(tracee, ERROR, INTERNAL, "talloc_zero_array() failed"); return -1; } talloc_set_name_const(tracee->cmdline, "@cmdline"); for (i = 0; cmdline[i] != NULL; i++) tracee->cmdline[i] = cmdline[i]; /* Resolve the full guest path to tracee->cmdline[0]. */ status = which(tracee, tracee->reconf.paths, path, tracee->cmdline[0]); if (status < 0) return -1; status = detranslate_path(tracee, path, NULL); if (status < 0) return -1; tracee->exe = talloc_strdup(tracee, path); if (tracee->exe == NULL) return -1; talloc_set_name_const(tracee->exe, "$exe"); /* Nothing else to do ? */ if (tracee->qemu == NULL) return 0; /* Resolve the full guest path to tracee->qemu[0]. */ status = which(tracee->reconf.tracee, tracee->reconf.paths, path, tracee->qemu[0]); if (status < 0) return -1; /* Actually tracee->qemu[0] has to be a host path from the tracee's * point-of-view, not from the PRoot's point-of-view. See * translate_execve() for details. */ if (tracee->reconf.tracee != NULL) { status = detranslate_path(tracee->reconf.tracee, path, NULL); if (status < 0) return -1; } tracee->qemu[0] = talloc_strdup(tracee->qemu, path); if (tracee->qemu[0] == NULL) return -1; /** * There's a bug when using the ELF interpreter as a loader (as PRoot * does) on PIE programs that uses constructors (typically QEMU v1.1+). * In this case, constructors are called twice as you can see on the * test below: * * $ cat test.c * static void __attribute__((constructor)) init(void) { puts("OK"); } * int main() { return 0; } * * $ gcc -fPIC -pie test.c -o test * $ ./test * OK * * $ /lib64/ld-linux-x86-64.so.2 ./test * OK * OK * * Actually, PRoot doesn't have to use the ELF interpreter as a loader * if QEMU isn't nested. When QEMU is nested (sub reconfiguration), the * user has to use either a version of QEMU prior v1.1 or a version of * QEMU compiled with the --disable-pie option. */ tracee->qemu_pie_workaround = (tracee->reconf.tracee == NULL); return 0; } static char *default_command[] = { "/bin/sh", NULL }; /** * Configure @tracee according to the command-line arguments stored in * @argv[]. This function returns -1 if an error occured, otherwise * 0. */ int parse_config(Tracee *tracee, size_t argc, char *argv[]) { option_handler_t handler = NULL; size_t i, j, k; int status; if (argc == 1) { print_usage(tracee, false); return -1; } for (i = 1; i < argc; i++) { char *arg = argv[i]; /* The current argument is the value of a short option. */ if (handler != NULL) { status = handler(tracee, arg); if (status < 0) return status; handler = NULL; continue; } if (arg[0] != '-') break; /* End of PRoot options. */ for (j = 0; j < NB_OPTIONS; j++) { Option *option = &options[j]; /* A given option has several aliases. */ for (k = 0; ; k++) { Argument *argument; size_t length; argument = &option->arguments[k]; /* End of aliases for this option. */ if (!argument->name) break; length = strlen(argument->name); if (strncmp(arg, argument->name, length) != 0) continue; /* Avoid ambiguities. */ if (strlen(arg) > length && arg[length] != argument->separator) { print_error_separator(tracee, argument); return -1; } /* No option value. */ if (!argument->value) { status = option->handler(tracee, arg); if (status < 0) return -1; goto known_option; } /* Value coalesced with to its option. */ if (argument->separator == arg[length]) { assert(strlen(arg) >= length); status = option->handler(tracee, &arg[length + 1]); if (status < 0) return -1; goto known_option; } /* Avoid ambiguities. */ if (argument->separator != ' ') { print_error_separator(tracee, argument); return -1; } /* Short option with a separated value. */ handler = option->handler; goto known_option; } } notice(tracee, ERROR, USER, "unknown option '%s'.", arg); return -1; known_option: if (handler != NULL && i == argc - 1) { notice(tracee, ERROR, USER, "missing value for option '%s'.", arg); return -1; } } /* When no guest rootfs were specified: if the first bare * option is a directory, then the old command-line interface * (similar to the chroot one) is expected. Otherwise this is * the new command-line interface where the default guest * rootfs is "/". */ if (get_root(tracee) == NULL) { char path[PATH_MAX]; struct stat buf; if (argv[i] != NULL && realpath2(tracee->reconf.tracee, path, argv[i], true) == 0 && stat(path, &buf) == 0 && S_ISDIR(buf.st_mode)) { status = handle_option_r(tracee, argv[i]); i++; } else status = handle_option_r(tracee, "/"); if (status < 0) return -1; } /* The guest rootfs is now known: bindings specified by the * user (tracee->bindings.user) can be canonicalized. */ status = initialize_bindings(tracee); if (status < 0) return -1; /* Bindings are now installed (tracee->bindings.guest & * tracee->bindings.host): the current working directory can * be canonicalized. */ status = initialize_cwd(tracee); if (status < 0) return -1; /* Bindings are now installed and the current working directory is * canonicalized: resolve path to @tracee->exe and @tracee->qemu. */ status = initialize_command(tracee, i < argc ? &argv[i] : default_command); if (status < 0) return -1; print_config(tracee); return 0; } int main(int argc, char *argv[]) { Tracee *tracee; int status; /* Configure the memory allocator. */ talloc_enable_leak_report(); #if defined(TALLOC_VERSION_MAJOR) && TALLOC_VERSION_MAJOR >= 2 talloc_set_log_stderr(); #endif /* Pre-create the first tracee (pid == 0). */ tracee = get_tracee(NULL, 0, true); if (tracee == NULL) goto error; tracee->pid = getpid(); /* Pre-configure the first tracee. */ status = parse_config(tracee, argc, argv); if (status < 0) goto error; /* Start the first tracee. */ status = launch_process(tracee); if (status < 0) { print_execve_help(tracee, tracee->exe); goto error; } /* Start tracing the first tracee and all its children. */ exit(event_loop()); error: TALLOC_FREE(tracee); if (exit_failure) { fprintf(stderr, "proot error: see `proot --help` or `man proot`.\n"); exit(EXIT_FAILURE); } else exit(EXIT_SUCCESS); } proot-3.0.2/src/syscall/0000755000175000017500000000000012156552156014504 5ustar ivoireivoireproot-3.0.2/src/syscall/seccomp.h0000644000175000017500000000233212156552156016306 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef SECCOMP_H #define SECCOMP_H #include "tracee/tracee.h" #include "attribute.h" #include "arch.h" typedef struct { word_t value; int flag; } FilteredSyscall; typedef struct { int architecture; const FilteredSyscall *syscalls; } Filter; #define FILTERED_SYSCALL_END { SYSCALL_AVOIDER, -1 } extern int enable_syscall_filtering(const Tracee *tracee); #endif /* SECCOMP_H */ proot-3.0.2/src/syscall/exit.c0000644000175000017500000001517212156552156015627 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ /* This file is included by syscall.c, it can return two kinds of * status code: * * - break: update the syscall result register with "status" * * - goto end: nothing else to do. */ syscall_number = peek_reg(tracee, ORIGINAL, SYSARG_NUM); syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); switch (syscall_number) { case PR_getcwd: { size_t new_size; size_t size; word_t output; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; output = peek_reg(tracee, ORIGINAL, SYSARG_1); size = (size_t) peek_reg(tracee, ORIGINAL, SYSARG_2); if (size == 0) { status = -EINVAL; break; } new_size = strlen(tracee->fs->cwd) + 1; if (size < new_size) { status = -ERANGE; break; } /* Overwrite the path. */ status = write_data(tracee, output, tracee->fs->cwd, new_size); if (status < 0) break; /* The value of "status" is used to update the returned value * in translate_syscall_exit(). */ status = new_size; break; } case PR_accept: case PR_accept4: case PR_getsockname: case PR_getpeername: { word_t sock_addr; word_t size_addr; word_t max_size; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; sock_addr = peek_reg(tracee, ORIGINAL, SYSARG_2); size_addr = peek_reg(tracee, MODIFIED, SYSARG_3); max_size = peek_reg(tracee, MODIFIED, SYSARG_6); status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size); if (status < 0) break; /* Don't overwrite the syscall result. */ goto end; } case PR_socketcall: { word_t args_addr; word_t sock_addr; word_t size_addr; word_t max_size; args_addr = peek_reg(tracee, ORIGINAL, SYSARG_2); switch (peek_reg(tracee, ORIGINAL, SYSARG_1)) { case SYS_ACCEPT: case SYS_ACCEPT4: case SYS_GETSOCKNAME: case SYS_GETPEERNAME: /* Handle these cases below. */ status = 1; break; case SYS_BIND: case SYS_CONNECT: /* Restore the initial parameters: this memory was * overwritten at the enter stage. Remember: POKE_MEM * puts -errno in status and breaks if an error * occured. */ POKE_MEM(SYSARG_ADDR(2), peek_reg(tracee, MODIFIED, SYSARG_5)); POKE_MEM(SYSARG_ADDR(3), peek_reg(tracee, MODIFIED, SYSARG_6)); status = 0; break; default: status = 0; break; } /* Error reported by the kernel or there's nothing else to do. */ if ((int) syscall_result < 0 || status == 0) goto end; /* An error occured in SYS_BIND or SYS_CONNECT. */ if (status < 0) break; /* Remember: PEEK_MEM puts -errno in status and breaks if an * error occured. */ sock_addr = PEEK_MEM(SYSARG_ADDR(2)); size_addr = PEEK_MEM(SYSARG_ADDR(3)); max_size = peek_reg(tracee, MODIFIED, SYSARG_6); status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size); if (status < 0) break; /* Don't overwrite the syscall result. */ goto end; } case PR_fchdir: case PR_chdir: /* These syscalls are fully emulated, see enter.c for details * (like errors). */ status = 0; break; case PR_readlink: case PR_readlinkat: { char referee[PATH_MAX]; char referer[PATH_MAX]; size_t old_size; size_t new_size; size_t max_size; word_t input; word_t output; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; old_size = syscall_result; if (syscall_number == PR_readlink) { output = peek_reg(tracee, ORIGINAL, SYSARG_2); max_size = peek_reg(tracee, ORIGINAL, SYSARG_3); input = peek_reg(tracee, MODIFIED, SYSARG_1); } else { output = peek_reg(tracee, ORIGINAL, SYSARG_3); max_size = peek_reg(tracee, ORIGINAL, SYSARG_4); input = peek_reg(tracee, MODIFIED, SYSARG_2); } if (max_size > PATH_MAX) max_size = PATH_MAX; if (max_size == 0) { status = -EINVAL; break; } /* The kernel does NOT put the NULL terminating byte for * readlink(2). */ status = read_data(tracee, referee, output, old_size); if (status < 0) break; referee[old_size] = '\0'; /* Not optimal but safe (path is fully translated). */ status = read_string(tracee, referer, input, PATH_MAX); if (status < 0) break; if (status >= PATH_MAX) { status = -ENAMETOOLONG; break; } status = detranslate_path(tracee, referee, referer); if (status < 0) break; /* The original path doesn't require any transformation, i.e * it is a symetric binding. */ if (status == 0) goto end; /* Overwrite the path. Note: the output buffer might be * initialized with zeros but it was updated with the kernel * result, and then with the detranslated result. This later * might be shorter than the former, so it's safier to add a * NULL terminating byte when possible. This problem was * exposed by IDA Demo 6.3. */ if ((size_t) status < max_size) { new_size = status - 1; status = write_data(tracee, output, referee, status); } else { new_size = max_size; status = write_data(tracee, output, referee, max_size); } if (status < 0) break; /* The value of "status" is used to update the returned value * in translate_syscall_exit(). */ status = new_size; break; } #if defined(ARCH_X86_64) case PR_uname: { struct utsname utsname; word_t address; size_t size; if (get_abi(tracee) != ABI_2) goto end; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; address = peek_reg(tracee, ORIGINAL, SYSARG_1); status = read_data(tracee, &utsname, address, sizeof(utsname)); if (status < 0) break; /* Some 32-bit programs like package managers can be * confused when the kernel reports "x86_64". */ size = sizeof(utsname.machine); strncpy(utsname.machine, "i686", size); utsname.machine[size - 1] = '\0'; status = write_data(tracee, address, &utsname, sizeof(utsname)); if (status < 0) break; status = 0; break; } #endif case PR_execve: if ((int) syscall_result >= 0) { case PR_rt_sigreturn: case PR_sigreturn: tracee->restore_original_regs = false; } goto end; default: goto end; } proot-3.0.2/src/syscall/socket.c0000644000175000017500000001310612156552156016141 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* offsetof(3), */ #include /* bzero(3), */ #include /* strncpy(3), strlen(3), */ #include /* assert(3), */ #include /* E*, */ #include /* struct sockaddr_un, AF_UNIX, */ #include /* struct sockaddr_un, */ #include "syscall/socket.h" #include "tracee/tracee.h" #include "tracee/mem.h" #include "path/path.h" #include "arch.h" /* The sockaddr_un structure has exactly the same layout on all * architectures. */ static const off_t offsetof_path = offsetof(struct sockaddr_un, sun_path); static struct sockaddr_un sockaddr_un__; static const size_t sizeof_path = sizeof(sockaddr_un__.sun_path); /** * Copy in @sockaddr the struct sockaddr_un stored in the @tracee * memory at the given @address. Also, its pathname is copied to the * null-terminated @path. Only @size bytes are read from the @tracee * memory (should be <= @max_size <= sizeof(struct sockaddr_un)). * This function returns -errno if an error occurred, 0 if the * structure was not found (not a sockaddr_un or @size > @max_size), * otherwise 1. */ static int read_sockaddr_un(Tracee *tracee, struct sockaddr_un *sockaddr, word_t max_size, char path[PATH_MAX], word_t address, int size) { int status; assert(max_size <= sizeof(struct sockaddr_un)); /* Nothing to do if the sockaddr has an unexpected size. */ if (size <= offsetof_path || (word_t) size > max_size) return 0; bzero(sockaddr, sizeof(struct sockaddr_un)); status = read_data(tracee, sockaddr, address, size); if (status < 0) return status; /* Nothing to do if it's not a named Unix domain socket. */ if ((sockaddr->sun_family != AF_UNIX) || sockaddr->sun_path[0] == '\0') return 0; /* Be careful: sun_path doesn't have to be null-terminated. */ assert(sizeof_path < PATH_MAX - 1); strncpy(path, sockaddr->sun_path, sizeof_path); path[sizeof_path] = '\0'; return 1; } /** * Translate the pathname of the struct sockaddr_un currently stored * in the @tracee memory at the given @address. See the documentation * of read_sockaddr_un() for the meaning of the @size parameter. * Also, the new address of the translated sockaddr_un is put in the * @address parameter. This function returns -errno if an error * occurred, otherwise 0. */ int translate_socketcall_enter(Tracee *tracee, word_t *address, int size) { struct sockaddr_un sockaddr; char path2[PATH_MAX]; char path[PATH_MAX]; int status; if (*address == 0) return 0; status = read_sockaddr_un(tracee, &sockaddr, sizeof(sockaddr), path2, *address, size); if (status <= 0) return status; status = translate_path(tracee, path, AT_FDCWD, path2, true); if (status < 0) return status; /* Be careful: sun_path doesn't have to be null-terminated. */ if (strlen(path) > sizeof_path) return -EINVAL; strncpy(sockaddr.sun_path, path, sizeof_path); /* Push the updated sockaddr to a newly allocated space. */ *address = alloc_mem(tracee, sizeof(sockaddr)); if (*address == 0) return -EFAULT; status = write_data(tracee, *address, &sockaddr, sizeof(sockaddr)); if (status < 0) return status; return 1; } #if !defined(MIN) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /** * Detranslate the pathname of the struct sockaddr_un currently stored * in the @tracee memory at the given @sock_addr. See the * documentation of read_sockaddr_un() for the meaning of the * @size_addr and @max_size parameters. This function returns -errno * if an error occurred, otherwise 0. */ int translate_socketcall_exit(Tracee *tracee, word_t sock_addr, word_t size_addr, word_t max_size) { struct sockaddr_un sockaddr; bool is_truncated = false; char path[PATH_MAX]; int status; int size; if (sock_addr == 0) return 0; size = (int) peek_mem(tracee, size_addr); if (errno != 0) return -errno; max_size = MIN(max_size, sizeof(sockaddr)); status = read_sockaddr_un(tracee, &sockaddr, max_size, path, sock_addr, size); if (status <= 0) return status; status = detranslate_path(tracee, path, NULL); if (status < 0) return status; /* Be careful: sun_path doesn't have to be null-terminated. */ size = offsetof_path + strlen(path) + 1; if (size < 0 || (word_t) size > max_size) { size = max_size; is_truncated = true; } strncpy(sockaddr.sun_path, path, sizeof_path); /* Overwrite the sockaddr and socklen parameters. */ status = write_data(tracee, sock_addr, &sockaddr, size); if (status < 0) return status; /* If sockaddr is truncated (because the buffer provided is * too small), addrlen will return a value greater than was * supplied to the call. See man 2 accept. */ if (is_truncated) size = max_size + 1; status = write_data(tracee, size_addr, &size, sizeof(size)); if (status < 0) return status; return 0; } proot-3.0.2/src/syscall/syscall.h0000644000175000017500000000253612156552156016335 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef SYSCALL_H #define SYSCALL_H #include /* PATH_MAX, */ #include /* bool ,true, false, */ #include "arch.h" /* word_t */ #include "tracee/tracee.h" #include "tracee/reg.h" extern void translate_syscall(Tracee *tracee); extern int get_sysarg_path(const Tracee *tracee, char path[PATH_MAX], Reg reg); extern int set_sysarg_path(Tracee *tracee, char path[PATH_MAX], Reg reg); extern int set_sysarg_data(Tracee *tracee, void *tracer_ptr, word_t size, Reg reg); #endif /* SYSCALL_H */ proot-3.0.2/src/syscall/sysnum-arm.h0000644000175000017500000002545112156552156016777 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file was generated thanks to the following command: * * cpp -dM -D__ARM_EABI__=1 linux/arch/arm/include/asm/unistd.h | grep '#define __NR_' | sed s/__NR_/PR_/g | sort -u */ #include "syscall/sysnum-undefined.h" #define PR__llseek 140 #define PR__newselect 142 #define PR__sysctl 149 #define PR_accept 285 #define PR_accept4 366 #define PR_access 33 #define PR_acct 51 #define PR_add_key 309 #define PR_adjtimex 124 #define PR_arm_fadvise64_64 270 #define PR_arm_sync_file_range 341 #define PR_bdflush 134 #define PR_bind 282 #define PR_brk 45 #define PR_capget 184 #define PR_capset 185 #define PR_chdir 12 #define PR_chmod 15 #define PR_chown 182 #define PR_chown32 212 #define PR_chroot 61 #define PR_clock_adjtime 372 #define PR_clock_getres 264 #define PR_clock_gettime 263 #define PR_clock_nanosleep 265 #define PR_clock_settime 262 #define PR_clone 120 #define PR_close 6 #define PR_connect 283 #define PR_creat 8 #define PR_delete_module 129 #define PR_dup 41 #define PR_dup2 63 #define PR_dup3 358 #define PR_epoll_create 250 #define PR_epoll_create1 357 #define PR_epoll_ctl 251 #define PR_epoll_pwait 346 #define PR_epoll_wait 252 #define PR_eventfd 351 #define PR_eventfd2 356 #define PR_execve 11 #define PR_exit 1 #define PR_exit_group 248 #define PR_faccessat 334 #define PR_fallocate 352 #define PR_fanotify_init 367 #define PR_fanotify_mark 368 #define PR_fchdir 133 #define PR_fchmod 94 #define PR_fchmodat 333 #define PR_fchown 95 #define PR_fchown32 207 #define PR_fchownat 325 #define PR_fcntl 55 #define PR_fcntl64 221 #define PR_fdatasync 148 #define PR_fgetxattr 231 #define PR_flistxattr 234 #define PR_flock 143 #define PR_fork 2 #define PR_fremovexattr 237 #define PR_fsetxattr 228 #define PR_fstat 108 #define PR_fstat64 197 #define PR_fstatat64 327 #define PR_fstatfs 100 #define PR_fstatfs64 267 #define PR_fsync 118 #define PR_ftruncate 93 #define PR_ftruncate64 194 #define PR_futex 240 #define PR_futimesat 326 #define PR_get_mempolicy 320 #define PR_get_robust_list 339 #define PR_getcpu 345 #define PR_getcwd 183 #define PR_getdents 141 #define PR_getdents64 217 #define PR_getegid 50 #define PR_getegid32 202 #define PR_geteuid 49 #define PR_geteuid32 201 #define PR_getgid 47 #define PR_getgid32 200 #define PR_getgroups 80 #define PR_getgroups32 205 #define PR_getitimer 105 #define PR_getpeername 287 #define PR_getpgid 132 #define PR_getpgrp 65 #define PR_getpid 20 #define PR_getppid 64 #define PR_getpriority 96 #define PR_getresgid 171 #define PR_getresgid32 211 #define PR_getresuid 165 #define PR_getresuid32 209 #define PR_getrusage 77 #define PR_getsid 147 #define PR_getsockname 286 #define PR_getsockopt 295 #define PR_gettid 224 #define PR_gettimeofday 78 #define PR_getuid 24 #define PR_getuid32 199 #define PR_getxattr 229 #define PR_init_module 128 #define PR_inotify_add_watch 317 #define PR_inotify_init 316 #define PR_inotify_init1 360 #define PR_inotify_rm_watch 318 #define PR_io_cancel 247 #define PR_io_destroy 244 #define PR_io_getevents 245 #define PR_io_setup 243 #define PR_io_submit 246 #define PR_ioctl 54 #define PR_ioprio_get 315 #define PR_ioprio_set 314 #define PR_kexec_load 347 #define PR_keyctl 311 #define PR_kill 37 #define PR_lchown 16 #define PR_lchown32 198 #define PR_lgetxattr 230 #define PR_link 9 #define PR_linkat 330 #define PR_listen 284 #define PR_listxattr 232 #define PR_llistxattr 233 #define PR_lookup_dcookie 249 #define PR_lremovexattr 236 #define PR_lseek 19 #define PR_lsetxattr 227 #define PR_lstat 107 #define PR_lstat64 196 #define PR_madvise 220 #define PR_mbind 319 #define PR_mincore 219 #define PR_mkdir 39 #define PR_mkdirat 323 #define PR_mknod 14 #define PR_mknodat 324 #define PR_mlock 150 #define PR_mlockall 152 #define PR_mmap2 192 #define PR_mount 21 #define PR_move_pages 344 #define PR_mprotect 125 #define PR_mq_getsetattr 279 #define PR_mq_notify 278 #define PR_mq_open 274 #define PR_mq_timedreceive 277 #define PR_mq_timedsend 276 #define PR_mq_unlink 275 #define PR_mremap 163 #define PR_msgctl 304 #define PR_msgget 303 #define PR_msgrcv 302 #define PR_msgsnd 301 #define PR_msync 144 #define PR_munlock 151 #define PR_munlockall 153 #define PR_munmap 91 #define PR_name_to_handle_at 370 #define PR_nanosleep 162 #define PR_nfsservctl 169 #define PR_nice 34 #define PR_open 5 #define PR_open_by_handle_at 371 #define PR_openat 322 #define PR_pause 29 #define PR_pciconfig_iobase 271 #define PR_pciconfig_read 272 #define PR_pciconfig_write 273 #define PR_perf_event_open 364 #define PR_personality 136 #define PR_pipe 42 #define PR_pipe2 359 #define PR_pivot_root 218 #define PR_poll 168 #define PR_ppoll 336 #define PR_prctl 172 #define PR_pread64 180 #define PR_preadv 361 #define PR_prlimit64 369 #define PR_process_vm_readv 376 #define PR_process_vm_writev 377 #define PR_pselect6 335 #define PR_ptrace 26 #define PR_pwrite64 181 #define PR_pwritev 362 #define PR_quotactl 131 #define PR_read 3 #define PR_readahead 225 #define PR_readlink 85 #define PR_readlinkat 332 #define PR_readv 145 #define PR_reboot 88 #define PR_recv 291 #define PR_recvfrom 292 #define PR_recvmmsg 365 #define PR_recvmsg 297 #define PR_remap_file_pages 253 #define PR_removexattr 235 #define PR_rename 38 #define PR_renameat 329 #define PR_request_key 310 #define PR_restart_syscall 0 #define PR_rmdir 40 #define PR_rt_sigaction 174 #define PR_rt_sigpending 176 #define PR_rt_sigprocmask 175 #define PR_rt_sigqueueinfo 178 #define PR_rt_sigreturn 173 #define PR_rt_sigsuspend 179 #define PR_rt_sigtimedwait 177 #define PR_rt_tgsigqueueinfo 363 #define PR_sched_get_priority_max 159 #define PR_sched_get_priority_min 160 #define PR_sched_getaffinity 242 #define PR_sched_getparam 155 #define PR_sched_getscheduler 157 #define PR_sched_rr_get_interval 161 #define PR_sched_setaffinity 241 #define PR_sched_setparam 154 #define PR_sched_setscheduler 156 #define PR_sched_yield 158 #define PR_semctl 300 #define PR_semget 299 #define PR_semop 298 #define PR_semtimedop 312 #define PR_send 289 #define PR_sendfile 187 #define PR_sendfile64 239 #define PR_sendmmsg 374 #define PR_sendmsg 296 #define PR_sendto 290 #define PR_set_mempolicy 321 #define PR_set_robust_list 338 #define PR_set_tid_address 256 #define PR_setdomainname 121 #define PR_setfsgid 139 #define PR_setfsgid32 216 #define PR_setfsuid 138 #define PR_setfsuid32 215 #define PR_setgid 46 #define PR_setgid32 214 #define PR_setgroups 81 #define PR_setgroups32 206 #define PR_sethostname 74 #define PR_setitimer 104 #define PR_setns 375 #define PR_setpgid 57 #define PR_setpriority 97 #define PR_setregid 71 #define PR_setregid32 204 #define PR_setresgid 170 #define PR_setresgid32 210 #define PR_setresuid 164 #define PR_setresuid32 208 #define PR_setreuid 70 #define PR_setreuid32 203 #define PR_setrlimit 75 #define PR_setsid 66 #define PR_setsockopt 294 #define PR_settimeofday 79 #define PR_setuid 23 #define PR_setuid32 213 #define PR_setxattr 226 #define PR_shmat 305 #define PR_shmctl 308 #define PR_shmdt 306 #define PR_shmget 307 #define PR_shutdown 293 #define PR_sigaction 67 #define PR_sigaltstack 186 #define PR_signalfd 349 #define PR_signalfd4 355 #define PR_sigpending 73 #define PR_sigprocmask 126 #define PR_sigreturn 119 #define PR_sigsuspend 72 #define PR_socket 281 #define PR_socketpair 288 #define PR_splice 340 #define PR_stat 106 #define PR_stat64 195 #define PR_statfs 99 #define PR_statfs64 266 #define PR_swapoff 115 #define PR_swapon 87 #define PR_symlink 83 #define PR_symlinkat 331 #define PR_sync 36 #define PR_syncfs 373 #define PR_sysfs 135 #define PR_sysinfo 116 #define PR_syslog 103 #define PR_tee 342 #define PR_tgkill 268 #define PR_timer_create 257 #define PR_timer_delete 261 #define PR_timer_getoverrun 260 #define PR_timer_gettime 259 #define PR_timer_settime 258 #define PR_timerfd_create 350 #define PR_timerfd_gettime 354 #define PR_timerfd_settime 353 #define PR_times 43 #define PR_tkill 238 #define PR_truncate 92 #define PR_truncate64 193 #define PR_ugetrlimit 191 #define PR_umask 60 #define PR_umount2 52 #define PR_uname 122 #define PR_unlink 10 #define PR_unlinkat 328 #define PR_unshare 337 #define PR_uselib 86 #define PR_ustat 62 #define PR_utimensat 348 #define PR_utimes 269 #define PR_vfork 190 #define PR_vhangup 111 #define PR_vmsplice 343 #define PR_vserver 313 #define PR_wait4 114 #define PR_waitid 280 #define PR_write 4 #define PR_writev 146 /* * These following syscalls are arm specific, this list was generated * thanks to the following command: * * cpp -dM -D__ARM_EABI__=1 arch/arm/include/asm/unistd.h | grep '#define __ARM_NR_' | sed s/__ARM_NR_/PR_ARM_/g | sort -u */ #define PR_ARM_BASE 0x0f0000 #define PR_ARM_breakpoint (PR_ARM_BASE+1) #define PR_ARM_cacheflush (PR_ARM_BASE+2) #define PR_ARM_set_tls (PR_ARM_BASE+5) #define PR_ARM_usr26 (PR_ARM_BASE+3) #define PR_ARM_usr32 (PR_ARM_BASE+4) /* * These following syscalls do not exist on ARM. Note that syscall * numbers from -1 to -10 are reserved for PRoot internal usage. */ #define PR_afs_syscall ((word_t) -11) #define PR_alarm ((word_t) -12) #define PR_arch_prctl ((word_t) -13) #define PR_break ((word_t) -14) #define PR_cacheflush ((word_t) -15) #define PR_create_module ((word_t) -16) #define PR_epoll_ctl_old ((word_t) -17) #define PR_epoll_wait_old ((word_t) -18) #define PR_fadvise64 ((word_t) -19) #define PR_fadvise64_64 ((word_t) -20) #define PR_ftime ((word_t) -21) #define PR_get_kernel_syms ((word_t) -22) #define PR_get_thread_area ((word_t) -23) #define PR_getpmsg ((word_t) -24) #define PR_getrlimit ((word_t) -25) #define PR_gtty ((word_t) -26) #define PR_idle ((word_t) -27) #define PR_ioperm ((word_t) -28) #define PR_iopl ((word_t) -29) #define PR_ipc ((word_t) -30) #define PR_lock ((word_t) -31) #define PR_migrate_pages ((word_t) -32) #define PR_mmap ((word_t) -33) #define PR_modify_ldt ((word_t) -34) #define PR_mpx ((word_t) -35) #define PR_newfstatat ((word_t) -36) #define PR_oldfstat ((word_t) -37) #define PR_oldlstat ((word_t) -38) #define PR_oldolduname ((word_t) -39) #define PR_oldstat ((word_t) -40) #define PR_olduname ((word_t) -41) #define PR_prof ((word_t) -42) #define PR_profil ((word_t) -43) #define PR_putpmsg ((word_t) -44) #define PR_query_module ((word_t) -45) #define PR_readdir ((word_t) -46) #define PR_security ((word_t) -47) #define PR_select ((word_t) -48) #define PR_set_thread_area ((word_t) -49) #define PR_sgetmask ((word_t) -50) #define PR_signal ((word_t) -51) #define PR_socketcall ((word_t) -52) #define PR_ssetmask ((word_t) -53) #define PR_stime ((word_t) -54) #define PR_stty ((word_t) -55) #define PR_sync_file_range ((word_t) -56) #define PR_time ((word_t) -57) #define PR_tuxcall ((word_t) -58) #define PR_ulimit ((word_t) -59) #define PR_umount ((word_t) -60) #define PR_utime ((word_t) -61) #define PR_vm86 ((word_t) -62) #define PR_vm86old ((word_t) -63) #define PR_waitpid ((word_t) -64) #define PR_sync_file_range2 ((word_t) -65) proot-3.0.2/src/syscall/seccomp.c0000644000175000017500000002531612156552156016310 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include "build.h" #include "arch.h" #if defined(HAVE_SECCOMP_FILTER) #include /* prctl(2), PR_* */ #include /* struct sock_*, */ #include /* SECCOMP_MODE_FILTER, */ #include /* struct sock_*, */ #include /* AUDIT_, */ #include /* LIST_FOREACH, */ #include /* size_t, */ #include /* talloc_*, */ #include /* E*, */ #include /* memcpy(3), */ #include /* offsetof(3), */ #include /* uint*_t, UINT*_MAX, */ #include "syscall/seccomp.h" #include "tracee/tracee.h" #include "syscall/syscall.h" #include "extension/extension.h" #include "notice.h" #include "compat.h" #include "attribute.h" #define DEBUG_FILTER(...) /* fprintf(stderr, __VA_ARGS__) */ /** * Allocate an empty @program->filter. This function returns -errno * if an error occurred, otherwise 0. */ static int new_program_filter(struct sock_fprog *program) { program->filter = talloc_array(NULL, struct sock_filter, 0); if (program->filter == NULL) return -ENOMEM; program->len = 0; return 0; } /** * Append to @program->filter the given @statements (@nb_statements * items). This function returns -errno if an error occurred, * otherwise 0. */ static int add_statements(struct sock_fprog *program, size_t nb_statements, struct sock_filter *statements) { size_t length; void *tmp; size_t i; length = talloc_array_length(program->filter); tmp = talloc_realloc(NULL, program->filter, struct sock_filter, length + nb_statements); if (tmp == NULL) return -ENOMEM; program->filter = tmp; for (i = 0; i < nb_statements; i++, length++) memcpy(&program->filter[length], &statements[i], sizeof(struct sock_filter)); return 0; } /** * Append to @program->filter the statements required to notify PRoot * about the given @syscall made by a tracee, with the given @flag. * This function returns -errno if an error occurred, otherwise 0. */ static int add_trace_syscall(struct sock_fprog *program, word_t syscall, int flag) { int status; /* Sanity check. */ if (syscall > UINT32_MAX) return -ERANGE; #define LENGTH_TRACE_SYSCALL 2 struct sock_filter statements[LENGTH_TRACE_SYSCALL] = { /* Compare the accumulator with the expected syscall: * skip the next statement if not equal. */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, syscall, 0, 1), /* Notify the tracer. */ BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE + flag) }; DEBUG_FILTER("FILTER: trace if syscall == %ld\n", syscall); status = add_statements(program, LENGTH_TRACE_SYSCALL, statements); if (status < 0) return status; return 0; } /** * Append to @program->filter the statements that allow anything (if * unfiltered). Note that @nb_traced_syscalls is used to make a * sanity check. This function returns -errno if an error occurred, * otherwise 0. */ static int end_arch_section(struct sock_fprog *program, size_t nb_traced_syscalls) { int status; #define LENGTH_END_SECTION 1 struct sock_filter statements[LENGTH_END_SECTION] = { BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) }; DEBUG_FILTER("FILTER: allow\n"); status = add_statements(program, LENGTH_END_SECTION, statements); if (status < 0) return status; /* Sanity check, see start_arch_section(). */ if ( talloc_array_length(program->filter) - program->len != LENGTH_END_SECTION + nb_traced_syscalls * LENGTH_TRACE_SYSCALL) return -ERANGE; return 0; } /** * Append to @program->filter the statements that check the current * @architecture. Note that @nb_traced_syscalls is used to make a * sanity check. This function returns -errno if an error occurred, * otherwise 0. */ static int start_arch_section(struct sock_fprog *program, uint32_t architecture, size_t nb_traced_syscalls) { const size_t arch_offset = offsetof(struct seccomp_data, arch); const size_t syscall_offset = offsetof(struct seccomp_data, nr); const size_t section_length = LENGTH_END_SECTION + nb_traced_syscalls * LENGTH_TRACE_SYSCALL; int status; /* Sanity checks. */ if ( arch_offset > UINT32_MAX || syscall_offset > UINT32_MAX || section_length > UINT32_MAX - 1) return -ERANGE; #define LENGTH_START_SECTION 4 struct sock_filter statements[LENGTH_START_SECTION] = { /* Load the current architecture into the * accumulator. */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_offset), /* Compare the accumulator with the expected * architecture: skip the following statement if * equal. */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, architecture, 1, 0), /* This is not the expected architecture, so jump * unconditionally to the end of this section. */ BPF_STMT(BPF_JMP + BPF_JA + BPF_K, section_length + 1), /* This is the expected architecture, so load the * current syscall into the accumulator. */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_offset) }; DEBUG_FILTER("FILTER: if arch == %ld, up to %zdth statement\n", architecture, nb_traced_syscalls); status = add_statements(program, LENGTH_START_SECTION, statements); if (status < 0) return status; /* See the sanity check in end_arch_section(). */ program->len = talloc_array_length(program->filter); return 0; } /** * Append to @program->filter the statements that forbid anything (if * unfiltered) and update @program->len. This function returns -errno * if an error occurred, otherwise 0. */ static int finalize_program_filter(struct sock_fprog *program) { int status; #define LENGTH_FINALIZE 1 struct sock_filter statements[LENGTH_FINALIZE] = { BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL) }; DEBUG_FILTER("FILTER: kill\n"); status = add_statements(program, LENGTH_FINALIZE, statements); if (status < 0) return status; program->len = talloc_array_length(program->filter); return 0; } /** * Free @program->filter and set @program->len to 0. */ static void free_program_filter(struct sock_fprog *program) { if (program->filter != NULL) TALLOC_FREE(program->filter); program->len = 0; } /** * Assemble the given @filters according to the following pseudo-code, * then enabled them for the given @tracee and all of its future * children: * * for each handled architectures * for each handled syscall * trace * allow * kill * * This function returns -errno if an error occurred, otherwise 0. */ static int set_seccomp_filters(const Tracee *tracee, const Filter *filters) { struct sock_fprog program = { .len = 0, .filter = NULL }; size_t nb_traced_syscalls; size_t i, j; int status; status = new_program_filter(&program); if (status < 0) goto end; for (i = 0; filters[i].architecture != 0; i++) { nb_traced_syscalls = 0; /* Pre-compute the number of traced syscalls for this architecture. */ for (j = 0; filters[i].syscalls[j].value != SYSCALL_AVOIDER; j++) if ((int) filters[i].syscalls[j].value >= 0) nb_traced_syscalls++; /* Filter: if handled architecture */ status = start_arch_section(&program, filters[i].architecture, nb_traced_syscalls); if (status < 0) goto end; for (j = 0; filters[i].syscalls[j].value != SYSCALL_AVOIDER; j++) { if ((int) filters[i].syscalls[j].value < 0) continue; /* Filter: trace if handled syscall */ status = add_trace_syscall(&program, filters[i].syscalls[j].value, filters[i].syscalls[j].flag); if (status < 0) goto end; } /* Filter: allow untraced syscalls for this architecture */ status = end_arch_section(&program, nb_traced_syscalls); if (status < 0) goto end; } status = finalize_program_filter(&program); if (status < 0) goto end; status = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); if (status < 0) { notice(tracee, WARNING, SYSTEM, "prctl(PR_SET_NO_NEW_PRIVS)"); goto end; } /* To output this BPF program for debug purpose: * * write(2, program.filter, program.len * sizeof(struct sock_filter)); */ status = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program); if (status < 0) { notice(tracee, WARNING, SYSTEM, "prctl(PR_SET_SECCOMP)"); goto end; } status = 0; end: free_program_filter(&program); return status; } /* List of syscalls handled by PRoot. */ #if defined(ARCH_X86_64) static const FilteredSyscall syscalls64[] = { #include SYSNUM_HEADER #include "syscall/filter.h" #include SYSNUM_HEADER3 #include "syscall/filter.h" FILTERED_SYSCALL_END }; static const FilteredSyscall syscalls32[] = { #include SYSNUM_HEADER2 #include "syscall/filter.h" FILTERED_SYSCALL_END }; static const Filter filters[] = { { .architecture = AUDIT_ARCH_X86_64, .syscalls = syscalls64 }, { .architecture = AUDIT_ARCH_I386, .syscalls = syscalls32 }, { 0 } }; #elif defined(AUDIT_ARCH_NUM) static const FilteredSyscall syscalls[] = { #include SYSNUM_HEADER #include "syscall/filter.h" FILTERED_SYSCALL_END }; static const Filter filters[] = { { .architecture = AUDIT_ARCH_NUM, .syscalls = syscalls }, { 0 } }; #else static const Filter filters[] = { 0 }; #endif #include "syscall/sysnum-undefined.h" /** * Tell the kernel to trace only syscalls handled by PRoot and its * extensions. This filter will be enabled for the given @tracee and * all of its future children. This function returns -errno if an * error occurred, otherwise 0. */ int enable_syscall_filtering(const Tracee *tracee) { Extension *extension; int status; status = set_seccomp_filters(tracee, filters); if (status < 0) return status; /* No more filters? */ if (tracee->extensions == NULL) return 0; /* Filters are evaluated with the following precedence order: * KILL, TRAP, DATA, ERRNO, TRACE, then ALLOW. For details, * see linux/Documentation/prctl/seccomp_filter.txt. */ LIST_FOREACH(extension, tracee->extensions, link) { if (extension->filters == NULL) continue; status = set_seccomp_filters(tracee, extension->filters); if (status < 0) return status; } return 0; } #endif /* defined(HAVE_SECCOMP_FILTER) */ proot-3.0.2/src/syscall/sysnum-i386.h0000644000175000017500000002420712156552156016707 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file was generated thanks to the following command: * * cpp -dM linux/arch/x86/syscalls/../include/generated/asm/unistd_32.h | grep '#define __NR_' | sed s/__NR_/PR_/g | sort -u */ #include "syscall/sysnum-undefined.h" #define PR__llseek 140 #define PR__newselect 142 #define PR__sysctl 149 #define PR_access 33 #define PR_acct 51 #define PR_add_key 286 #define PR_adjtimex 124 #define PR_afs_syscall 137 #define PR_alarm 27 #define PR_bdflush 134 #define PR_break 17 #define PR_brk 45 #define PR_capget 184 #define PR_capset 185 #define PR_chdir 12 #define PR_chmod 15 #define PR_chown 182 #define PR_chown32 212 #define PR_chroot 61 #define PR_clock_adjtime 343 #define PR_clock_getres 266 #define PR_clock_gettime 265 #define PR_clock_nanosleep 267 #define PR_clock_settime 264 #define PR_clone 120 #define PR_close 6 #define PR_creat 8 #define PR_create_module 127 #define PR_delete_module 129 #define PR_dup 41 #define PR_dup2 63 #define PR_dup3 330 #define PR_epoll_create 254 #define PR_epoll_create1 329 #define PR_epoll_ctl 255 #define PR_epoll_pwait 319 #define PR_epoll_wait 256 #define PR_eventfd 323 #define PR_eventfd2 328 #define PR_execve 11 #define PR_exit 1 #define PR_exit_group 252 #define PR_faccessat 307 #define PR_fadvise64 250 #define PR_fadvise64_64 272 #define PR_fallocate 324 #define PR_fanotify_init 338 #define PR_fanotify_mark 339 #define PR_fchdir 133 #define PR_fchmod 94 #define PR_fchmodat 306 #define PR_fchown 95 #define PR_fchown32 207 #define PR_fchownat 298 #define PR_fcntl 55 #define PR_fcntl64 221 #define PR_fdatasync 148 #define PR_fgetxattr 231 #define PR_flistxattr 234 #define PR_flock 143 #define PR_fork 2 #define PR_fremovexattr 237 #define PR_fsetxattr 228 #define PR_fstat 108 #define PR_fstat64 197 #define PR_fstatat64 300 #define PR_fstatfs 100 #define PR_fstatfs64 269 #define PR_fsync 118 #define PR_ftime 35 #define PR_ftruncate 93 #define PR_ftruncate64 194 #define PR_futex 240 #define PR_futimesat 299 #define PR_get_kernel_syms 130 #define PR_get_mempolicy 275 #define PR_get_robust_list 312 #define PR_get_thread_area 244 #define PR_getcpu 318 #define PR_getcwd 183 #define PR_getdents 141 #define PR_getdents64 220 #define PR_getegid 50 #define PR_getegid32 202 #define PR_geteuid 49 #define PR_geteuid32 201 #define PR_getgid 47 #define PR_getgid32 200 #define PR_getgroups 80 #define PR_getgroups32 205 #define PR_getitimer 105 #define PR_getpgid 132 #define PR_getpgrp 65 #define PR_getpid 20 #define PR_getpmsg 188 #define PR_getppid 64 #define PR_getpriority 96 #define PR_getresgid 171 #define PR_getresgid32 211 #define PR_getresuid 165 #define PR_getresuid32 209 #define PR_getrlimit 76 #define PR_getrusage 77 #define PR_getsid 147 #define PR_gettid 224 #define PR_gettimeofday 78 #define PR_getuid 24 #define PR_getuid32 199 #define PR_getxattr 229 #define PR_gtty 32 #define PR_idle 112 #define PR_init_module 128 #define PR_inotify_add_watch 292 #define PR_inotify_init 291 #define PR_inotify_init1 332 #define PR_inotify_rm_watch 293 #define PR_io_cancel 249 #define PR_io_destroy 246 #define PR_io_getevents 247 #define PR_io_setup 245 #define PR_io_submit 248 #define PR_ioctl 54 #define PR_ioperm 101 #define PR_iopl 110 #define PR_ioprio_get 290 #define PR_ioprio_set 289 #define PR_ipc 117 #define PR_kcmp 349 #define PR_kexec_load 283 #define PR_keyctl 288 #define PR_kill 37 #define PR_lchown 16 #define PR_lchown32 198 #define PR_lgetxattr 230 #define PR_link 9 #define PR_linkat 303 #define PR_listxattr 232 #define PR_llistxattr 233 #define PR_lock 53 #define PR_lookup_dcookie 253 #define PR_lremovexattr 236 #define PR_lseek 19 #define PR_lsetxattr 227 #define PR_lstat 107 #define PR_lstat64 196 #define PR_madvise 219 #define PR_mbind 274 #define PR_migrate_pages 294 #define PR_mincore 218 #define PR_mkdir 39 #define PR_mkdirat 296 #define PR_mknod 14 #define PR_mknodat 297 #define PR_mlock 150 #define PR_mlockall 152 #define PR_mmap 90 #define PR_mmap2 192 #define PR_modify_ldt 123 #define PR_mount 21 #define PR_move_pages 317 #define PR_mprotect 125 #define PR_mpx 56 #define PR_mq_getsetattr 282 #define PR_mq_notify 281 #define PR_mq_open 277 #define PR_mq_timedreceive 280 #define PR_mq_timedsend 279 #define PR_mq_unlink 278 #define PR_mremap 163 #define PR_msync 144 #define PR_munlock 151 #define PR_munlockall 153 #define PR_munmap 91 #define PR_name_to_handle_at 341 #define PR_nanosleep 162 #define PR_nfsservctl 169 #define PR_nice 34 #define PR_oldfstat 28 #define PR_oldlstat 84 #define PR_oldolduname 59 #define PR_oldstat 18 #define PR_olduname 109 #define PR_open 5 #define PR_open_by_handle_at 342 #define PR_openat 295 #define PR_pause 29 #define PR_perf_event_open 336 #define PR_personality 136 #define PR_pipe 42 #define PR_pipe2 331 #define PR_pivot_root 217 #define PR_poll 168 #define PR_ppoll 309 #define PR_prctl 172 #define PR_pread64 180 #define PR_preadv 333 #define PR_prlimit64 340 #define PR_process_vm_readv 347 #define PR_process_vm_writev 348 #define PR_prof 44 #define PR_profil 98 #define PR_pselect6 308 #define PR_ptrace 26 #define PR_putpmsg 189 #define PR_pwrite64 181 #define PR_pwritev 334 #define PR_query_module 167 #define PR_quotactl 131 #define PR_read 3 #define PR_readahead 225 #define PR_readdir 89 #define PR_readlink 85 #define PR_readlinkat 305 #define PR_readv 145 #define PR_reboot 88 #define PR_recvmmsg 337 #define PR_remap_file_pages 257 #define PR_removexattr 235 #define PR_rename 38 #define PR_renameat 302 #define PR_request_key 287 #define PR_restart_syscall 0 #define PR_rmdir 40 #define PR_rt_sigaction 174 #define PR_rt_sigpending 176 #define PR_rt_sigprocmask 175 #define PR_rt_sigqueueinfo 178 #define PR_rt_sigreturn 173 #define PR_rt_sigsuspend 179 #define PR_rt_sigtimedwait 177 #define PR_rt_tgsigqueueinfo 335 #define PR_sched_get_priority_max 159 #define PR_sched_get_priority_min 160 #define PR_sched_getaffinity 242 #define PR_sched_getparam 155 #define PR_sched_getscheduler 157 #define PR_sched_rr_get_interval 161 #define PR_sched_setaffinity 241 #define PR_sched_setparam 154 #define PR_sched_setscheduler 156 #define PR_sched_yield 158 #define PR_select 82 #define PR_sendfile 187 #define PR_sendfile64 239 #define PR_sendmmsg 345 #define PR_set_mempolicy 276 #define PR_set_robust_list 311 #define PR_set_thread_area 243 #define PR_set_tid_address 258 #define PR_setdomainname 121 #define PR_setfsgid 139 #define PR_setfsgid32 216 #define PR_setfsuid 138 #define PR_setfsuid32 215 #define PR_setgid 46 #define PR_setgid32 214 #define PR_setgroups 81 #define PR_setgroups32 206 #define PR_sethostname 74 #define PR_setitimer 104 #define PR_setns 346 #define PR_setpgid 57 #define PR_setpriority 97 #define PR_setregid 71 #define PR_setregid32 204 #define PR_setresgid 170 #define PR_setresgid32 210 #define PR_setresuid 164 #define PR_setresuid32 208 #define PR_setreuid 70 #define PR_setreuid32 203 #define PR_setrlimit 75 #define PR_setsid 66 #define PR_settimeofday 79 #define PR_setuid 23 #define PR_setuid32 213 #define PR_setxattr 226 #define PR_sgetmask 68 #define PR_sigaction 67 #define PR_sigaltstack 186 #define PR_signal 48 #define PR_signalfd 321 #define PR_signalfd4 327 #define PR_sigpending 73 #define PR_sigprocmask 126 #define PR_sigreturn 119 #define PR_sigsuspend 72 #define PR_socketcall 102 #define PR_splice 313 #define PR_ssetmask 69 #define PR_stat 106 #define PR_stat64 195 #define PR_statfs 99 #define PR_statfs64 268 #define PR_stime 25 #define PR_stty 31 #define PR_swapoff 115 #define PR_swapon 87 #define PR_symlink 83 #define PR_symlinkat 304 #define PR_sync 36 #define PR_sync_file_range 314 #define PR_syncfs 344 #define PR_sysfs 135 #define PR_sysinfo 116 #define PR_syslog 103 #define PR_tee 315 #define PR_tgkill 270 #define PR_time 13 #define PR_timer_create 259 #define PR_timer_delete 263 #define PR_timer_getoverrun 262 #define PR_timer_gettime 261 #define PR_timer_settime 260 #define PR_timerfd_create 322 #define PR_timerfd_gettime 326 #define PR_timerfd_settime 325 #define PR_times 43 #define PR_tkill 238 #define PR_truncate 92 #define PR_truncate64 193 #define PR_ugetrlimit 191 #define PR_ulimit 58 #define PR_umask 60 #define PR_umount 22 #define PR_umount2 52 #define PR_uname 122 #define PR_unlink 10 #define PR_unlinkat 301 #define PR_unshare 310 #define PR_uselib 86 #define PR_ustat 62 #define PR_utime 30 #define PR_utimensat 320 #define PR_utimes 271 #define PR_vfork 190 #define PR_vhangup 111 #define PR_vm86 166 #define PR_vm86old 113 #define PR_vmsplice 316 #define PR_vserver 273 #define PR_wait4 114 #define PR_waitid 284 #define PR_waitpid 7 #define PR_write 4 #define PR_writev 146 /* * These following syscalls do not exist on i386. Note that syscall * numbers from -1 to -10 are reserved for PRoot internal usage. */ #define PR_accept ((word_t) -16) #define PR_accept4 ((word_t) -17) #define PR_arch_prctl ((word_t) -18) #define PR_arm_fadvise64_64 ((word_t) -19) #define PR_arm_sync_file_range ((word_t) -20) #define PR_bind ((word_t) -21) #define PR_cacheflush ((word_t) -22) #define PR_connect ((word_t) -23) #define PR_epoll_ctl_old ((word_t) -24) #define PR_epoll_wait_old ((word_t) -25) #define PR_getpeername ((word_t) -26) #define PR_getsockname ((word_t) -27) #define PR_getsockopt ((word_t) -28) #define PR_listen ((word_t) -29) #define PR_msgctl ((word_t) -30) #define PR_msgget ((word_t) -31) #define PR_msgrcv ((word_t) -32) #define PR_msgsnd ((word_t) -33) #define PR_newfstatat ((word_t) -34) #define PR_pciconfig_iobase ((word_t) -35) #define PR_pciconfig_read ((word_t) -36) #define PR_pciconfig_write ((word_t) -37) #define PR_recv ((word_t) -38) #define PR_recvfrom ((word_t) -39) #define PR_recvmsg ((word_t) -40) #define PR_security ((word_t) -41) #define PR_semctl ((word_t) -42) #define PR_semget ((word_t) -43) #define PR_semop ((word_t) -44) #define PR_semtimedop ((word_t) -45) #define PR_send ((word_t) -46) #define PR_sendmsg ((word_t) -47) #define PR_sendto ((word_t) -48) #define PR_setsockopt ((word_t) -49) #define PR_shmat ((word_t) -50) #define PR_shmctl ((word_t) -51) #define PR_shmdt ((word_t) -52) #define PR_shmget ((word_t) -53) #define PR_shutdown ((word_t) -54) #define PR_socket ((word_t) -55) #define PR_socketpair ((word_t) -56) #define PR_sync_file_range2 ((word_t) -57) #define PR_tuxcall ((word_t) -58) proot-3.0.2/src/syscall/sysnum-arm64.h0000644000175000017500000002753212156552156017153 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file was generated thanks to the following command: * * cpp -Iinclude -dM linux/arch/arm64/include/asm/unistd.h | grep '#define __NR_' | sed s/__NR_/PR_/g | sort -u */ #include "syscall/sysnum-undefined.h" #define PR_accept 202 #define PR_accept4 242 #define PR_acct 89 #define PR_add_key 217 #define PR_adjtimex 171 #define PR_arch_specific_syscall 244 #define PR_bind 200 #define PR_brk 214 #define PR_capget 90 #define PR_capset 91 #define PR_chdir 49 #define PR_chroot 51 #define PR_clock_adjtime 266 #define PR_clock_getres 114 #define PR_clock_gettime 113 #define PR_clock_nanosleep 115 #define PR_clock_settime 112 #define PR_clone 220 #define PR_close 57 #define PR_connect 203 #define PR_delete_module 106 #define PR_dup 23 #define PR_dup3 24 #define PR_epoll_create1 20 #define PR_epoll_ctl 21 #define PR_epoll_pwait 22 #define PR_eventfd2 19 #define PR_execve 221 #define PR_exit 93 #define PR_exit_group 94 #define PR_faccessat 48 #define PR_fadvise64 __NR3264_fadvise64 #define PR_fallocate 47 #define PR_fanotify_init 262 #define PR_fanotify_mark 263 #define PR_fchdir 50 #define PR_fchmod 52 #define PR_fchmodat 53 #define PR_fchown 55 #define PR_fchownat 54 #define PR_fcntl __NR3264_fcntl #define PR_fdatasync 83 #define PR_fgetxattr 10 #define PR_flistxattr 13 #define PR_flock 32 #define PR_fremovexattr 16 #define PR_fsetxattr 7 #define PR_fstat __NR3264_fstat #define PR_fstatfs __NR3264_fstatfs #define PR_fsync 82 #define PR_ftruncate __NR3264_ftruncate #define PR_futex 98 #define PR_get_mempolicy 236 #define PR_get_robust_list 100 #define PR_getcpu 168 #define PR_getcwd 17 #define PR_getdents64 61 #define PR_getegid 177 #define PR_geteuid 175 #define PR_getgid 176 #define PR_getgroups 158 #define PR_getitimer 102 #define PR_getpeername 205 #define PR_getpgid 155 #define PR_getpid 172 #define PR_getppid 173 #define PR_getpriority 141 #define PR_getresgid 150 #define PR_getresuid 148 #define PR_getrlimit 163 #define PR_getrusage 165 #define PR_getsid 156 #define PR_getsockname 204 #define PR_getsockopt 209 #define PR_gettid 178 #define PR_gettimeofday 169 #define PR_getuid 174 #define PR_getxattr 8 #define PR_init_module 105 #define PR_inotify_add_watch 27 #define PR_inotify_init1 26 #define PR_inotify_rm_watch 28 #define PR_io_cancel 3 #define PR_io_destroy 1 #define PR_io_getevents 4 #define PR_io_setup 0 #define PR_io_submit 2 #define PR_ioctl 29 #define PR_ioprio_get 31 #define PR_ioprio_set 30 #define PR_kcmp 272 #define PR_kexec_load 104 #define PR_keyctl 219 #define PR_kill 129 #define PR_lgetxattr 9 #define PR_linkat 37 #define PR_listen 201 #define PR_listxattr 11 #define PR_llistxattr 12 #define PR_lookup_dcookie 18 #define PR_lremovexattr 15 #define PR_lseek __NR3264_lseek #define PR_lsetxattr 6 #define PR_madvise 233 #define PR_mbind 235 #define PR_migrate_pages 238 #define PR_mincore 232 #define PR_mkdirat 34 #define PR_mknodat 33 #define PR_mlock 228 #define PR_mlockall 230 #define PR_mmap __NR3264_mmap #define PR_mount 40 #define PR_move_pages 239 #define PR_mprotect 226 #define PR_mq_getsetattr 185 #define PR_mq_notify 184 #define PR_mq_open 180 #define PR_mq_timedreceive 183 #define PR_mq_timedsend 182 #define PR_mq_unlink 181 #define PR_mremap 216 #define PR_msgctl 187 #define PR_msgget 186 #define PR_msgrcv 188 #define PR_msgsnd 189 #define PR_msync 227 #define PR_munlock 229 #define PR_munlockall 231 #define PR_munmap 215 #define PR_name_to_handle_at 264 #define PR_nanosleep 101 #define PR_newfstatat __NR3264_fstatat #define PR_nfsservctl 42 #define PR_open_by_handle_at 265 #define PR_openat 56 #define PR_perf_event_open 241 #define PR_personality 92 #define PR_pipe2 59 #define PR_pivot_root 41 #define PR_ppoll 73 #define PR_prctl 167 #define PR_pread64 67 #define PR_preadv 69 #define PR_prlimit64 261 #define PR_process_vm_readv 270 #define PR_process_vm_writev 271 #define PR_pselect6 72 #define PR_ptrace 117 #define PR_pwrite64 68 #define PR_pwritev 70 #define PR_quotactl 60 #define PR_read 63 #define PR_readahead 213 #define PR_readlinkat 78 #define PR_readv 65 #define PR_reboot 142 #define PR_recvfrom 207 #define PR_recvmmsg 243 #define PR_recvmsg 212 #define PR_remap_file_pages 234 #define PR_removexattr 14 #define PR_renameat 38 #define PR_request_key 218 #define PR_restart_syscall 128 #define PR_rt_sigaction 134 #define PR_rt_sigpending 136 #define PR_rt_sigprocmask 135 #define PR_rt_sigqueueinfo 138 #define PR_rt_sigreturn 139 #define PR_rt_sigsuspend 133 #define PR_rt_sigtimedwait 137 #define PR_rt_tgsigqueueinfo 240 #define PR_sched_get_priority_max 125 #define PR_sched_get_priority_min 126 #define PR_sched_getaffinity 123 #define PR_sched_getparam 121 #define PR_sched_getscheduler 120 #define PR_sched_rr_get_interval 127 #define PR_sched_setaffinity 122 #define PR_sched_setparam 118 #define PR_sched_setscheduler 119 #define PR_sched_yield 124 #define PR_semctl 191 #define PR_semget 190 #define PR_semop 193 #define PR_semtimedop 192 #define PR_sendfile __NR3264_sendfile #define PR_sendmmsg 269 #define PR_sendmsg 211 #define PR_sendto 206 #define PR_set_mempolicy 237 #define PR_set_robust_list 99 #define PR_set_tid_address 96 #define PR_setdomainname 162 #define PR_setfsgid 152 #define PR_setfsuid 151 #define PR_setgid 144 #define PR_setgroups 159 #define PR_sethostname 161 #define PR_setitimer 103 #define PR_setns 268 #define PR_setpgid 154 #define PR_setpriority 140 #define PR_setregid 143 #define PR_setresgid 149 #define PR_setresuid 147 #define PR_setreuid 145 #define PR_setrlimit 164 #define PR_setsid 157 #define PR_setsockopt 208 #define PR_settimeofday 170 #define PR_setuid 146 #define PR_setxattr 5 #define PR_shmat 196 #define PR_shmctl 195 #define PR_shmdt 197 #define PR_shmget 194 #define PR_shutdown 210 #define PR_sigaltstack 132 #define PR_signalfd4 74 #define PR_socket 198 #define PR_socketpair 199 #define PR_splice 76 #define PR_statfs __NR3264_statfs #define PR_swapoff 225 #define PR_swapon 224 #define PR_symlinkat 36 #define PR_sync 81 #define PR_sync_file_range 84 #define PR_syncfs 267 #define PR_syscalls 273 #define PR_sysinfo 179 #define PR_syslog 116 #define PR_tee 77 #define PR_tgkill 131 #define PR_timer_create 107 #define PR_timer_delete 111 #define PR_timer_getoverrun 109 #define PR_timer_gettime 108 #define PR_timer_settime 110 #define PR_timerfd_create 85 #define PR_timerfd_gettime 87 #define PR_timerfd_settime 86 #define PR_times 153 #define PR_tkill 130 #define PR_truncate __NR3264_truncate #define PR_umask 166 #define PR_umount2 39 #define PR_uname 160 #define PR_unlinkat 35 #define PR_unshare 97 #define PR_utimensat 88 #define PR_vhangup 58 #define PR_vmsplice 75 #define PR_wait4 260 #define PR_waitid 95 #define PR_write 64 #define PR_writev 66 /* * These following syscalls are 32/64 specific, this list was * generated thanks to the following command: * * cpp -dM include/uapi/asm-generic/unistd.h | grep '#define __NR3264_' | sed s/__NR/PR/g | sort -u */ #define __NR3264_fadvise64 223 #define __NR3264_fcntl 25 #define __NR3264_fstat 80 #define __NR3264_fstatat 79 #define __NR3264_fstatfs 44 #define __NR3264_ftruncate 46 #define __NR3264_lseek 62 #define __NR3264_mmap 222 #define __NR3264_sendfile 71 #define __NR3264_statfs 43 #define __NR3264_truncate 45 /* * These following syscalls do not exist on ARM. Note that syscall * numbers from -1 to -10 are reserved for PRoot internal usage. */ #define PR__llseek ((word_t) -10) #define PR__newselect ((word_t) -11) #define PR__sysctl ((word_t) -12) #define PR_access ((word_t) -13) #define PR_afs_syscall ((word_t) -14) #define PR_alarm ((word_t) -15) #define PR_arch_prctl ((word_t) -16) #define PR_arm_fadvise64_64 ((word_t) -17) #define PR_arm_sync_file_range ((word_t) -18) #define PR_bdflush ((word_t) -19) #define PR_break ((word_t) -20) #define PR_cacheflush ((word_t) -21) #define PR_chmod ((word_t) -22) #define PR_chown ((word_t) -23) #define PR_chown32 ((word_t) -24) #define PR_creat ((word_t) -25) #define PR_create_module ((word_t) -26) #define PR_dup2 ((word_t) -27) #define PR_epoll_create ((word_t) -28) #define PR_epoll_ctl_old ((word_t) -29) #define PR_epoll_wait ((word_t) -30) #define PR_epoll_wait_old ((word_t) -31) #define PR_eventfd ((word_t) -32) #define PR_fadvise64_64 ((word_t) -33) #define PR_fchown32 ((word_t) -34) #define PR_fcntl64 ((word_t) -35) #define PR_fork ((word_t) -36) #define PR_fstat64 ((word_t) -37) #define PR_fstatat64 ((word_t) -38) #define PR_fstatfs64 ((word_t) -39) #define PR_ftime ((word_t) -40) #define PR_ftruncate64 ((word_t) -41) #define PR_futimesat ((word_t) -42) #define PR_get_kernel_syms ((word_t) -43) #define PR_get_thread_area ((word_t) -44) #define PR_getdents ((word_t) -45) #define PR_getegid32 ((word_t) -46) #define PR_geteuid32 ((word_t) -47) #define PR_getgid32 ((word_t) -48) #define PR_getgroups32 ((word_t) -49) #define PR_getpgrp ((word_t) -50) #define PR_getpmsg ((word_t) -51) #define PR_getresgid32 ((word_t) -52) #define PR_getresuid32 ((word_t) -53) #define PR_getuid32 ((word_t) -54) #define PR_gtty ((word_t) -55) #define PR_idle ((word_t) -56) #define PR_inotify_init ((word_t) -57) #define PR_ioperm ((word_t) -58) #define PR_iopl ((word_t) -59) #define PR_ipc ((word_t) -60) #define PR_lchown ((word_t) -61) #define PR_lchown32 ((word_t) -62) #define PR_link ((word_t) -63) #define PR_lock ((word_t) -64) #define PR_lstat ((word_t) -65) #define PR_lstat64 ((word_t) -66) #define PR_mkdir ((word_t) -67) #define PR_mknod ((word_t) -68) #define PR_mmap2 ((word_t) -69) #define PR_modify_ldt ((word_t) -70) #define PR_mpx ((word_t) -71) #define PR_nice ((word_t) -72) #define PR_oldfstat ((word_t) -73) #define PR_oldlstat ((word_t) -74) #define PR_oldolduname ((word_t) -75) #define PR_oldstat ((word_t) -76) #define PR_olduname ((word_t) -77) #define PR_open ((word_t) -78) #define PR_pause ((word_t) -79) #define PR_pciconfig_iobase ((word_t) -80) #define PR_pciconfig_read ((word_t) -81) #define PR_pciconfig_write ((word_t) -82) #define PR_pipe ((word_t) -83) #define PR_poll ((word_t) -84) #define PR_prof ((word_t) -85) #define PR_profil ((word_t) -86) #define PR_putpmsg ((word_t) -87) #define PR_query_module ((word_t) -88) #define PR_readdir ((word_t) -89) #define PR_readlink ((word_t) -90) #define PR_recv ((word_t) -91) #define PR_rename ((word_t) -92) #define PR_rmdir ((word_t) -93) #define PR_security ((word_t) -94) #define PR_select ((word_t) -95) #define PR_send ((word_t) -96) #define PR_sendfile64 ((word_t) -97) #define PR_set_thread_area ((word_t) -98) #define PR_setfsgid32 ((word_t) -99) #define PR_setfsuid32 ((word_t) -100) #define PR_setgid32 ((word_t) -101) #define PR_setgroups32 ((word_t) -102) #define PR_setregid32 ((word_t) -103) #define PR_setresgid32 ((word_t) -104) #define PR_setresuid32 ((word_t) -105) #define PR_setreuid32 ((word_t) -106) #define PR_setuid32 ((word_t) -107) #define PR_sgetmask ((word_t) -108) #define PR_sigaction ((word_t) -109) #define PR_signal ((word_t) -110) #define PR_signalfd ((word_t) -111) #define PR_sigpending ((word_t) -112) #define PR_sigprocmask ((word_t) -113) #define PR_sigreturn ((word_t) -114) #define PR_sigsuspend ((word_t) -115) #define PR_socketcall ((word_t) -116) #define PR_ssetmask ((word_t) -117) #define PR_stat ((word_t) -118) #define PR_stat64 ((word_t) -119) #define PR_statfs64 ((word_t) -120) #define PR_stime ((word_t) -121) #define PR_stty ((word_t) -122) #define PR_symlink ((word_t) -123) #define PR_sync_file_range2 ((word_t) -124) #define PR_sysfs ((word_t) -125) #define PR_time ((word_t) -126) #define PR_truncate64 ((word_t) -127) #define PR_tuxcall ((word_t) -128) #define PR_ugetrlimit ((word_t) -129) #define PR_ulimit ((word_t) -130) #define PR_umount ((word_t) -131) #define PR_unlink ((word_t) -132) #define PR_uselib ((word_t) -133) #define PR_ustat ((word_t) -134) #define PR_utime ((word_t) -135) #define PR_utimes ((word_t) -136) #define PR_vfork ((word_t) -137) #define PR_vm86 ((word_t) -138) #define PR_vm86old ((word_t) -139) #define PR_vserver ((word_t) -140) #define PR_waitpid ((word_t) -141) proot-3.0.2/src/syscall/filter.h0000644000175000017500000000326212156552156016145 0ustar ivoireivoire/* This file was generated thanks to the following command: * * grep '^case PR_' enter.c exit.c | cut -f 2 -d ' ' | cut -f 1 -d : | sort -u */ { PR_accept, 0 }, { PR_accept4, 0 }, { PR_access, 0 }, { PR_acct, 0 }, { PR_bind, 0 }, { PR_chdir, 0 }, { PR_chmod, 0 }, { PR_chown, 0 }, { PR_chown32, 0 }, { PR_chroot, 0 }, { PR_connect, 0 }, { PR_creat, 0 }, { PR_execve, 1 }, /* Special case! */ { PR_faccessat, 0 }, { PR_fchdir, 0 }, { PR_fchmodat, 0 }, { PR_fchownat, 0 }, { PR_fstatat64, 0 }, { PR_futimesat, 0 }, { PR_getcwd, 0 }, { PR_getpeername, 0 }, { PR_getsockname, 0 }, { PR_getxattr, 0 }, { PR_inotify_add_watch, 0 }, { PR_lchown, 0 }, { PR_lchown32, 0 }, { PR_lgetxattr, 0 }, { PR_link, 0 }, { PR_linkat, 0 }, { PR_listxattr, 0 }, { PR_llistxattr, 0 }, { PR_lremovexattr, 0 }, { PR_lsetxattr, 0 }, { PR_lstat, 0 }, { PR_lstat64, 0 }, { PR_mkdir, 0 }, { PR_mkdirat, 0 }, { PR_mknod, 0 }, { PR_mknodat, 0 }, { PR_mount, 0 }, { PR_name_to_handle_at, 0 }, { PR_newfstatat, 0 }, { PR_oldlstat, 0 }, { PR_oldstat, 0 }, { PR_open, 0 }, { PR_openat, 0 }, { PR_pivot_root, 0 }, { PR_readlink, 0 }, { PR_readlinkat, 0 }, { PR_removexattr, 0 }, { PR_rename, 0 }, { PR_renameat, 0 }, { PR_rmdir, 0 }, { PR_rt_sigreturn, 0 }, { PR_setxattr, 0 }, { PR_sigreturn, 0 }, { PR_socketcall, 0 }, { PR_stat, 0 }, { PR_stat64, 0 }, { PR_statfs, 0 }, { PR_statfs64, 0 }, { PR_swapoff, 0 }, { PR_swapon, 0 }, { PR_symlink, 0 }, { PR_symlinkat, 0 }, { PR_truncate, 0 }, { PR_truncate64, 0 }, { PR_umount, 0 }, { PR_umount2, 0 }, { PR_uname, 0 }, { PR_unlink, 0 }, { PR_unlinkat, 0 }, { PR_uselib, 0 }, { PR_utime, 0 }, { PR_utimensat, 0 }, { PR_utimes, 0 }, proot-3.0.2/src/syscall/sysnum-x86_64.h0000644000175000017500000002476412156552156017164 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file was generated thanks to the following command: * * cpp -dM linux/arch/x86/syscalls/../include/generated/asm/unistd_64.h | grep '#define __NR_' | sed s/__NR_/PR_/g | sort -u */ #include "syscall/sysnum-undefined.h" #define PR__sysctl 156 #define PR_accept 43 #define PR_accept4 288 #define PR_access 21 #define PR_acct 163 #define PR_add_key 248 #define PR_adjtimex 159 #define PR_afs_syscall 183 #define PR_alarm 37 #define PR_arch_prctl 158 #define PR_bind 49 #define PR_brk 12 #define PR_capget 125 #define PR_capset 126 #define PR_chdir 80 #define PR_chmod 90 #define PR_chown 92 #define PR_chroot 161 #define PR_clock_adjtime 305 #define PR_clock_getres 229 #define PR_clock_gettime 228 #define PR_clock_nanosleep 230 #define PR_clock_settime 227 #define PR_clone 56 #define PR_close 3 #define PR_connect 42 #define PR_creat 85 #define PR_create_module 174 #define PR_delete_module 176 #define PR_dup 32 #define PR_dup2 33 #define PR_dup3 292 #define PR_epoll_create 213 #define PR_epoll_create1 291 #define PR_epoll_ctl 233 #define PR_epoll_ctl_old 214 #define PR_epoll_pwait 281 #define PR_epoll_wait 232 #define PR_epoll_wait_old 215 #define PR_eventfd 284 #define PR_eventfd2 290 #define PR_execve 59 #define PR_exit 60 #define PR_exit_group 231 #define PR_faccessat 269 #define PR_fadvise64 221 #define PR_fallocate 285 #define PR_fanotify_init 300 #define PR_fanotify_mark 301 #define PR_fchdir 81 #define PR_fchmod 91 #define PR_fchmodat 268 #define PR_fchown 93 #define PR_fchownat 260 #define PR_fcntl 72 #define PR_fdatasync 75 #define PR_fgetxattr 193 #define PR_flistxattr 196 #define PR_flock 73 #define PR_fork 57 #define PR_fremovexattr 199 #define PR_fsetxattr 190 #define PR_fstat 5 #define PR_fstatfs 138 #define PR_fsync 74 #define PR_ftruncate 77 #define PR_futex 202 #define PR_futimesat 261 #define PR_get_kernel_syms 177 #define PR_get_mempolicy 239 #define PR_get_robust_list 274 #define PR_get_thread_area 211 #define PR_getcpu 309 #define PR_getcwd 79 #define PR_getdents 78 #define PR_getdents64 217 #define PR_getegid 108 #define PR_geteuid 107 #define PR_getgid 104 #define PR_getgroups 115 #define PR_getitimer 36 #define PR_getpeername 52 #define PR_getpgid 121 #define PR_getpgrp 111 #define PR_getpid 39 #define PR_getpmsg 181 #define PR_getppid 110 #define PR_getpriority 140 #define PR_getresgid 120 #define PR_getresuid 118 #define PR_getrlimit 97 #define PR_getrusage 98 #define PR_getsid 124 #define PR_getsockname 51 #define PR_getsockopt 55 #define PR_gettid 186 #define PR_gettimeofday 96 #define PR_getuid 102 #define PR_getxattr 191 #define PR_init_module 175 #define PR_inotify_add_watch 254 #define PR_inotify_init 253 #define PR_inotify_init1 294 #define PR_inotify_rm_watch 255 #define PR_io_cancel 210 #define PR_io_destroy 207 #define PR_io_getevents 208 #define PR_io_setup 206 #define PR_io_submit 209 #define PR_ioctl 16 #define PR_ioperm 173 #define PR_iopl 172 #define PR_ioprio_get 252 #define PR_ioprio_set 251 #define PR_kcmp 312 #define PR_kexec_load 246 #define PR_keyctl 250 #define PR_kill 62 #define PR_lchown 94 #define PR_lgetxattr 192 #define PR_link 86 #define PR_linkat 265 #define PR_listen 50 #define PR_listxattr 194 #define PR_llistxattr 195 #define PR_lookup_dcookie 212 #define PR_lremovexattr 198 #define PR_lseek 8 #define PR_lsetxattr 189 #define PR_lstat 6 #define PR_madvise 28 #define PR_mbind 237 #define PR_migrate_pages 256 #define PR_mincore 27 #define PR_mkdir 83 #define PR_mkdirat 258 #define PR_mknod 133 #define PR_mknodat 259 #define PR_mlock 149 #define PR_mlockall 151 #define PR_mmap 9 #define PR_modify_ldt 154 #define PR_mount 165 #define PR_move_pages 279 #define PR_mprotect 10 #define PR_mq_getsetattr 245 #define PR_mq_notify 244 #define PR_mq_open 240 #define PR_mq_timedreceive 243 #define PR_mq_timedsend 242 #define PR_mq_unlink 241 #define PR_mremap 25 #define PR_msgctl 71 #define PR_msgget 68 #define PR_msgrcv 70 #define PR_msgsnd 69 #define PR_msync 26 #define PR_munlock 150 #define PR_munlockall 152 #define PR_munmap 11 #define PR_name_to_handle_at 303 #define PR_nanosleep 35 #define PR_newfstatat 262 #define PR_nfsservctl 180 #define PR_open 2 #define PR_open_by_handle_at 304 #define PR_openat 257 #define PR_pause 34 #define PR_perf_event_open 298 #define PR_personality 135 #define PR_pipe 22 #define PR_pipe2 293 #define PR_pivot_root 155 #define PR_poll 7 #define PR_ppoll 271 #define PR_prctl 157 #define PR_pread64 17 #define PR_preadv 295 #define PR_prlimit64 302 #define PR_process_vm_readv 310 #define PR_process_vm_writev 311 #define PR_pselect6 270 #define PR_ptrace 101 #define PR_putpmsg 182 #define PR_pwrite64 18 #define PR_pwritev 296 #define PR_query_module 178 #define PR_quotactl 179 #define PR_read 0 #define PR_readahead 187 #define PR_readlink 89 #define PR_readlinkat 267 #define PR_readv 19 #define PR_reboot 169 #define PR_recvfrom 45 #define PR_recvmmsg 299 #define PR_recvmsg 47 #define PR_remap_file_pages 216 #define PR_removexattr 197 #define PR_rename 82 #define PR_renameat 264 #define PR_request_key 249 #define PR_restart_syscall 219 #define PR_rmdir 84 #define PR_rt_sigaction 13 #define PR_rt_sigpending 127 #define PR_rt_sigprocmask 14 #define PR_rt_sigqueueinfo 129 #define PR_rt_sigreturn 15 #define PR_rt_sigsuspend 130 #define PR_rt_sigtimedwait 128 #define PR_rt_tgsigqueueinfo 297 #define PR_sched_get_priority_max 146 #define PR_sched_get_priority_min 147 #define PR_sched_getaffinity 204 #define PR_sched_getparam 143 #define PR_sched_getscheduler 145 #define PR_sched_rr_get_interval 148 #define PR_sched_setaffinity 203 #define PR_sched_setparam 142 #define PR_sched_setscheduler 144 #define PR_sched_yield 24 #define PR_security 185 #define PR_select 23 #define PR_semctl 66 #define PR_semget 64 #define PR_semop 65 #define PR_semtimedop 220 #define PR_sendfile 40 #define PR_sendmmsg 307 #define PR_sendmsg 46 #define PR_sendto 44 #define PR_set_mempolicy 238 #define PR_set_robust_list 273 #define PR_set_thread_area 205 #define PR_set_tid_address 218 #define PR_setdomainname 171 #define PR_setfsgid 123 #define PR_setfsuid 122 #define PR_setgid 106 #define PR_setgroups 116 #define PR_sethostname 170 #define PR_setitimer 38 #define PR_setns 308 #define PR_setpgid 109 #define PR_setpriority 141 #define PR_setregid 114 #define PR_setresgid 119 #define PR_setresuid 117 #define PR_setreuid 113 #define PR_setrlimit 160 #define PR_setsid 112 #define PR_setsockopt 54 #define PR_settimeofday 164 #define PR_setuid 105 #define PR_setxattr 188 #define PR_shmat 30 #define PR_shmctl 31 #define PR_shmdt 67 #define PR_shmget 29 #define PR_shutdown 48 #define PR_sigaltstack 131 #define PR_signalfd 282 #define PR_signalfd4 289 #define PR_socket 41 #define PR_socketpair 53 #define PR_splice 275 #define PR_stat 4 #define PR_statfs 137 #define PR_swapoff 168 #define PR_swapon 167 #define PR_symlink 88 #define PR_symlinkat 266 #define PR_sync 162 #define PR_sync_file_range 277 #define PR_syncfs 306 #define PR_sysfs 139 #define PR_sysinfo 99 #define PR_syslog 103 #define PR_tee 276 #define PR_tgkill 234 #define PR_time 201 #define PR_timer_create 222 #define PR_timer_delete 226 #define PR_timer_getoverrun 225 #define PR_timer_gettime 224 #define PR_timer_settime 223 #define PR_timerfd_create 283 #define PR_timerfd_gettime 287 #define PR_timerfd_settime 286 #define PR_times 100 #define PR_tkill 200 #define PR_truncate 76 #define PR_tuxcall 184 #define PR_umask 95 #define PR_umount2 166 #define PR_uname 63 #define PR_unlink 87 #define PR_unlinkat 263 #define PR_unshare 272 #define PR_uselib 134 #define PR_ustat 136 #define PR_utime 132 #define PR_utimensat 280 #define PR_utimes 235 #define PR_vfork 58 #define PR_vhangup 153 #define PR_vmsplice 278 #define PR_vserver 236 #define PR_wait4 61 #define PR_waitid 247 #define PR_write 1 #define PR_writev 20 /* * These following syscalls do not exist on x86_64. Note that syscall * numbers from -1 to -10 are reserved for PRoot internal usage. */ #define PR__llseek ((word_t) -16) #define PR__newselect ((word_t) -17) #define PR_arm_fadvise64_64 ((word_t) -18) #define PR_arm_sync_file_range ((word_t) -19) #define PR_bdflush ((word_t) -20) #define PR_break ((word_t) -21) #define PR_cacheflush ((word_t) -22) #define PR_chown32 ((word_t) -23) #define PR_fadvise64_64 ((word_t) -24) #define PR_fchown32 ((word_t) -25) #define PR_fcntl64 ((word_t) -26) #define PR_fstat64 ((word_t) -27) #define PR_fstatat64 ((word_t) -28) #define PR_fstatfs64 ((word_t) -29) #define PR_ftime ((word_t) -30) #define PR_ftruncate64 ((word_t) -31) #define PR_getegid32 ((word_t) -32) #define PR_geteuid32 ((word_t) -33) #define PR_getgid32 ((word_t) -34) #define PR_getgroups32 ((word_t) -35) #define PR_getresgid32 ((word_t) -36) #define PR_getresuid32 ((word_t) -37) #define PR_getuid32 ((word_t) -38) #define PR_gtty ((word_t) -39) #define PR_idle ((word_t) -40) #define PR_ipc ((word_t) -41) #define PR_lchown32 ((word_t) -42) #define PR_lock ((word_t) -43) #define PR_lstat64 ((word_t) -44) #define PR_mmap2 ((word_t) -45) #define PR_mpx ((word_t) -46) #define PR_nice ((word_t) -47) #define PR_oldfstat ((word_t) -48) #define PR_oldlstat ((word_t) -49) #define PR_oldolduname ((word_t) -50) #define PR_oldstat ((word_t) -51) #define PR_olduname ((word_t) -52) #define PR_pciconfig_iobase ((word_t) -53) #define PR_pciconfig_read ((word_t) -54) #define PR_pciconfig_write ((word_t) -55) #define PR_prof ((word_t) -56) #define PR_profil ((word_t) -57) #define PR_readdir ((word_t) -58) #define PR_recv ((word_t) -59) #define PR_send ((word_t) -60) #define PR_sendfile64 ((word_t) -61) #define PR_setfsgid32 ((word_t) -62) #define PR_setfsuid32 ((word_t) -63) #define PR_setgid32 ((word_t) -64) #define PR_setgroups32 ((word_t) -65) #define PR_setregid32 ((word_t) -66) #define PR_setresgid32 ((word_t) -67) #define PR_setresuid32 ((word_t) -68) #define PR_setreuid32 ((word_t) -69) #define PR_setuid32 ((word_t) -70) #define PR_sgetmask ((word_t) -71) #define PR_sigaction ((word_t) -72) #define PR_signal ((word_t) -73) #define PR_sigpending ((word_t) -74) #define PR_sigprocmask ((word_t) -75) #define PR_sigreturn ((word_t) -76) #define PR_sigsuspend ((word_t) -77) #define PR_socketcall ((word_t) -78) #define PR_ssetmask ((word_t) -79) #define PR_stat64 ((word_t) -80) #define PR_statfs64 ((word_t) -81) #define PR_stime ((word_t) -82) #define PR_stty ((word_t) -83) #define PR_sync_file_range2 ((word_t) -84) #define PR_truncate64 ((word_t) -85) #define PR_ugetrlimit ((word_t) -86) #define PR_ulimit ((word_t) -87) #define PR_umount ((word_t) -88) #define PR_vm86 ((word_t) -89) #define PR_vm86old ((word_t) -90) #define PR_waitpid ((word_t) -91) proot-3.0.2/src/syscall/sysnum-x32.h0000644000175000017500000004472512156552156016641 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file was generated thanks to the following command: * * cpp -dM linux/arch/x86/syscalls/../include/generated/asm/unistd_x32.h | grep '#define __NR_' | sed s/__NR_/PR_/g | sort -u */ #include "syscall/sysnum-undefined.h" #define PR_X32_SYSCALL_BIT 0x40000000 #define PR_accept (PR_X32_SYSCALL_BIT + 43) #define PR_accept4 (PR_X32_SYSCALL_BIT + 288) #define PR_access (PR_X32_SYSCALL_BIT + 21) #define PR_acct (PR_X32_SYSCALL_BIT + 163) #define PR_add_key (PR_X32_SYSCALL_BIT + 248) #define PR_adjtimex (PR_X32_SYSCALL_BIT + 159) #define PR_afs_syscall (PR_X32_SYSCALL_BIT + 183) #define PR_alarm (PR_X32_SYSCALL_BIT + 37) #define PR_arch_prctl (PR_X32_SYSCALL_BIT + 158) #define PR_bind (PR_X32_SYSCALL_BIT + 49) #define PR_brk (PR_X32_SYSCALL_BIT + 12) #define PR_capget (PR_X32_SYSCALL_BIT + 125) #define PR_capset (PR_X32_SYSCALL_BIT + 126) #define PR_chdir (PR_X32_SYSCALL_BIT + 80) #define PR_chmod (PR_X32_SYSCALL_BIT + 90) #define PR_chown (PR_X32_SYSCALL_BIT + 92) #define PR_chroot (PR_X32_SYSCALL_BIT + 161) #define PR_clock_adjtime (PR_X32_SYSCALL_BIT + 305) #define PR_clock_getres (PR_X32_SYSCALL_BIT + 229) #define PR_clock_gettime (PR_X32_SYSCALL_BIT + 228) #define PR_clock_nanosleep (PR_X32_SYSCALL_BIT + 230) #define PR_clock_settime (PR_X32_SYSCALL_BIT + 227) #define PR_clone (PR_X32_SYSCALL_BIT + 56) #define PR_close (PR_X32_SYSCALL_BIT + 3) #define PR_connect (PR_X32_SYSCALL_BIT + 42) #define PR_creat (PR_X32_SYSCALL_BIT + 85) #define PR_delete_module (PR_X32_SYSCALL_BIT + 176) #define PR_dup (PR_X32_SYSCALL_BIT + 32) #define PR_dup2 (PR_X32_SYSCALL_BIT + 33) #define PR_dup3 (PR_X32_SYSCALL_BIT + 292) #define PR_epoll_create (PR_X32_SYSCALL_BIT + 213) #define PR_epoll_create1 (PR_X32_SYSCALL_BIT + 291) #define PR_epoll_ctl (PR_X32_SYSCALL_BIT + 233) #define PR_epoll_pwait (PR_X32_SYSCALL_BIT + 281) #define PR_epoll_wait (PR_X32_SYSCALL_BIT + 232) #define PR_eventfd (PR_X32_SYSCALL_BIT + 284) #define PR_eventfd2 (PR_X32_SYSCALL_BIT + 290) #define PR_execve (PR_X32_SYSCALL_BIT + 520) #define PR_exit (PR_X32_SYSCALL_BIT + 60) #define PR_exit_group (PR_X32_SYSCALL_BIT + 231) #define PR_faccessat (PR_X32_SYSCALL_BIT + 269) #define PR_fadvise64 (PR_X32_SYSCALL_BIT + 221) #define PR_fallocate (PR_X32_SYSCALL_BIT + 285) #define PR_fanotify_init (PR_X32_SYSCALL_BIT + 300) #define PR_fanotify_mark (PR_X32_SYSCALL_BIT + 301) #define PR_fchdir (PR_X32_SYSCALL_BIT + 81) #define PR_fchmod (PR_X32_SYSCALL_BIT + 91) #define PR_fchmodat (PR_X32_SYSCALL_BIT + 268) #define PR_fchown (PR_X32_SYSCALL_BIT + 93) #define PR_fchownat (PR_X32_SYSCALL_BIT + 260) #define PR_fcntl (PR_X32_SYSCALL_BIT + 72) #define PR_fdatasync (PR_X32_SYSCALL_BIT + 75) #define PR_fgetxattr (PR_X32_SYSCALL_BIT + 193) #define PR_flistxattr (PR_X32_SYSCALL_BIT + 196) #define PR_flock (PR_X32_SYSCALL_BIT + 73) #define PR_fork (PR_X32_SYSCALL_BIT + 57) #define PR_fremovexattr (PR_X32_SYSCALL_BIT + 199) #define PR_fsetxattr (PR_X32_SYSCALL_BIT + 190) #define PR_fstat (PR_X32_SYSCALL_BIT + 5) #define PR_fstatfs (PR_X32_SYSCALL_BIT + 138) #define PR_fsync (PR_X32_SYSCALL_BIT + 74) #define PR_ftruncate (PR_X32_SYSCALL_BIT + 77) #define PR_futex (PR_X32_SYSCALL_BIT + 202) #define PR_futimesat (PR_X32_SYSCALL_BIT + 261) #define PR_get_mempolicy (PR_X32_SYSCALL_BIT + 239) #define PR_get_robust_list (PR_X32_SYSCALL_BIT + 531) #define PR_getcpu (PR_X32_SYSCALL_BIT + 309) #define PR_getcwd (PR_X32_SYSCALL_BIT + 79) #define PR_getdents (PR_X32_SYSCALL_BIT + 78) #define PR_getdents64 (PR_X32_SYSCALL_BIT + 217) #define PR_getegid (PR_X32_SYSCALL_BIT + 108) #define PR_geteuid (PR_X32_SYSCALL_BIT + 107) #define PR_getgid (PR_X32_SYSCALL_BIT + 104) #define PR_getgroups (PR_X32_SYSCALL_BIT + 115) #define PR_getitimer (PR_X32_SYSCALL_BIT + 36) #define PR_getpeername (PR_X32_SYSCALL_BIT + 52) #define PR_getpgid (PR_X32_SYSCALL_BIT + 121) #define PR_getpgrp (PR_X32_SYSCALL_BIT + 111) #define PR_getpid (PR_X32_SYSCALL_BIT + 39) #define PR_getpmsg (PR_X32_SYSCALL_BIT + 181) #define PR_getppid (PR_X32_SYSCALL_BIT + 110) #define PR_getpriority (PR_X32_SYSCALL_BIT + 140) #define PR_getresgid (PR_X32_SYSCALL_BIT + 120) #define PR_getresuid (PR_X32_SYSCALL_BIT + 118) #define PR_getrlimit (PR_X32_SYSCALL_BIT + 97) #define PR_getrusage (PR_X32_SYSCALL_BIT + 98) #define PR_getsid (PR_X32_SYSCALL_BIT + 124) #define PR_getsockname (PR_X32_SYSCALL_BIT + 51) #define PR_getsockopt (PR_X32_SYSCALL_BIT + 542) #define PR_gettid (PR_X32_SYSCALL_BIT + 186) #define PR_gettimeofday (PR_X32_SYSCALL_BIT + 96) #define PR_getuid (PR_X32_SYSCALL_BIT + 102) #define PR_getxattr (PR_X32_SYSCALL_BIT + 191) #define PR_init_module (PR_X32_SYSCALL_BIT + 175) #define PR_inotify_add_watch (PR_X32_SYSCALL_BIT + 254) #define PR_inotify_init (PR_X32_SYSCALL_BIT + 253) #define PR_inotify_init1 (PR_X32_SYSCALL_BIT + 294) #define PR_inotify_rm_watch (PR_X32_SYSCALL_BIT + 255) #define PR_io_cancel (PR_X32_SYSCALL_BIT + 210) #define PR_io_destroy (PR_X32_SYSCALL_BIT + 207) #define PR_io_getevents (PR_X32_SYSCALL_BIT + 208) #define PR_io_setup (PR_X32_SYSCALL_BIT + 206) #define PR_io_submit (PR_X32_SYSCALL_BIT + 209) #define PR_ioctl (PR_X32_SYSCALL_BIT + 514) #define PR_ioperm (PR_X32_SYSCALL_BIT + 173) #define PR_iopl (PR_X32_SYSCALL_BIT + 172) #define PR_ioprio_get (PR_X32_SYSCALL_BIT + 252) #define PR_ioprio_set (PR_X32_SYSCALL_BIT + 251) #define PR_kcmp (PR_X32_SYSCALL_BIT + 312) #define PR_kexec_load (PR_X32_SYSCALL_BIT + 528) #define PR_keyctl (PR_X32_SYSCALL_BIT + 250) #define PR_kill (PR_X32_SYSCALL_BIT + 62) #define PR_lchown (PR_X32_SYSCALL_BIT + 94) #define PR_lgetxattr (PR_X32_SYSCALL_BIT + 192) #define PR_link (PR_X32_SYSCALL_BIT + 86) #define PR_linkat (PR_X32_SYSCALL_BIT + 265) #define PR_listen (PR_X32_SYSCALL_BIT + 50) #define PR_listxattr (PR_X32_SYSCALL_BIT + 194) #define PR_llistxattr (PR_X32_SYSCALL_BIT + 195) #define PR_lookup_dcookie (PR_X32_SYSCALL_BIT + 212) #define PR_lremovexattr (PR_X32_SYSCALL_BIT + 198) #define PR_lseek (PR_X32_SYSCALL_BIT + 8) #define PR_lsetxattr (PR_X32_SYSCALL_BIT + 189) #define PR_lstat (PR_X32_SYSCALL_BIT + 6) #define PR_madvise (PR_X32_SYSCALL_BIT + 28) #define PR_mbind (PR_X32_SYSCALL_BIT + 237) #define PR_migrate_pages (PR_X32_SYSCALL_BIT + 256) #define PR_mincore (PR_X32_SYSCALL_BIT + 27) #define PR_mkdir (PR_X32_SYSCALL_BIT + 83) #define PR_mkdirat (PR_X32_SYSCALL_BIT + 258) #define PR_mknod (PR_X32_SYSCALL_BIT + 133) #define PR_mknodat (PR_X32_SYSCALL_BIT + 259) #define PR_mlock (PR_X32_SYSCALL_BIT + 149) #define PR_mlockall (PR_X32_SYSCALL_BIT + 151) #define PR_mmap (PR_X32_SYSCALL_BIT + 9) #define PR_modify_ldt (PR_X32_SYSCALL_BIT + 154) #define PR_mount (PR_X32_SYSCALL_BIT + 165) #define PR_move_pages (PR_X32_SYSCALL_BIT + 533) #define PR_mprotect (PR_X32_SYSCALL_BIT + 10) #define PR_mq_getsetattr (PR_X32_SYSCALL_BIT + 245) #define PR_mq_notify (PR_X32_SYSCALL_BIT + 527) #define PR_mq_open (PR_X32_SYSCALL_BIT + 240) #define PR_mq_timedreceive (PR_X32_SYSCALL_BIT + 243) #define PR_mq_timedsend (PR_X32_SYSCALL_BIT + 242) #define PR_mq_unlink (PR_X32_SYSCALL_BIT + 241) #define PR_mremap (PR_X32_SYSCALL_BIT + 25) #define PR_msgctl (PR_X32_SYSCALL_BIT + 71) #define PR_msgget (PR_X32_SYSCALL_BIT + 68) #define PR_msgrcv (PR_X32_SYSCALL_BIT + 70) #define PR_msgsnd (PR_X32_SYSCALL_BIT + 69) #define PR_msync (PR_X32_SYSCALL_BIT + 26) #define PR_munlock (PR_X32_SYSCALL_BIT + 150) #define PR_munlockall (PR_X32_SYSCALL_BIT + 152) #define PR_munmap (PR_X32_SYSCALL_BIT + 11) #define PR_name_to_handle_at (PR_X32_SYSCALL_BIT + 303) #define PR_nanosleep (PR_X32_SYSCALL_BIT + 35) #define PR_newfstatat (PR_X32_SYSCALL_BIT + 262) #define PR_open (PR_X32_SYSCALL_BIT + 2) #define PR_open_by_handle_at (PR_X32_SYSCALL_BIT + 304) #define PR_openat (PR_X32_SYSCALL_BIT + 257) #define PR_pause (PR_X32_SYSCALL_BIT + 34) #define PR_perf_event_open (PR_X32_SYSCALL_BIT + 298) #define PR_personality (PR_X32_SYSCALL_BIT + 135) #define PR_pipe (PR_X32_SYSCALL_BIT + 22) #define PR_pipe2 (PR_X32_SYSCALL_BIT + 293) #define PR_pivot_root (PR_X32_SYSCALL_BIT + 155) #define PR_poll (PR_X32_SYSCALL_BIT + 7) #define PR_ppoll (PR_X32_SYSCALL_BIT + 271) #define PR_prctl (PR_X32_SYSCALL_BIT + 157) #define PR_pread64 (PR_X32_SYSCALL_BIT + 17) #define PR_preadv (PR_X32_SYSCALL_BIT + 534) #define PR_prlimit64 (PR_X32_SYSCALL_BIT + 302) #define PR_process_vm_readv (PR_X32_SYSCALL_BIT + 539) #define PR_process_vm_writev (PR_X32_SYSCALL_BIT + 540) #define PR_pselect6 (PR_X32_SYSCALL_BIT + 270) #define PR_ptrace (PR_X32_SYSCALL_BIT + 521) #define PR_putpmsg (PR_X32_SYSCALL_BIT + 182) #define PR_pwrite64 (PR_X32_SYSCALL_BIT + 18) #define PR_pwritev (PR_X32_SYSCALL_BIT + 535) #define PR_quotactl (PR_X32_SYSCALL_BIT + 179) #define PR_read (PR_X32_SYSCALL_BIT + 0) #define PR_readahead (PR_X32_SYSCALL_BIT + 187) #define PR_readlink (PR_X32_SYSCALL_BIT + 89) #define PR_readlinkat (PR_X32_SYSCALL_BIT + 267) #define PR_readv (PR_X32_SYSCALL_BIT + 515) #define PR_reboot (PR_X32_SYSCALL_BIT + 169) #define PR_recvfrom (PR_X32_SYSCALL_BIT + 517) #define PR_recvmmsg (PR_X32_SYSCALL_BIT + 537) #define PR_recvmsg (PR_X32_SYSCALL_BIT + 519) #define PR_remap_file_pages (PR_X32_SYSCALL_BIT + 216) #define PR_removexattr (PR_X32_SYSCALL_BIT + 197) #define PR_rename (PR_X32_SYSCALL_BIT + 82) #define PR_renameat (PR_X32_SYSCALL_BIT + 264) #define PR_request_key (PR_X32_SYSCALL_BIT + 249) #define PR_restart_syscall (PR_X32_SYSCALL_BIT + 219) #define PR_rmdir (PR_X32_SYSCALL_BIT + 84) #define PR_rt_sigaction (PR_X32_SYSCALL_BIT + 512) #define PR_rt_sigpending (PR_X32_SYSCALL_BIT + 522) #define PR_rt_sigprocmask (PR_X32_SYSCALL_BIT + 14) #define PR_rt_sigqueueinfo (PR_X32_SYSCALL_BIT + 524) #define PR_rt_sigreturn (PR_X32_SYSCALL_BIT + 513) #define PR_rt_sigsuspend (PR_X32_SYSCALL_BIT + 130) #define PR_rt_sigtimedwait (PR_X32_SYSCALL_BIT + 523) #define PR_rt_tgsigqueueinfo (PR_X32_SYSCALL_BIT + 536) #define PR_sched_get_priority_max (PR_X32_SYSCALL_BIT + 146) #define PR_sched_get_priority_min (PR_X32_SYSCALL_BIT + 147) #define PR_sched_getaffinity (PR_X32_SYSCALL_BIT + 204) #define PR_sched_getparam (PR_X32_SYSCALL_BIT + 143) #define PR_sched_getscheduler (PR_X32_SYSCALL_BIT + 145) #define PR_sched_rr_get_interval (PR_X32_SYSCALL_BIT + 148) #define PR_sched_setaffinity (PR_X32_SYSCALL_BIT + 203) #define PR_sched_setparam (PR_X32_SYSCALL_BIT + 142) #define PR_sched_setscheduler (PR_X32_SYSCALL_BIT + 144) #define PR_sched_yield (PR_X32_SYSCALL_BIT + 24) #define PR_security (PR_X32_SYSCALL_BIT + 185) #define PR_select (PR_X32_SYSCALL_BIT + 23) #define PR_semctl (PR_X32_SYSCALL_BIT + 66) #define PR_semget (PR_X32_SYSCALL_BIT + 64) #define PR_semop (PR_X32_SYSCALL_BIT + 65) #define PR_semtimedop (PR_X32_SYSCALL_BIT + 220) #define PR_sendfile (PR_X32_SYSCALL_BIT + 40) #define PR_sendmmsg (PR_X32_SYSCALL_BIT + 538) #define PR_sendmsg (PR_X32_SYSCALL_BIT + 518) #define PR_sendto (PR_X32_SYSCALL_BIT + 44) #define PR_set_mempolicy (PR_X32_SYSCALL_BIT + 238) #define PR_set_robust_list (PR_X32_SYSCALL_BIT + 530) #define PR_set_tid_address (PR_X32_SYSCALL_BIT + 218) #define PR_setdomainname (PR_X32_SYSCALL_BIT + 171) #define PR_setfsgid (PR_X32_SYSCALL_BIT + 123) #define PR_setfsuid (PR_X32_SYSCALL_BIT + 122) #define PR_setgid (PR_X32_SYSCALL_BIT + 106) #define PR_setgroups (PR_X32_SYSCALL_BIT + 116) #define PR_sethostname (PR_X32_SYSCALL_BIT + 170) #define PR_setitimer (PR_X32_SYSCALL_BIT + 38) #define PR_setns (PR_X32_SYSCALL_BIT + 308) #define PR_setpgid (PR_X32_SYSCALL_BIT + 109) #define PR_setpriority (PR_X32_SYSCALL_BIT + 141) #define PR_setregid (PR_X32_SYSCALL_BIT + 114) #define PR_setresgid (PR_X32_SYSCALL_BIT + 119) #define PR_setresuid (PR_X32_SYSCALL_BIT + 117) #define PR_setreuid (PR_X32_SYSCALL_BIT + 113) #define PR_setrlimit (PR_X32_SYSCALL_BIT + 160) #define PR_setsid (PR_X32_SYSCALL_BIT + 112) #define PR_setsockopt (PR_X32_SYSCALL_BIT + 541) #define PR_settimeofday (PR_X32_SYSCALL_BIT + 164) #define PR_setuid (PR_X32_SYSCALL_BIT + 105) #define PR_setxattr (PR_X32_SYSCALL_BIT + 188) #define PR_shmat (PR_X32_SYSCALL_BIT + 30) #define PR_shmctl (PR_X32_SYSCALL_BIT + 31) #define PR_shmdt (PR_X32_SYSCALL_BIT + 67) #define PR_shmget (PR_X32_SYSCALL_BIT + 29) #define PR_shutdown (PR_X32_SYSCALL_BIT + 48) #define PR_sigaltstack (PR_X32_SYSCALL_BIT + 525) #define PR_signalfd (PR_X32_SYSCALL_BIT + 282) #define PR_signalfd4 (PR_X32_SYSCALL_BIT + 289) #define PR_socket (PR_X32_SYSCALL_BIT + 41) #define PR_socketpair (PR_X32_SYSCALL_BIT + 53) #define PR_splice (PR_X32_SYSCALL_BIT + 275) #define PR_stat (PR_X32_SYSCALL_BIT + 4) #define PR_statfs (PR_X32_SYSCALL_BIT + 137) #define PR_swapoff (PR_X32_SYSCALL_BIT + 168) #define PR_swapon (PR_X32_SYSCALL_BIT + 167) #define PR_symlink (PR_X32_SYSCALL_BIT + 88) #define PR_symlinkat (PR_X32_SYSCALL_BIT + 266) #define PR_sync (PR_X32_SYSCALL_BIT + 162) #define PR_sync_file_range (PR_X32_SYSCALL_BIT + 277) #define PR_syncfs (PR_X32_SYSCALL_BIT + 306) #define PR_sysfs (PR_X32_SYSCALL_BIT + 139) #define PR_sysinfo (PR_X32_SYSCALL_BIT + 99) #define PR_syslog (PR_X32_SYSCALL_BIT + 103) #define PR_tee (PR_X32_SYSCALL_BIT + 276) #define PR_tgkill (PR_X32_SYSCALL_BIT + 234) #define PR_time (PR_X32_SYSCALL_BIT + 201) #define PR_timer_create (PR_X32_SYSCALL_BIT + 526) #define PR_timer_delete (PR_X32_SYSCALL_BIT + 226) #define PR_timer_getoverrun (PR_X32_SYSCALL_BIT + 225) #define PR_timer_gettime (PR_X32_SYSCALL_BIT + 224) #define PR_timer_settime (PR_X32_SYSCALL_BIT + 223) #define PR_timerfd_create (PR_X32_SYSCALL_BIT + 283) #define PR_timerfd_gettime (PR_X32_SYSCALL_BIT + 287) #define PR_timerfd_settime (PR_X32_SYSCALL_BIT + 286) #define PR_times (PR_X32_SYSCALL_BIT + 100) #define PR_tkill (PR_X32_SYSCALL_BIT + 200) #define PR_truncate (PR_X32_SYSCALL_BIT + 76) #define PR_tuxcall (PR_X32_SYSCALL_BIT + 184) #define PR_umask (PR_X32_SYSCALL_BIT + 95) #define PR_umount2 (PR_X32_SYSCALL_BIT + 166) #define PR_uname (PR_X32_SYSCALL_BIT + 63) #define PR_unlink (PR_X32_SYSCALL_BIT + 87) #define PR_unlinkat (PR_X32_SYSCALL_BIT + 263) #define PR_unshare (PR_X32_SYSCALL_BIT + 272) #define PR_ustat (PR_X32_SYSCALL_BIT + 136) #define PR_utime (PR_X32_SYSCALL_BIT + 132) #define PR_utimensat (PR_X32_SYSCALL_BIT + 280) #define PR_utimes (PR_X32_SYSCALL_BIT + 235) #define PR_vfork (PR_X32_SYSCALL_BIT + 58) #define PR_vhangup (PR_X32_SYSCALL_BIT + 153) #define PR_vmsplice (PR_X32_SYSCALL_BIT + 532) #define PR_wait4 (PR_X32_SYSCALL_BIT + 61) #define PR_waitid (PR_X32_SYSCALL_BIT + 529) #define PR_write (PR_X32_SYSCALL_BIT + 1) #define PR_writev (PR_X32_SYSCALL_BIT + 516) /* * These following syscalls are x32 specific, this list was generated * thanks to the following command: * * cpp -dM linux/arch/x86/syscalls/../include/generated/asm/unistd_64_x32.h | grep '#define __NR_x32_' | sed s/__NR_x32_/PR_X32_/g | sort -u */ #define PR_x32_execve 520 #define PR_x32_get_robust_list 531 #define PR_x32_ioctl 514 #define PR_x32_kexec_load 528 #define PR_x32_move_pages 533 #define PR_x32_mq_notify 527 #define PR_x32_preadv 534 #define PR_x32_process_vm_readv 539 #define PR_x32_process_vm_writev 540 #define PR_x32_ptrace 521 #define PR_x32_pwritev 535 #define PR_x32_readv 515 #define PR_x32_recvfrom 517 #define PR_x32_recvmmsg 537 #define PR_x32_recvmsg 519 #define PR_x32_rt_sigaction 512 #define PR_x32_rt_sigpending 522 #define PR_x32_rt_sigqueueinfo 524 #define PR_x32_rt_sigreturn 513 #define PR_x32_rt_sigtimedwait 523 #define PR_x32_rt_tgsigqueueinfo 536 #define PR_x32_sendmmsg 538 #define PR_x32_sendmsg 518 #define PR_x32_set_robust_list 530 #define PR_x32_sigaltstack 525 #define PR_x32_timer_create 526 #define PR_x32_vmsplice 532 #define PR_x32_waitid 529 #define PR_x32_writev 516 /* * These following syscalls do not exist on x32. Note that syscall * numbers from -1 to -10 are reserved for PRoot internal usage. */ #define PR__llseek ((word_t) -11) #define PR__newselect ((word_t) -12) #define PR__sysctl ((word_t) -13) #define PR_bdflush ((word_t) -14) #define PR_break ((word_t) -15) #define PR_cacheflush ((word_t) -16) #define PR_chown32 ((word_t) -17) #define PR_create_module ((word_t) -18) #define PR_epoll_ctl_old ((word_t) -19) #define PR_epoll_wait_old ((word_t) -20) #define PR_fadvise64_64 ((word_t) -21) #define PR_fchown32 ((word_t) -22) #define PR_fcntl64 ((word_t) -23) #define PR_fstat64 ((word_t) -24) #define PR_fstatat64 ((word_t) -25) #define PR_fstatfs64 ((word_t) -26) #define PR_ftime ((word_t) -27) #define PR_ftruncate64 ((word_t) -28) #define PR_get_kernel_syms ((word_t) -29) #define PR_get_thread_area ((word_t) -30) #define PR_getegid32 ((word_t) -31) #define PR_geteuid32 ((word_t) -32) #define PR_getgid32 ((word_t) -33) #define PR_getgroups32 ((word_t) -34) #define PR_getresgid32 ((word_t) -35) #define PR_getresuid32 ((word_t) -36) #define PR_getuid32 ((word_t) -37) #define PR_gtty ((word_t) -38) #define PR_idle ((word_t) -39) #define PR_ipc ((word_t) -40) #define PR_lchown32 ((word_t) -41) #define PR_lock ((word_t) -42) #define PR_lstat64 ((word_t) -43) #define PR_mmap2 ((word_t) -44) #define PR_mpx ((word_t) -45) #define PR_nfsservctl ((word_t) -46) #define PR_nice ((word_t) -47) #define PR_oldfstat ((word_t) -48) #define PR_oldlstat ((word_t) -49) #define PR_oldolduname ((word_t) -50) #define PR_oldstat ((word_t) -51) #define PR_olduname ((word_t) -52) #define PR_pciconfig_iobase ((word_t) -53) #define PR_pciconfig_read ((word_t) -54) #define PR_pciconfig_write ((word_t) -55) #define PR_profil ((word_t) -56) #define PR_prof ((word_t) -57) #define PR_query_module ((word_t) -58) #define PR_readdir ((word_t) -59) #define PR_recv ((word_t) -60) #define PR_sendfile64 ((word_t) -61) #define PR_send ((word_t) -62) #define PR_set_thread_area ((word_t) -63) #define PR_setfsgid32 ((word_t) -64) #define PR_setfsuid32 ((word_t) -65) #define PR_setgid32 ((word_t) -66) #define PR_setgroups32 ((word_t) -67) #define PR_setregid32 ((word_t) -68) #define PR_setresgid32 ((word_t) -69) #define PR_setresuid32 ((word_t) -70) #define PR_setreuid32 ((word_t) -71) #define PR_setuid32 ((word_t) -72) #define PR_sgetmask ((word_t) -73) #define PR_sigaction ((word_t) -74) #define PR_signal ((word_t) -75) #define PR_sigpending ((word_t) -76) #define PR_sigprocmask ((word_t) -77) #define PR_sigreturn ((word_t) -78) #define PR_sigsuspend ((word_t) -79) #define PR_socketcall ((word_t) -80) #define PR_ssetmask ((word_t) -81) #define PR_stat64 ((word_t) -82) #define PR_statfs64 ((word_t) -83) #define PR_stime ((word_t) -84) #define PR_stty ((word_t) -85) #define PR_sync_file_range2 ((word_t) -86) #define PR_truncate64 ((word_t) -87) #define PR_ugetrlimit ((word_t) -88) #define PR_ulimit ((word_t) -89) #define PR_umount ((word_t) -90) #define PR_uselib ((word_t) -91) #define PR_vm86old ((word_t) -92) #define PR_vm86 ((word_t) -93) #define PR_vserver ((word_t) -94) #define PR_waitpid ((word_t) -95) proot-3.0.2/src/syscall/enter.c0000644000175000017500000002452712156552156015777 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ syscall_number = peek_reg(tracee, ORIGINAL, SYSARG_NUM); switch (syscall_number) { default: /* Nothing to do. */ status = 0; break; case PR_execve: status = translate_execve(tracee); break; case PR_fchdir: case PR_chdir: { char *tmp; /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; if (syscall_number == PR_chdir) { status = get_sysarg_path(tracee, path, SYSARG_1); if (status < 0) break; /* The ending "." ensures canonicalize() will report * an error if path does not exist or if it is not a * directory. */ if (path[0] != '/') status = join_paths(3, newpath, tracee->fs->cwd, path, "."); else status = join_paths(2, newpath, path, "."); if (status < 0) break; /* Initiale state for canonicalization. */ strcpy(path, "/"); status = canonicalize(tracee, newpath, true, path, 0); if (status < 0) break; } else { dirfd = peek_reg(tracee, CURRENT, SYSARG_1); /* Sadly this method doesn't detranslate statefully, * this means that there's an ambiguity when several * bindings are from the same host path: * * $ proot -m /tmp:/a -m /tmp:/b fchdir_getcwd /a * /b * * $ proot -m /tmp:/b -m /tmp:/a fchdir_getcwd /a * /a * * A solution would be to follow each file descriptor * just like it is done for cwd. */ status = translate_path(tracee, path, dirfd, ".", true); if (status < 0) break; status = detranslate_path(tracee, path, NULL); if (status < 0) break; } /* Remove the trailing "/" or "/.". */ chop_finality(path); tmp = talloc_strdup(tracee->fs, path); if (tmp == NULL) { status = -ENOMEM; break; } TALLOC_FREE(tracee->fs->cwd); tracee->fs->cwd = tmp; talloc_set_name_const(tracee->fs->cwd, "$cwd"); poke_reg(tracee, SYSARG_NUM, SYSCALL_AVOIDER); status = 0; break; } case PR_bind: case PR_connect: { word_t address; word_t size; address = peek_reg(tracee, CURRENT, SYSARG_2); size = peek_reg(tracee, CURRENT, SYSARG_3); status = translate_socketcall_enter(tracee, &address, size); if (status <= 0) break; poke_reg(tracee, SYSARG_2, address); poke_reg(tracee, SYSARG_3, sizeof(struct sockaddr_un)); status = 0; break; } case PR_accept: case PR_accept4: case PR_getsockname: case PR_getpeername:{ int size; /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; /* Remember: PEEK_MEM puts -errno in status and breaks if an * error occured. */ size = (int) PEEK_MEM(peek_reg(tracee, ORIGINAL, SYSARG_3)); /* The "size" argument is both used as an input parameter * (max. size) and as an output parameter (actual size). The * exit stage needs to know the max. size to not overwrite * anything, that's why it is copied in the 6th argument * (unused) before the kernel updates it. */ poke_reg(tracee, SYSARG_6, size); status = 0; break; } case PR_socketcall: { word_t args_addr; word_t sock_addr_saved; word_t sock_addr; word_t size_addr; word_t size; /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; args_addr = peek_reg(tracee, CURRENT, SYSARG_2); switch (peek_reg(tracee, CURRENT, SYSARG_1)) { case SYS_BIND: case SYS_CONNECT: /* Handle these cases below. */ status = 1; break; case SYS_ACCEPT: case SYS_ACCEPT4: case SYS_GETSOCKNAME: case SYS_GETPEERNAME: /* Remember: PEEK_MEM puts -errno in status and breaks * if an error occured. */ size_addr = PEEK_MEM(SYSARG_ADDR(3)); size = (int) PEEK_MEM(size_addr); /* See case PR_accept for explanation. */ poke_reg(tracee, SYSARG_6, size); status = 0; break; default: status = 0; break; } /* An error occured or there's nothing else to do. */ if (status <= 0) break; /* Remember: PEEK_MEM puts -errno in status and breaks if an * error occured. */ sock_addr = PEEK_MEM(SYSARG_ADDR(2)); size = PEEK_MEM(SYSARG_ADDR(3)); sock_addr_saved = sock_addr; status = translate_socketcall_enter(tracee, &sock_addr, size); if (status <= 0) break; /* These parameters are used/restored at the exit stage. */ poke_reg(tracee, SYSARG_5, sock_addr_saved); poke_reg(tracee, SYSARG_6, size); /* Remember: POKE_MEM puts -errno in status and breaks if an * error occured. */ POKE_MEM(SYSARG_ADDR(2), sock_addr); POKE_MEM(SYSARG_ADDR(3), sizeof(struct sockaddr_un)); status = 0; break; } case PR_access: case PR_acct: case PR_chmod: case PR_chown: case PR_chown32: case PR_chroot: case PR_getxattr: case PR_listxattr: case PR_mknod: case PR_oldstat: case PR_creat: case PR_removexattr: case PR_setxattr: case PR_stat: case PR_stat64: case PR_statfs: case PR_statfs64: case PR_swapoff: case PR_swapon: case PR_truncate: case PR_truncate64: case PR_umount: case PR_umount2: case PR_uselib: case PR_utime: case PR_utimes: status = translate_sysarg(tracee, SYSARG_1, REGULAR); break; case PR_open: flags = peek_reg(tracee, CURRENT, SYSARG_2); if ( ((flags & O_NOFOLLOW) != 0) || ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)) status = translate_sysarg(tracee, SYSARG_1, SYMLINK); else status = translate_sysarg(tracee, SYSARG_1, REGULAR); break; case PR_faccessat: case PR_fchmodat: case PR_fchownat: case PR_fstatat64: case PR_newfstatat: case PR_utimensat: case PR_name_to_handle_at: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; flags = ( syscall_number == PR_fchownat || syscall_number == PR_name_to_handle_at) ? peek_reg(tracee, CURRENT, SYSARG_5) : peek_reg(tracee, CURRENT, SYSARG_4); if ((flags & AT_SYMLINK_NOFOLLOW) != 0) status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); else status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); break; case PR_futimesat: case PR_mknodat: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); break; case PR_inotify_add_watch: flags = peek_reg(tracee, CURRENT, SYSARG_3); if ((flags & IN_DONT_FOLLOW) != 0) status = translate_sysarg(tracee, SYSARG_2, SYMLINK); else status = translate_sysarg(tracee, SYSARG_2, REGULAR); break; case PR_readlink: /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; case PR_lchown: case PR_lchown32: case PR_lgetxattr: case PR_llistxattr: case PR_lremovexattr: case PR_lsetxattr: case PR_lstat: case PR_lstat64: case PR_oldlstat: case PR_unlink: case PR_rmdir: case PR_mkdir: status = translate_sysarg(tracee, SYSARG_1, SYMLINK); break; case PR_pivot_root: status = translate_sysarg(tracee, SYSARG_1, REGULAR); if (status < 0) break; status = translate_sysarg(tracee, SYSARG_2, REGULAR); break; case PR_linkat: olddirfd = peek_reg(tracee, CURRENT, SYSARG_1); newdirfd = peek_reg(tracee, CURRENT, SYSARG_3); flags = peek_reg(tracee, CURRENT, SYSARG_5); status = get_sysarg_path(tracee, oldpath, SYSARG_2); if (status < 0) break; status = get_sysarg_path(tracee, newpath, SYSARG_4); if (status < 0) break; if ((flags & AT_SYMLINK_FOLLOW) != 0) status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, REGULAR); else status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK); if (status < 0) break; status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK); break; case PR_mount: status = get_sysarg_path(tracee, path, SYSARG_1); if (status < 0) break; /* The following check covers only 90% of the cases. */ if (path[0] == '/' || path[0] == '.') { status = translate_path2(tracee, AT_FDCWD, path, SYSARG_1, REGULAR); if (status < 0) break; } status = translate_sysarg(tracee, SYSARG_2, REGULAR); break; case PR_openat: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); flags = peek_reg(tracee, CURRENT, SYSARG_3); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; if ( ((flags & O_NOFOLLOW) != 0) || ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)) status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); else status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); break; case PR_readlinkat: /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; case PR_unlinkat: case PR_mkdirat: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); break; case PR_link: case PR_rename: status = translate_sysarg(tracee, SYSARG_1, SYMLINK); if (status < 0) break; status = translate_sysarg(tracee, SYSARG_2, SYMLINK); break; case PR_renameat: olddirfd = peek_reg(tracee, CURRENT, SYSARG_1); newdirfd = peek_reg(tracee, CURRENT, SYSARG_3); status = get_sysarg_path(tracee, oldpath, SYSARG_2); if (status < 0) break; status = get_sysarg_path(tracee, newpath, SYSARG_4); if (status < 0) break; status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK); if (status < 0) break; status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK); break; case PR_symlink: status = translate_sysarg(tracee, SYSARG_2, SYMLINK); break; case PR_symlinkat: newdirfd = peek_reg(tracee, CURRENT, SYSARG_2); status = get_sysarg_path(tracee, newpath, SYSARG_3); if (status < 0) break; status = translate_path2(tracee, newdirfd, newpath, SYSARG_3, SYMLINK); break; case PR_getcwd: case PR_uname: case PR_rt_sigreturn: case PR_sigreturn: /* Force the sysexit stage under seccomp. */ tracee->restart_how = PTRACE_SYSCALL; status = 0; break; } proot-3.0.2/src/syscall/syscall.c0000644000175000017500000002214112156552156016322 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _GNU_SOURCE /* O_NOFOLLOW in fcntl.h, clone(2), */ #include /* clone(2), */ #include /* AT_FDCWD, */ #include /* pid_t, */ #include /* assert(3), */ #include /* PATH_MAX, */ #include /* intptr_t, offsetof(3), getenv(2), */ #include /* errno(3), */ #include /* strlen(3), */ #include /* struct utsname, */ #include /* va_*, */ #include /* talloc_*, */ #include /* struct sockaddr_un, */ #include /* SYS_*, */ #include "syscall/syscall.h" #include "syscall/socket.h" #include "arch.h" #include "tracee/mem.h" #include "tracee/tracee.h" #include "tracee/reg.h" #include "tracee/abi.h" #include "path/path.h" #include "path/canon.h" #include "execve/execve.h" #include "extension/extension.h" #include "notice.h" #include "compat.h" /** * Copy in @path a C string (PATH_MAX bytes max.) from the @tracee's * memory address space pointed to by the @reg argument of the * current syscall. This function returns -errno if an error occured, * otherwise it returns the size in bytes put into the @path. */ int get_sysarg_path(const Tracee *tracee, char path[PATH_MAX], Reg reg) { int size; word_t src; src = peek_reg(tracee, CURRENT, reg); /* Check if the parameter is not NULL. Technically we should * not return an -EFAULT for this special value since it is * allowed for some syscall, utimensat(2) for instance. */ if (src == 0) { path[0] = '\0'; return 0; } /* Get the path from the tracee's memory space. */ size = read_string(tracee, path, src, PATH_MAX); if (size < 0) return size; if (size >= PATH_MAX) return -ENAMETOOLONG; path[size] = '\0'; return size; } /** * Copy @size bytes of the data pointed to by @tracer_ptr into a * @tracee's memory block and make the @reg argument of the current * syscall points to this new block. This function returns -errno if * an error occured, otherwise 0. */ int set_sysarg_data(Tracee *tracee, void *tracer_ptr, word_t size, Reg reg) { word_t tracee_ptr; int status; /* Allocate space into the tracee's memory to host the new data. */ tracee_ptr = alloc_mem(tracee, size); if (tracee_ptr == 0) return -EFAULT; /* Copy the new data into the previously allocated space. */ status = write_data(tracee, tracee_ptr, tracer_ptr, size); if (status < 0) return status; /* Make this argument point to the new data. */ poke_reg(tracee, reg, tracee_ptr); return 0; } /** * Copy @path to a @tracee's memory block and make the @reg argument * of the current syscall points to this new block. This function * returns -errno if an error occured, otherwise 0. */ int set_sysarg_path(Tracee *tracee, char path[PATH_MAX], Reg reg) { return set_sysarg_data(tracee, path, strlen(path) + 1, reg); } /** * Translate @path and put the result in the @tracee's memory address * space pointed to by the @reg argument of the current syscall. See * the documentation of translate_path() about the meaning of * @type. This function returns -errno if an error occured, otherwise * 0. */ static int translate_path2(Tracee *tracee, int dir_fd, char path[PATH_MAX], Reg reg, Type type) { char new_path[PATH_MAX]; int status; /* Special case where the argument was NULL. */ if (path[0] == '\0') return 0; /* Translate the original path. */ status = translate_path(tracee, new_path, dir_fd, path, type != SYMLINK); if (status < 0) return status; return set_sysarg_path(tracee, new_path, reg); } /** * A helper, see the comment of the function above. */ static int translate_sysarg(Tracee *tracee, Reg reg, Type type) { char old_path[PATH_MAX]; int status; /* Extract the original path. */ status = get_sysarg_path(tracee, old_path, reg); if (status < 0) return status; return translate_path2(tracee, AT_FDCWD, old_path, reg, type); } #define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) #define PEEK_MEM(addr) peek_mem(tracee, addr); \ if (errno != 0) { \ status = -errno; \ break; \ } #define POKE_MEM(addr, value) poke_mem(tracee, addr, value); \ if (errno != 0) { \ status = -errno; \ break; \ } /** * Translate the input arguments of the current @tracee's syscall in the * @tracee->pid process area. This function sets @tracee->status to * -errno if an error occured from the tracee's point-of-view (EFAULT * for instance), otherwise 0. */ static void translate_syscall_enter(Tracee *tracee) { int flags; int dirfd; int olddirfd; int newdirfd; int status; int status2; char path[PATH_MAX]; char oldpath[PATH_MAX]; char newpath[PATH_MAX]; word_t syscall_number; status = notify_extensions(tracee, SYSCALL_ENTER_START, 0, 0); if (status < 0) goto end; if (status > 0) return; /* Translate input arguments. */ switch (get_abi(tracee)) { case ABI_DEFAULT: { #include SYSNUM_HEADER #include "syscall/enter.c" break; } #ifdef SYSNUM_HEADER2 case ABI_2: { #include SYSNUM_HEADER2 #include "syscall/enter.c" break; } #endif #ifdef SYSNUM_HEADER3 case ABI_3: { #include SYSNUM_HEADER3 #include "syscall/enter.c" break; } #endif default: assert(0); } #include "syscall/sysnum-undefined.h" end: status2 = notify_extensions(tracee, SYSCALL_ENTER_END, status, 0); if (status2 < 0) status = status2; /* Remember the tracee status for the "exit" stage and avoid * the actual syscall if an error occured during the * translation. */ if (status < 0) { poke_reg(tracee, SYSARG_NUM, SYSCALL_AVOIDER); poke_reg(tracee, SYSARG_RESULT, status); tracee->status = status; } else tracee->status = 1; } /** * Translate the output arguments of the current @tracee's syscall in * the @tracee->pid process area. This function sets the result of * this syscall to @tracee->status if an error occured previously * during the translation, that is, if @tracee->status is less than 0. */ static void translate_syscall_exit(Tracee *tracee) { word_t syscall_number; word_t syscall_result; int status; status = notify_extensions(tracee, SYSCALL_EXIT_START, 0, 0); if (status < 0) { poke_reg(tracee, SYSARG_RESULT, (word_t) status); goto end; } if (status > 0) return; /* Set the tracee's errno if an error occured previously during * the translation. */ if (tracee->status < 0) { poke_reg(tracee, SYSARG_RESULT, (word_t) tracee->status); goto end; } /* Translate output arguments. */ switch (get_abi(tracee)) { case ABI_DEFAULT: { #include SYSNUM_HEADER #include "syscall/exit.c" } break; #ifdef SYSNUM_HEADER2 case ABI_2: { #include SYSNUM_HEADER2 #include "syscall/exit.c" } break; #endif #ifdef SYSNUM_HEADER3 case ABI_3: { #include SYSNUM_HEADER3 #include "syscall/exit.c" } break; #endif default: assert(0); } #include "syscall/sysnum-undefined.h" /* "status" was updated in syscall/exit.c. */ poke_reg(tracee, SYSARG_RESULT, (word_t) status); end: status = notify_extensions(tracee, SYSCALL_EXIT_END, 0, 0); if (status < 0) poke_reg(tracee, SYSARG_RESULT, (word_t) status); /* Reset the tracee's status. */ tracee->status = 0; } #undef SYSARG_ADDR #undef PEEK_MEM #undef POKE_MEM void translate_syscall(Tracee *tracee) { const bool is_enter_stage = (tracee->status == 0); int status; assert(tracee->exe != NULL); status = fetch_regs(tracee); if (status < 0) return; if (is_enter_stage) { /* Never restore original register values at the end * of this stage. */ tracee->restore_original_regs = false; print_current_regs(tracee, 3, "sysenter start"); save_current_regs(tracee, ORIGINAL); translate_syscall_enter(tracee); print_current_regs(tracee, 5, "sysenter end"); save_current_regs(tracee, MODIFIED); /* Restore tracee's stack pointer now if it won't hit * the sysexit stage (i.e. when seccomp is enabled and * there's nothing else to do). */ if (tracee->restart_how == PTRACE_CONT) { tracee->status = 0; poke_reg(tracee, STACK_POINTER, peek_reg(tracee, ORIGINAL, STACK_POINTER)); } } else { /* By default, restore original register values at the * end of this stage. */ tracee->restore_original_regs = true; print_current_regs(tracee, 5, "sysexit start"); translate_syscall_exit(tracee); print_current_regs(tracee, 4, "sysexit end"); } (void) push_regs(tracee); } proot-3.0.2/src/syscall/socket.h0000644000175000017500000000217512156552156016152 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef SOCKET_H #define SOCKET_H #include "arch.h" /* word_t */ #include "tracee/tracee.h" int translate_socketcall_enter(Tracee *tracee, word_t *sock_addr, int size); int translate_socketcall_exit(Tracee *tracee, word_t sock_addr, word_t size_addr, word_t max_size); #endif /* SOCKET_H */ proot-3.0.2/src/syscall/sysnum-sh4.h0000644000175000017500000002455212156552156016717 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file was generated thanks to the following command: * * cpp -dM linux/arch/sh/include/asm/unistd_32.h | grep '#define __NR_' | sed s/__NR_/PR_/g | sort -u */ #include "syscall/sysnum-undefined.h" #define PR__llseek 140 #define PR__newselect 142 #define PR__sysctl 149 #define PR_accept 344 #define PR_accept4 358 #define PR_access 33 #define PR_acct 51 #define PR_add_key 285 #define PR_adjtimex 124 #define PR_alarm 27 #define PR_bdflush 134 #define PR_bind 341 #define PR_brk 45 #define PR_cacheflush 123 #define PR_capget 184 #define PR_capset 185 #define PR_chdir 12 #define PR_chmod 15 #define PR_chown 182 #define PR_chown32 212 #define PR_chroot 61 #define PR_clock_adjtime 361 #define PR_clock_getres (PR_timer_create+7) #define PR_clock_gettime (PR_timer_create+6) #define PR_clock_nanosleep (PR_timer_create+8) #define PR_clock_settime (PR_timer_create+5) #define PR_clone 120 #define PR_close 6 #define PR_connect 342 #define PR_creat 8 #define PR_delete_module 129 #define PR_dup 41 #define PR_dup2 63 #define PR_dup3 330 #define PR_epoll_create 254 #define PR_epoll_create1 329 #define PR_epoll_ctl 255 #define PR_epoll_pwait 319 #define PR_epoll_wait 256 #define PR_eventfd 323 #define PR_eventfd2 328 #define PR_execve 11 #define PR_exit 1 #define PR_exit_group 252 #define PR_faccessat 307 #define PR_fadvise64 250 #define PR_fadvise64_64 272 #define PR_fallocate 324 #define PR_fanotify_init 337 #define PR_fanotify_mark 338 #define PR_fchdir 133 #define PR_fchmod 94 #define PR_fchmodat 306 #define PR_fchown 95 #define PR_fchown32 207 #define PR_fchownat 298 #define PR_fcntl 55 #define PR_fcntl64 221 #define PR_fdatasync 148 #define PR_fgetxattr 231 #define PR_flistxattr 234 #define PR_flock 143 #define PR_fork 2 #define PR_fremovexattr 237 #define PR_fsetxattr 228 #define PR_fstat 108 #define PR_fstat64 197 #define PR_fstatat64 300 #define PR_fstatfs 100 #define PR_fstatfs64 269 #define PR_fsync 118 #define PR_ftruncate 93 #define PR_ftruncate64 194 #define PR_futex 240 #define PR_futimesat 299 #define PR_get_mempolicy 275 #define PR_get_robust_list 312 #define PR_getcpu 318 #define PR_getcwd 183 #define PR_getdents 141 #define PR_getdents64 220 #define PR_getegid 50 #define PR_getegid32 202 #define PR_geteuid 49 #define PR_geteuid32 201 #define PR_getgid 47 #define PR_getgid32 200 #define PR_getgroups 80 #define PR_getgroups32 205 #define PR_getitimer 105 #define PR_getpeername 346 #define PR_getpgid 132 #define PR_getpgrp 65 #define PR_getpid 20 #define PR_getppid 64 #define PR_getpriority 96 #define PR_getresgid 171 #define PR_getresgid32 211 #define PR_getresuid 165 #define PR_getresuid32 209 #define PR_getrlimit 76 #define PR_getrusage 77 #define PR_getsid 147 #define PR_getsockname 345 #define PR_getsockopt 354 #define PR_gettid 224 #define PR_gettimeofday 78 #define PR_getuid 24 #define PR_getuid32 199 #define PR_getxattr 229 #define PR_init_module 128 #define PR_inotify_add_watch 291 #define PR_inotify_init 290 #define PR_inotify_init1 332 #define PR_inotify_rm_watch 292 #define PR_io_cancel 249 #define PR_io_destroy 246 #define PR_io_getevents 247 #define PR_io_setup 245 #define PR_io_submit 248 #define PR_ioctl 54 #define PR_ioprio_get 289 #define PR_ioprio_set 288 #define PR_ipc 117 #define PR_kexec_load 283 #define PR_keyctl 287 #define PR_kill 37 #define PR_lchown 16 #define PR_lchown32 198 #define PR_lgetxattr 230 #define PR_link 9 #define PR_linkat 303 #define PR_listen 343 #define PR_listxattr 232 #define PR_llistxattr 233 #define PR_lookup_dcookie 253 #define PR_lremovexattr 236 #define PR_lseek 19 #define PR_lsetxattr 227 #define PR_lstat 107 #define PR_lstat64 196 #define PR_madvise 219 #define PR_mbind 274 #define PR_migrate_pages 294 #define PR_mincore 218 #define PR_mkdir 39 #define PR_mkdirat 296 #define PR_mknod 14 #define PR_mknodat 297 #define PR_mlock 150 #define PR_mlockall 152 #define PR_mmap 90 #define PR_mmap2 192 #define PR_mount 21 #define PR_move_pages 317 #define PR_mprotect 125 #define PR_mq_getsetattr (PR_mq_open+5) #define PR_mq_notify (PR_mq_open+4) #define PR_mq_open 277 #define PR_mq_timedreceive (PR_mq_open+3) #define PR_mq_timedsend (PR_mq_open+2) #define PR_mq_unlink (PR_mq_open+1) #define PR_mremap 163 #define PR_msync 144 #define PR_munlock 151 #define PR_munlockall 153 #define PR_munmap 91 #define PR_name_to_handle_at 359 #define PR_nanosleep 162 #define PR_nfsservctl 169 #define PR_nice 34 #define PR_oldfstat 28 #define PR_oldlstat 84 #define PR_oldstat 18 #define PR_olduname 109 #define PR_open 5 #define PR_open_by_handle_at 360 #define PR_openat 295 #define PR_pause 29 #define PR_perf_event_open 336 #define PR_personality 136 #define PR_pipe 42 #define PR_pipe2 331 #define PR_pivot_root 217 #define PR_poll 168 #define PR_ppoll 309 #define PR_prctl 172 #define PR_pread64 180 #define PR_preadv 333 #define PR_prlimit64 339 #define PR_process_vm_readv 365 #define PR_process_vm_writev 366 #define PR_pselect6 308 #define PR_ptrace 26 #define PR_pwrite64 181 #define PR_pwritev 334 #define PR_quotactl 131 #define PR_read 3 #define PR_readahead 225 #define PR_readdir 89 #define PR_readlink 85 #define PR_readlinkat 305 #define PR_readv 145 #define PR_reboot 88 #define PR_recv 350 #define PR_recvfrom 351 #define PR_recvmmsg 357 #define PR_recvmsg 356 #define PR_remap_file_pages 257 #define PR_removexattr 235 #define PR_rename 38 #define PR_renameat 302 #define PR_request_key 286 #define PR_restart_syscall 0 #define PR_rmdir 40 #define PR_rt_sigaction 174 #define PR_rt_sigpending 176 #define PR_rt_sigprocmask 175 #define PR_rt_sigqueueinfo 178 #define PR_rt_sigreturn 173 #define PR_rt_sigsuspend 179 #define PR_rt_sigtimedwait 177 #define PR_rt_tgsigqueueinfo 335 #define PR_sched_get_priority_max 159 #define PR_sched_get_priority_min 160 #define PR_sched_getaffinity 242 #define PR_sched_getparam 155 #define PR_sched_getscheduler 157 #define PR_sched_rr_get_interval 161 #define PR_sched_setaffinity 241 #define PR_sched_setparam 154 #define PR_sched_setscheduler 156 #define PR_sched_yield 158 #define PR_send 348 #define PR_sendfile 187 #define PR_sendfile64 239 #define PR_sendmmsg 363 #define PR_sendmsg 355 #define PR_sendto 349 #define PR_set_mempolicy 276 #define PR_set_robust_list 311 #define PR_set_tid_address 258 #define PR_setdomainname 121 #define PR_setfsgid 139 #define PR_setfsgid32 216 #define PR_setfsuid 138 #define PR_setfsuid32 215 #define PR_setgid 46 #define PR_setgid32 214 #define PR_setgroups 81 #define PR_setgroups32 206 #define PR_sethostname 74 #define PR_setitimer 104 #define PR_setns 364 #define PR_setpgid 57 #define PR_setpriority 97 #define PR_setregid 71 #define PR_setregid32 204 #define PR_setresgid 170 #define PR_setresgid32 210 #define PR_setresuid 164 #define PR_setresuid32 208 #define PR_setreuid 70 #define PR_setreuid32 203 #define PR_setrlimit 75 #define PR_setsid 66 #define PR_setsockopt 353 #define PR_settimeofday 79 #define PR_setuid 23 #define PR_setuid32 213 #define PR_setxattr 226 #define PR_sgetmask 68 #define PR_shutdown 352 #define PR_sigaction 67 #define PR_sigaltstack 186 #define PR_signal 48 #define PR_signalfd 321 #define PR_signalfd4 327 #define PR_sigpending 73 #define PR_sigprocmask 126 #define PR_sigreturn 119 #define PR_sigsuspend 72 #define PR_socket 340 #define PR_socketcall 102 #define PR_socketpair 347 #define PR_splice 313 #define PR_ssetmask 69 #define PR_stat 106 #define PR_stat64 195 #define PR_statfs 99 #define PR_statfs64 268 #define PR_stime 25 #define PR_swapoff 115 #define PR_swapon 87 #define PR_symlink 83 #define PR_symlinkat 304 #define PR_sync 36 #define PR_sync_file_range 314 #define PR_syncfs 362 #define PR_sysfs 135 #define PR_sysinfo 116 #define PR_syslog 103 #define PR_tee 315 #define PR_tgkill 270 #define PR_time 13 #define PR_timer_create 259 #define PR_timer_delete (PR_timer_create+4) #define PR_timer_getoverrun (PR_timer_create+3) #define PR_timer_gettime (PR_timer_create+2) #define PR_timer_settime (PR_timer_create+1) #define PR_timerfd_create 322 #define PR_timerfd_gettime 326 #define PR_timerfd_settime 325 #define PR_times 43 #define PR_tkill 238 #define PR_truncate 92 #define PR_truncate64 193 #define PR_ugetrlimit 191 #define PR_umask 60 #define PR_umount 22 #define PR_umount2 52 #define PR_uname 122 #define PR_unlink 10 #define PR_unlinkat 301 #define PR_unshare 310 #define PR_uselib 86 #define PR_ustat 62 #define PR_utime 30 #define PR_utimensat 320 #define PR_utimes 271 #define PR_vfork 190 #define PR_vhangup 111 #define PR_vmsplice 316 #define PR_wait4 114 #define PR_waitid 284 #define PR_waitpid 7 #define PR_write 4 #define PR_writev 146 /* * These following syscalls do not exist on sh4. Note that syscall * numbers from -1 to -10 are reserved for PRoot internal usage. */ #define PR_afs_syscall ((word_t) -16) #define PR_arch_prctl ((word_t) -17) #define PR_arm_fadvise64_64 ((word_t) -18) #define PR_arm_sync_file_range ((word_t) -19) #define PR_break ((word_t) -20) #define PR_create_module ((word_t) -21) #define PR_epoll_ctl_old ((word_t) -22) #define PR_epoll_wait_old ((word_t) -23) #define PR_ftime ((word_t) -24) #define PR_get_kernel_syms ((word_t) -25) #define PR_get_thread_area ((word_t) -26) #define PR_getpmsg ((word_t) -27) #define PR_gtty ((word_t) -28) #define PR_idle ((word_t) -29) #define PR_ioperm ((word_t) -30) #define PR_iopl ((word_t) -31) #define PR_lock ((word_t) -32) #define PR_modify_ldt ((word_t) -33) #define PR_mpx ((word_t) -34) #define PR_msgctl ((word_t) -35) #define PR_msgget ((word_t) -36) #define PR_msgrcv ((word_t) -37) #define PR_msgsnd ((word_t) -38) #define PR_newfstatat ((word_t) -39) #define PR_oldolduname ((word_t) -40) #define PR_pciconfig_iobase ((word_t) -41) #define PR_pciconfig_read ((word_t) -42) #define PR_pciconfig_write ((word_t) -43) #define PR_prof ((word_t) -44) #define PR_profil ((word_t) -45) #define PR_putpmsg ((word_t) -46) #define PR_query_module ((word_t) -47) #define PR_security ((word_t) -48) #define PR_select ((word_t) -49) #define PR_semctl ((word_t) -50) #define PR_semget ((word_t) -51) #define PR_semop ((word_t) -52) #define PR_semtimedop ((word_t) -53) #define PR_set_thread_area ((word_t) -54) #define PR_shmat ((word_t) -55) #define PR_shmctl ((word_t) -56) #define PR_shmdt ((word_t) -57) #define PR_shmget ((word_t) -58) #define PR_stty ((word_t) -59) #define PR_sync_file_range2 ((word_t) -60) #define PR_tuxcall ((word_t) -61) #define PR_ulimit ((word_t) -62) #define PR_vm86 ((word_t) -63) #define PR_vm86old ((word_t) -64) #define PR_vserver ((word_t) -65) proot-3.0.2/src/syscall/sysnum-undefined.h0000644000175000017500000002057312156552156020161 0ustar ivoireivoire#undef PR_ARM_BASE #undef PR_ARM_breakpoint #undef PR_ARM_cacheflush #undef PR_ARM_set_tls #undef PR_ARM_usr26 #undef PR_ARM_usr32 #undef PR_X32_SYSCALL_BIT #undef PR__llseek #undef PR__newselect #undef PR__sysctl #undef PR_accept #undef PR_accept4 #undef PR_access #undef PR_acct #undef PR_add_key #undef PR_adjtimex #undef PR_afs_syscall #undef PR_alarm #undef PR_arch_prctl #undef PR_arch_specific_syscall #undef PR_arm_fadvise64_64 #undef PR_arm_sync_file_range #undef PR_bdflush #undef PR_bind #undef PR_break #undef PR_brk #undef PR_cacheflush #undef PR_capget #undef PR_capset #undef PR_chdir #undef PR_chmod #undef PR_chown #undef PR_chown32 #undef PR_chroot #undef PR_clock_adjtime #undef PR_clock_getres #undef PR_clock_gettime #undef PR_clock_nanosleep #undef PR_clock_settime #undef PR_clone #undef PR_close #undef PR_connect #undef PR_creat #undef PR_create_module #undef PR_delete_module #undef PR_dup #undef PR_dup2 #undef PR_dup3 #undef PR_epoll_create #undef PR_epoll_create1 #undef PR_epoll_ctl #undef PR_epoll_ctl_old #undef PR_epoll_pwait #undef PR_epoll_wait #undef PR_epoll_wait_old #undef PR_eventfd #undef PR_eventfd2 #undef PR_execve #undef PR_exit #undef PR_exit_group #undef PR_faccessat #undef PR_fadvise64 #undef PR_fadvise64_64 #undef PR_fallocate #undef PR_fanotify_init #undef PR_fanotify_mark #undef PR_fchdir #undef PR_fchmod #undef PR_fchmodat #undef PR_fchown #undef PR_fchown32 #undef PR_fchownat #undef PR_fcntl #undef PR_fcntl64 #undef PR_fdatasync #undef PR_fgetxattr #undef PR_flistxattr #undef PR_flock #undef PR_fork #undef PR_fremovexattr #undef PR_fsetxattr #undef PR_fstat #undef PR_fstat64 #undef PR_fstatat64 #undef PR_fstatfs #undef PR_fstatfs64 #undef PR_fsync #undef PR_ftime #undef PR_ftruncate #undef PR_ftruncate64 #undef PR_futex #undef PR_futimesat #undef PR_get_kernel_syms #undef PR_get_mempolicy #undef PR_get_robust_list #undef PR_get_thread_area #undef PR_getcpu #undef PR_getcwd #undef PR_getdents #undef PR_getdents64 #undef PR_getegid #undef PR_getegid32 #undef PR_geteuid #undef PR_geteuid32 #undef PR_getgid #undef PR_getgid32 #undef PR_getgroups #undef PR_getgroups32 #undef PR_getitimer #undef PR_getpeername #undef PR_getpgid #undef PR_getpgrp #undef PR_getpid #undef PR_getpmsg #undef PR_getppid #undef PR_getpriority #undef PR_getresgid #undef PR_getresgid32 #undef PR_getresuid #undef PR_getresuid32 #undef PR_getrlimit #undef PR_getrusage #undef PR_getsid #undef PR_getsockname #undef PR_getsockopt #undef PR_gettid #undef PR_gettimeofday #undef PR_getuid #undef PR_getuid32 #undef PR_getxattr #undef PR_gtty #undef PR_idle #undef PR_init_module #undef PR_inotify_add_watch #undef PR_inotify_init #undef PR_inotify_init1 #undef PR_inotify_rm_watch #undef PR_io_cancel #undef PR_io_destroy #undef PR_io_getevents #undef PR_io_setup #undef PR_io_submit #undef PR_ioctl #undef PR_ioperm #undef PR_iopl #undef PR_ioprio_get #undef PR_ioprio_set #undef PR_ipc #undef PR_kcmp #undef PR_kexec_load #undef PR_keyctl #undef PR_kill #undef PR_lchown #undef PR_lchown32 #undef PR_lgetxattr #undef PR_link #undef PR_linkat #undef PR_listen #undef PR_listxattr #undef PR_llistxattr #undef PR_lock #undef PR_lookup_dcookie #undef PR_lremovexattr #undef PR_lseek #undef PR_lsetxattr #undef PR_lstat #undef PR_lstat64 #undef PR_madvise #undef PR_mbind #undef PR_migrate_pages #undef PR_mincore #undef PR_mkdir #undef PR_mkdirat #undef PR_mknod #undef PR_mknodat #undef PR_mlock #undef PR_mlockall #undef PR_mmap #undef PR_mmap2 #undef PR_modify_ldt #undef PR_mount #undef PR_move_pages #undef PR_mprotect #undef PR_mpx #undef PR_mq_getsetattr #undef PR_mq_notify #undef PR_mq_open #undef PR_mq_timedreceive #undef PR_mq_timedsend #undef PR_mq_unlink #undef PR_mremap #undef PR_msgctl #undef PR_msgget #undef PR_msgrcv #undef PR_msgsnd #undef PR_msync #undef PR_munlock #undef PR_munlockall #undef PR_munmap #undef PR_name_to_handle_at #undef PR_nanosleep #undef PR_newfstatat #undef PR_nfsservctl #undef PR_nice #undef PR_oldfstat #undef PR_oldlstat #undef PR_oldolduname #undef PR_oldstat #undef PR_olduname #undef PR_open #undef PR_open_by_handle_at #undef PR_openat #undef PR_pause #undef PR_pciconfig_iobase #undef PR_pciconfig_read #undef PR_pciconfig_write #undef PR_perf_event_open #undef PR_personality #undef PR_pipe #undef PR_pipe2 #undef PR_pivot_root #undef PR_poll #undef PR_ppoll #undef PR_prctl #undef PR_pread64 #undef PR_preadv #undef PR_prlimit64 #undef PR_process_vm_readv #undef PR_process_vm_writev #undef PR_prof #undef PR_profil #undef PR_pselect6 #undef PR_ptrace #undef PR_putpmsg #undef PR_pwrite64 #undef PR_pwritev #undef PR_query_module #undef PR_quotactl #undef PR_read #undef PR_readahead #undef PR_readdir #undef PR_readlink #undef PR_readlinkat #undef PR_readv #undef PR_reboot #undef PR_recv #undef PR_recvfrom #undef PR_recvmmsg #undef PR_recvmsg #undef PR_remap_file_pages #undef PR_removexattr #undef PR_rename #undef PR_renameat #undef PR_request_key #undef PR_restart_syscall #undef PR_rmdir #undef PR_rt_sigaction #undef PR_rt_sigpending #undef PR_rt_sigprocmask #undef PR_rt_sigqueueinfo #undef PR_rt_sigreturn #undef PR_rt_sigsuspend #undef PR_rt_sigtimedwait #undef PR_rt_tgsigqueueinfo #undef PR_sched_get_priority_max #undef PR_sched_get_priority_min #undef PR_sched_getaffinity #undef PR_sched_getparam #undef PR_sched_getscheduler #undef PR_sched_rr_get_interval #undef PR_sched_setaffinity #undef PR_sched_setparam #undef PR_sched_setscheduler #undef PR_sched_yield #undef PR_security #undef PR_select #undef PR_semctl #undef PR_semget #undef PR_semop #undef PR_semtimedop #undef PR_send #undef PR_sendfile #undef PR_sendfile64 #undef PR_sendmmsg #undef PR_sendmsg #undef PR_sendto #undef PR_set_mempolicy #undef PR_set_robust_list #undef PR_set_thread_area #undef PR_set_tid_address #undef PR_setdomainname #undef PR_setfsgid #undef PR_setfsgid32 #undef PR_setfsuid #undef PR_setfsuid32 #undef PR_setgid #undef PR_setgid32 #undef PR_setgroups #undef PR_setgroups32 #undef PR_sethostname #undef PR_setitimer #undef PR_setns #undef PR_setpgid #undef PR_setpriority #undef PR_setregid #undef PR_setregid32 #undef PR_setresgid #undef PR_setresgid32 #undef PR_setresuid #undef PR_setresuid32 #undef PR_setreuid #undef PR_setreuid32 #undef PR_setrlimit #undef PR_setsid #undef PR_setsockopt #undef PR_settimeofday #undef PR_setuid #undef PR_setuid32 #undef PR_setxattr #undef PR_sgetmask #undef PR_shmat #undef PR_shmctl #undef PR_shmdt #undef PR_shmget #undef PR_shutdown #undef PR_sigaction #undef PR_sigaltstack #undef PR_signal #undef PR_signalfd #undef PR_signalfd4 #undef PR_sigpending #undef PR_sigprocmask #undef PR_sigreturn #undef PR_sigsuspend #undef PR_socket #undef PR_socketcall #undef PR_socketpair #undef PR_splice #undef PR_ssetmask #undef PR_stat #undef PR_stat64 #undef PR_statfs #undef PR_statfs64 #undef PR_stime #undef PR_stty #undef PR_swapoff #undef PR_swapon #undef PR_symlink #undef PR_symlinkat #undef PR_sync #undef PR_sync_file_range #undef PR_sync_file_range2 #undef PR_syncfs #undef PR_syscalls #undef PR_sysfs #undef PR_sysinfo #undef PR_syslog #undef PR_tee #undef PR_tgkill #undef PR_time #undef PR_timer_create #undef PR_timer_delete #undef PR_timer_getoverrun #undef PR_timer_gettime #undef PR_timer_settime #undef PR_timerfd_create #undef PR_timerfd_gettime #undef PR_timerfd_settime #undef PR_times #undef PR_tkill #undef PR_truncate #undef PR_truncate64 #undef PR_tuxcall #undef PR_ugetrlimit #undef PR_ulimit #undef PR_umask #undef PR_umount #undef PR_umount2 #undef PR_uname #undef PR_unlink #undef PR_unlinkat #undef PR_unshare #undef PR_uselib #undef PR_ustat #undef PR_utime #undef PR_utimensat #undef PR_utimes #undef PR_vfork #undef PR_vhangup #undef PR_vm86 #undef PR_vm86old #undef PR_vmsplice #undef PR_vserver #undef PR_wait4 #undef PR_waitid #undef PR_waitpid #undef PR_write #undef PR_writev #undef PR_x32_execve #undef PR_x32_get_robust_list #undef PR_x32_ioctl #undef PR_x32_kexec_load #undef PR_x32_move_pages #undef PR_x32_mq_notify #undef PR_x32_preadv #undef PR_x32_process_vm_readv #undef PR_x32_process_vm_writev #undef PR_x32_ptrace #undef PR_x32_pwritev #undef PR_x32_readv #undef PR_x32_recvfrom #undef PR_x32_recvmmsg #undef PR_x32_recvmsg #undef PR_x32_rt_sigaction #undef PR_x32_rt_sigpending #undef PR_x32_rt_sigqueueinfo #undef PR_x32_rt_sigreturn #undef PR_x32_rt_sigtimedwait #undef PR_x32_rt_tgsigqueueinfo #undef PR_x32_sendmmsg #undef PR_x32_sendmsg #undef PR_x32_set_robust_list #undef PR_x32_sigaltstack #undef PR_x32_timer_create #undef PR_x32_vmsplice #undef PR_x32_waitid #undef PR_x32_writev proot-3.0.2/src/tracee/0000755000175000017500000000000012156552156014275 5ustar ivoireivoireproot-3.0.2/src/tracee/reg.h0000644000175000017500000000270712156552156015231 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef TRACEE_REG_H #define TRACEE_REG_H #include "tracee/tracee.h" #include "arch.h" typedef enum { SYSARG_NUM = 0, SYSARG_1, SYSARG_2, SYSARG_3, SYSARG_4, SYSARG_5, SYSARG_6, SYSARG_RESULT, STACK_POINTER, } Reg; extern int fetch_regs(Tracee *tracee); extern int push_regs(Tracee *tracee); extern word_t peek_reg(const Tracee *tracee, RegVersion version, Reg reg); extern void poke_reg(Tracee *tracee, Reg reg, word_t value); extern void print_current_regs(Tracee *tracee, int verbose_level, const char *message); extern void save_current_regs(Tracee *tracee, RegVersion version); #endif /* TRACEE_REG_H */ proot-3.0.2/src/tracee/event.h0000644000175000017500000000203112156552156015563 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef TRACEE_EVENT_H #define TRACEE_EVENT_H #include #include "tracee/tracee.h" extern int launch_process(Tracee *tracee); extern int event_loop(); #endif /* TRACEE_EVENT_H */ proot-3.0.2/src/tracee/array.h0000644000175000017500000000521312156552156015565 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef ARRAY_H #define ARRAY_H #include #include "tracee/reg.h" #include "arch.h" typedef struct array Array; typedef int (*read_item_t)(Array *array, size_t index, void **value); typedef int (*write_item_t)(Array *array, size_t index, const void *value); typedef int (*compare_item_t)(Array *array, size_t index, const void *reference); typedef int (*sizeof_item_t)(Array *array, size_t index); typedef struct array_entry ArrayEntry; struct array { ArrayEntry *_cache; size_t length; read_item_t read_item; write_item_t write_item; compare_item_t compare_item; sizeof_item_t sizeof_item; }; static inline int read_item(Array *array, size_t index, void **value) { return array->read_item(array, index, value); } static inline int write_item(Array *array, size_t index, const void *value) { return array->write_item(array, index, value); } static inline int compare_item(Array *array, size_t index, const void *reference) { return array->compare_item(array, index, reference); } static inline int sizeof_item(Array *array, size_t index) { return array->sizeof_item(array, index); } extern int find_item(Array *array, const void *reference); extern int resize_array(Array *array, size_t index, ssize_t nb_delta_entries); extern int fetch_array(Tracee *tracee, Array **array, Reg reg, size_t nb_entries); extern int push_array(Array *array, Reg reg); extern int read_item_data(Array *array, size_t index, void **value); extern int read_item_string(Array *array, size_t index, char **value); extern int write_item_string(Array *array, size_t index, const char *value); extern int write_items(Array *array, size_t index, size_t nb_items, ...); extern int compare_item_generic(Array *array, size_t index, const void *reference); extern int sizeof_item_string(Array *array, size_t index); #endif /* ARRAY_H */ proot-3.0.2/src/tracee/mem.c0000644000175000017500000003320012156552156015215 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _GNU_SOURCE #include /* ptrace(2), PTRACE_*, */ #include /* pid_t, size_t, */ #include /* NULL, */ #include /* offsetof(), */ #include /* struct user*, */ #include /* errno, */ #include /* assert(3), */ #include /* waitpid(2), */ #include /* memcpy(3), */ #include /* uint8_t, */ #include /* process_vm_*, struct iovec, */ #include /* sysconf(3), */ #include "tracee/mem.h" #include "tracee/abi.h" #include "arch.h" /* word_t, NO_MISALIGNED_ACCESS */ #include "build.h" /* HAVE_PROCESS_VM, */ #include "notice.h" /** * Load the word at the given @address, potentially *not* aligned. */ static inline word_t load_word(const void *address) { #ifdef NO_MISALIGNED_ACCESS if (((word_t)address) % sizeof(word_t) == 0) return *(word_t *)address; else { word_t value; memcpy(&value, address, sizeof(word_t)); return value; } #else return *(word_t *)address; #endif } /** * Store the word with the given @value to the given @address, * potentially *not* aligned. */ static inline void store_word(void *address, word_t value) { #ifdef NO_MISALIGNED_ACCESS if (((word_t)address) % sizeof(word_t) == 0) *((word_t *)address) = value; else memcpy(address, &value, sizeof(word_t)); #else *((word_t *)address) = value; #endif } /** * Copy @size bytes from the buffer @src_tracer to the address * @dest_tracee within the memory space of the @tracee process. It * returns -errno if an error occured, otherwise 0. */ int write_data(const Tracee *tracee, word_t dest_tracee, const void *src_tracer, word_t size) { word_t *src = (word_t *)src_tracer; word_t *dest = (word_t *)dest_tracee; long status; word_t word, i, j; word_t nb_trailing_bytes; word_t nb_full_words; uint8_t *last_dest_word; uint8_t *last_src_word; #if defined(HAVE_PROCESS_VM) struct iovec local; struct iovec remote; local.iov_base = src; local.iov_len = size; remote.iov_base = dest; remote.iov_len = size; status = process_vm_writev(tracee->pid, &local, 1, &remote, 1, 0); if ((size_t) status == size) return 0; /* Fallback to ptrace if something went wrong. */ #endif /* HAVE_PROCESS_VM */ nb_trailing_bytes = size % sizeof(word_t); nb_full_words = (size - nb_trailing_bytes) / sizeof(word_t); /* Copy one word by one word, except for the last one. */ for (i = 0; i < nb_full_words; i++) { status = ptrace(PTRACE_POKEDATA, tracee->pid, dest + i, load_word(&src[i])); if (status < 0) { notice(tracee, WARNING, SYSTEM, "ptrace(POKEDATA)"); return -EFAULT; } } /* Copy the bytes in the last word carefully since we have to * overwrite only the relevant ones. */ word = ptrace(PTRACE_PEEKDATA, tracee->pid, dest + i, NULL); if (errno != 0) { notice(tracee, WARNING, SYSTEM, "ptrace(PEEKDATA)"); return -EFAULT; } last_dest_word = (uint8_t *)&word; last_src_word = (uint8_t *)&src[i]; for (j = 0; j < nb_trailing_bytes; j++) last_dest_word[j] = last_src_word[j]; status = ptrace(PTRACE_POKEDATA, tracee->pid, dest + i, word); if (status < 0) { notice(tracee, WARNING, SYSTEM, "ptrace(POKEDATA)"); return -EFAULT; } return 0; } /** * Gather the @src_tracer_count buffers pointed to by @src_tracer to * the address @dest_tracee within the memory space of the @tracee * process. This function returns -errno if an error occured, * otherwise 0. */ int writev_data(const Tracee *tracee, word_t dest_tracee, const struct iovec *src_tracer, int src_tracer_count) { size_t size; int status; int i; #if defined(HAVE_PROCESS_VM) struct iovec remote; for (i = 0, size = 0; i < src_tracer_count; i++) size += src_tracer[i].iov_len; remote.iov_base = (word_t *)dest_tracee; remote.iov_len = size; status = process_vm_writev(tracee->pid, src_tracer, src_tracer_count, &remote, 1, 0); if ((size_t) status == size) return 0; /* Fallback to iterative-write if something went wrong. */ #endif /* HAVE_PROCESS_VM */ for (i = 0, size = 0; i < src_tracer_count; i++) { status = write_data(tracee, dest_tracee + size, src_tracer[i].iov_base, src_tracer[i].iov_len); if (status < 0) return status; size += src_tracer[i].iov_len; } return 0; } /** * Copy @size bytes to the buffer @dest_tracer from the address * @src_tracee within the memory space of the @tracee process. It * returns -errno if an error occured, otherwise 0. */ int read_data(const Tracee *tracee, void *dest_tracer, word_t src_tracee, word_t size) { word_t *src = (word_t *)src_tracee; word_t *dest = (word_t *)dest_tracer; word_t nb_trailing_bytes; word_t nb_full_words; word_t word, i, j; uint8_t *last_src_word; uint8_t *last_dest_word; #if defined(HAVE_PROCESS_VM) long status; struct iovec local; struct iovec remote; local.iov_base = dest; local.iov_len = size; remote.iov_base = src; remote.iov_len = size; status = process_vm_readv(tracee->pid, &local, 1, &remote, 1, 0); if ((size_t) status == size) return 0; /* Fallback to ptrace if something went wrong. */ #endif /* HAVE_PROCESS_VM */ nb_trailing_bytes = size % sizeof(word_t); nb_full_words = (size - nb_trailing_bytes) / sizeof(word_t); /* Copy one word by one word, except for the last one. */ for (i = 0; i < nb_full_words; i++) { word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); if (errno != 0) { notice(tracee, WARNING, SYSTEM, "ptrace(PEEKDATA)"); return -EFAULT; } store_word(&dest[i], word); } /* Copy the bytes from the last word carefully since we have * to not overwrite the bytes lying beyond @dest_tracer. */ word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); if (errno != 0) { notice(tracee, WARNING, SYSTEM, "ptrace(PEEKDATA)"); return -EFAULT; } last_dest_word = (uint8_t *)&dest[i]; last_src_word = (uint8_t *)&word; for (j = 0; j < nb_trailing_bytes; j++) last_dest_word[j] = last_src_word[j]; return 0; } /** * Copy to @dest_tracer at most @max_size bytes from the string * pointed to by @src_tracee within the memory space of the @tracee * process. This function returns -errno on error, otherwise * it returns the number in bytes of the string, including the * end-of-string terminator. */ int read_string(const Tracee *tracee, char *dest_tracer, word_t src_tracee, word_t max_size) { word_t *src = (word_t *)src_tracee; word_t *dest = (word_t *)dest_tracer; word_t nb_trailing_bytes; word_t nb_full_words; word_t word, i, j; uint8_t *src_word; uint8_t *dest_word; #if defined(HAVE_PROCESS_VM) /* [process_vm] system calls do not check the memory regions * in the remote process until just before doing the * read/write. Consequently, a partial read/write [1] may * result if one of the remote_iov elements points to an * invalid memory region in the remote process. No further * reads/writes will be attempted beyond that point. Keep * this in mind when attempting to read data of unknown length * (such as C strings that are null-terminated) from a remote * process, by avoiding spanning memory pages (typically 4KiB) * in a single remote iovec element. (Instead, split the * remote read into two remote_iov elements and have them * merge back into a single write local_iov entry. The first * read entry goes up to the page boundary, while the second * starts on the next page boundary.). * * [1] Partial transfers apply at the granularity of iovec * elements. These system calls won't perform a partial * transfer that splits a single iovec element. * * -- man 2 process_vm_readv */ long status; size_t size; size_t offset; struct iovec local; struct iovec remote; static size_t chunk_size = 0; static uintptr_t chunk_mask; /* A chunk shall not cross a page boundary. */ if (chunk_size == 0) { chunk_size = sysconf(_SC_PAGESIZE); chunk_size = (chunk_size > 0 && chunk_size < 1024 ? chunk_size : 1024); chunk_mask = ~(chunk_size - 1); } /* Read the string by chunk. */ offset = 0; do { uintptr_t current_chunk = (src_tracee + offset) & chunk_mask; uintptr_t next_chunk = current_chunk + chunk_size; /* Compute the number of bytes available up to the * next chunk or up to max_size. */ size = next_chunk - (src_tracee + offset); size = (size < max_size - offset ? size : max_size - offset); local.iov_base = (uint8_t *)dest + offset; local.iov_len = size; remote.iov_base = (uint8_t *)src + offset; remote.iov_len = size; status = process_vm_readv(tracee->pid, &local, 1, &remote, 1, 0); if ((size_t) status != size) goto fallback; status = strnlen(local.iov_base, size); if ((size_t) status < size) { size = offset + status + 1; assert(size <= max_size); return size; } offset += size; } while (offset < max_size); assert(offset == max_size); /* Fallback to ptrace if something went wrong. */ fallback: #endif /* HAVE_PROCESS_VM */ nb_trailing_bytes = max_size % sizeof(word_t); nb_full_words = (max_size - nb_trailing_bytes) / sizeof(word_t); /* Copy one word by one word, except for the last one. */ for (i = 0; i < nb_full_words; i++) { word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); if (errno != 0) return -EFAULT; store_word(&dest[i], word); /* Stop once an end-of-string is detected. */ src_word = (uint8_t *)&word; for (j = 0; j < sizeof(word_t); j++) if (src_word[j] == '\0') return i * sizeof(word_t) + j + 1; } /* Copy the bytes from the last word carefully since we have * to not overwrite the bytes lying beyond @dest_tracer. */ word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); if (errno != 0) return -EFAULT; dest_word = (uint8_t *)&dest[i]; src_word = (uint8_t *)&word; for (j = 0; j < nb_trailing_bytes; j++) { dest_word[j] = src_word[j]; if (src_word[j] == '\0') break; } return i * sizeof(word_t) + j + 1; } /** * Return the value of the word at the given @address in the @tracee's * memory space. The caller must test errno to check if an error * occured. */ word_t peek_mem(const Tracee *tracee, word_t address) { word_t result = 0; #if defined(HAVE_PROCESS_VM) int status; struct iovec local; struct iovec remote; local.iov_base = &result; local.iov_len = sizeof_word(tracee); remote.iov_base = (void *)address; remote.iov_len = sizeof_word(tracee); errno = 0; status = process_vm_readv(tracee->pid, &local, 1, &remote, 1, 0); if (status > 0) return result; /* Fallback to ptrace if something went wrong. */ #endif errno = 0; result = (word_t) ptrace(PTRACE_PEEKDATA, tracee->pid, address, NULL); /* Use only the 32 LSB when running a 32-bit process on a * 64-bit kernel. */ if (is_32on64_mode(tracee)) result &= 0xFFFFFFFF; return result; } /** * Set the word at the given @address in the @tracee's memory space to * the given @value. The caller must test errno to check if an error * occured. */ void poke_mem(const Tracee *tracee, word_t address, word_t value) { word_t tmp; #if defined(HAVE_PROCESS_VM) int status; struct iovec local; struct iovec remote; /* Note: &value points to the 32 LSB on 64-bit little-endian * architecture. */ local.iov_base = &value; local.iov_len = sizeof_word(tracee); remote.iov_base = (void *)address; remote.iov_len = sizeof_word(tracee); errno = 0; status = process_vm_writev(tracee->pid, &local, 1, &remote, 1, 0); if (status > 0) return; /* Fallback to ptrace if something went wrong. */ #endif /* Don't overwrite the 32 MSB when running a 32-bit process on * a 64-bit kernel. */ if (is_32on64_mode(tracee)) { errno = 0; tmp = (word_t) ptrace(PTRACE_PEEKDATA, tracee->pid, address, NULL); if (errno != 0) return; value |= (tmp & 0xFFFFFFFF00000000ULL); } errno = 0; (void) ptrace(PTRACE_POKEDATA, tracee->pid, address, value); return; } /** * Allocate @size bytes in the @tracee's memory space. This function * returns the address of the allocated memory in the @tracee's memory * space, otherwise 0 if an error occured. */ word_t alloc_mem(Tracee *tracee, ssize_t size) { word_t stack_pointer; /* Get the current value of the stack pointer from the tracee's * USER area. */ stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER); /* Some ABIs specify an amount of bytes after the stack * pointer that shall not be used by anything but the compiler * (for optimization purpose). */ if (stack_pointer == peek_reg(tracee, ORIGINAL, STACK_POINTER)) size += RED_ZONE_SIZE; /* Sanity check. */ if ( (size > 0 && stack_pointer <= (word_t) size) || (size < 0 && stack_pointer >= ULONG_MAX + size)) { notice(tracee, WARNING, INTERNAL, "integer under/overflow detected in %s", __FUNCTION__); return 0; } /* Remember the stack grows downward. */ stack_pointer -= size; /* Set the new value of the stack pointer in the tracee's USER * area. */ poke_reg(tracee, STACK_POINTER, stack_pointer); return stack_pointer; } proot-3.0.2/src/tracee/reg.c0000644000175000017500000002150212156552156015216 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* off_t */ #include /* struct user*, */ #include /* ptrace(2), PTRACE*, */ #include /* assert(3), */ #include /* errno(3), */ #include /* offsetof(), */ #include /* *int*_t(), */ #include /* ULONG_MAX, */ #include /* memcpy(3), */ #include /* struct iovec, */ #include "arch.h" #if defined(ARCH_ARM64) #include /* NT_PRSTATUS */ #endif #include "tracee/reg.h" #include "tracee/abi.h" #include "notice.h" /** * Compute the offset of the register @reg_name in the USER area. */ #define USER_REGS_OFFSET(reg_name) \ (offsetof(struct user, regs) \ + offsetof(struct user_regs_struct, reg_name)) #define REG(tracee, version, index) \ (*(word_t*) (((uint8_t *) &tracee->_regs[version]) + reg_offset[index])) /* Specify the ABI registers (syscall argument passing, stack pointer). * See sysdeps/unix/sysv/linux/${ARCH}/syscall.S from the GNU C Library. */ #if defined(ARCH_X86_64) static off_t reg_offset[] = { [SYSARG_NUM] = USER_REGS_OFFSET(orig_rax), [SYSARG_1] = USER_REGS_OFFSET(rdi), [SYSARG_2] = USER_REGS_OFFSET(rsi), [SYSARG_3] = USER_REGS_OFFSET(rdx), [SYSARG_4] = USER_REGS_OFFSET(r10), [SYSARG_5] = USER_REGS_OFFSET(r8), [SYSARG_6] = USER_REGS_OFFSET(r9), [SYSARG_RESULT] = USER_REGS_OFFSET(rax), [STACK_POINTER] = USER_REGS_OFFSET(rsp), }; static off_t reg_offset_x86[] = { [SYSARG_NUM] = USER_REGS_OFFSET(orig_rax), [SYSARG_1] = USER_REGS_OFFSET(rbx), [SYSARG_2] = USER_REGS_OFFSET(rcx), [SYSARG_3] = USER_REGS_OFFSET(rdx), [SYSARG_4] = USER_REGS_OFFSET(rsi), [SYSARG_5] = USER_REGS_OFFSET(rdi), [SYSARG_6] = USER_REGS_OFFSET(rbp), [SYSARG_RESULT] = USER_REGS_OFFSET(rax), [STACK_POINTER] = USER_REGS_OFFSET(rsp), }; #undef REG #define REG(tracee, version, index) \ (*(word_t*) (tracee->_regs[ORIGINAL].cs == 0x23 \ ? (((uint8_t *) &tracee->_regs[version]) + reg_offset_x86[index]) \ : (((uint8_t *) &tracee->_regs[version]) + reg_offset[index]))) #elif defined(ARCH_ARM_EABI) static off_t reg_offset[] = { [SYSARG_NUM] = USER_REGS_OFFSET(uregs[7]), [SYSARG_1] = USER_REGS_OFFSET(uregs[0]), [SYSARG_2] = USER_REGS_OFFSET(uregs[1]), [SYSARG_3] = USER_REGS_OFFSET(uregs[2]), [SYSARG_4] = USER_REGS_OFFSET(uregs[3]), [SYSARG_5] = USER_REGS_OFFSET(uregs[4]), [SYSARG_6] = USER_REGS_OFFSET(uregs[5]), [SYSARG_RESULT] = USER_REGS_OFFSET(uregs[0]), [STACK_POINTER] = USER_REGS_OFFSET(uregs[13]), }; #elif defined(ARCH_ARM64) #undef USER_REGS_OFFSET #define USER_REGS_OFFSET(reg_name) offsetof(struct user_regs_struct, reg_name) static off_t reg_offset[] = { [SYSARG_NUM] = USER_REGS_OFFSET(regs[8]), [SYSARG_1] = USER_REGS_OFFSET(regs[0]), [SYSARG_2] = USER_REGS_OFFSET(regs[1]), [SYSARG_3] = USER_REGS_OFFSET(regs[2]), [SYSARG_4] = USER_REGS_OFFSET(regs[3]), [SYSARG_5] = USER_REGS_OFFSET(regs[4]), [SYSARG_6] = USER_REGS_OFFSET(regs[5]), [SYSARG_RESULT] = USER_REGS_OFFSET(regs[0]), [STACK_POINTER] = USER_REGS_OFFSET(sp), }; #elif defined(ARCH_X86) static off_t reg_offset[] = { [SYSARG_NUM] = USER_REGS_OFFSET(orig_eax), [SYSARG_1] = USER_REGS_OFFSET(ebx), [SYSARG_2] = USER_REGS_OFFSET(ecx), [SYSARG_3] = USER_REGS_OFFSET(edx), [SYSARG_4] = USER_REGS_OFFSET(esi), [SYSARG_5] = USER_REGS_OFFSET(edi), [SYSARG_6] = USER_REGS_OFFSET(ebp), [SYSARG_RESULT] = USER_REGS_OFFSET(eax), [STACK_POINTER] = USER_REGS_OFFSET(esp), }; #elif defined(ARCH_SH4) static off_t reg_offset[] = { [SYSARG_NUM] = USER_REGS_OFFSET(regs[3]), [SYSARG_1] = USER_REGS_OFFSET(regs[4]), [SYSARG_2] = USER_REGS_OFFSET(regs[5]), [SYSARG_3] = USER_REGS_OFFSET(regs[6]), [SYSARG_4] = USER_REGS_OFFSET(regs[7]), [SYSARG_5] = USER_REGS_OFFSET(regs[0]), [SYSARG_6] = USER_REGS_OFFSET(regs[1]), [SYSARG_RESULT] = USER_REGS_OFFSET(regs[0]), [STACK_POINTER] = USER_REGS_OFFSET(regs[15]), }; #else #error "Unsupported architecture" #endif /** * Return the *cached* value of the given @tracees' @reg. */ word_t peek_reg(const Tracee *tracee, RegVersion version, Reg reg) { word_t result; assert(version < NB_REG_VERSION); result = REG(tracee, version, reg); /* Use only the 32 least significant bits (LSB) when running * 32-bit processes on a 64-bit kernel. */ if (is_32on64_mode(tracee)) result &= 0xFFFFFFFF; return result; } /** * Set the *cached* value of the given @tracees' @reg. */ void poke_reg(Tracee *tracee, Reg reg, word_t value) { if (peek_reg(tracee, CURRENT, reg) == value) return; REG(tracee, CURRENT, reg) = value; tracee->_regs_were_changed = true; } /** * Print the value of the current @tracee's registers according * to the @verbose_level. Note: @message is mixed to the output. */ void print_current_regs(Tracee *tracee, int verbose_level, const char *message) { if (tracee->verbose < verbose_level) return; notice(tracee, INFO, INTERNAL, "pid %d: %s: %ld, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx) = 0x%lx [0x%lx]", tracee->pid, message, peek_reg(tracee, CURRENT, SYSARG_NUM), peek_reg(tracee, CURRENT, SYSARG_1), peek_reg(tracee, CURRENT, SYSARG_2), peek_reg(tracee, CURRENT, SYSARG_3), peek_reg(tracee, CURRENT, SYSARG_4), peek_reg(tracee, CURRENT, SYSARG_5), peek_reg(tracee, CURRENT, SYSARG_6), peek_reg(tracee, CURRENT, SYSARG_RESULT), peek_reg(tracee, CURRENT, STACK_POINTER)); } /** * Save the @tracee's current register bank into the @version register * bank. */ void save_current_regs(Tracee *tracee, RegVersion version) { /* Optimization: don't restore original register values if * they were never changed. */ if (version == ORIGINAL) tracee->_regs_were_changed = false; memcpy(&tracee->_regs[version], &tracee->_regs[CURRENT], sizeof(tracee->_regs[CURRENT])); } /** * Copy all @tracee's general purpose registers into a dedicated * cache. This function returns -errno if an error occured, 0 * otherwise. */ int fetch_regs(Tracee *tracee) { int status; #if defined(ARCH_ARM64) struct iovec regs; regs.iov_base = &tracee->_regs[CURRENT]; regs.iov_len = sizeof(tracee->_regs[CURRENT]); status = ptrace(PTRACE_GETREGSET, tracee->pid, NT_PRSTATUS, ®s); #else status = ptrace(PTRACE_GETREGS, tracee->pid, NULL, &tracee->_regs[CURRENT]); #endif if (status < 0) return status; return 0; } /** * Copy the cached values of all @tracee's general purpose registers * back to the process, if necessary. This function returns -errno if * an error occured, 0 otherwise. */ int push_regs(Tracee *tracee) { int status; if (tracee->_regs_were_changed) { /* At the very end of a syscall, with regard to the * entry, only the result register can be modified by * PRoot. */ if (tracee->restore_original_regs) { /* Restore the sysarg register only if it is * not the same as the result register. Note: * it's never the case on x86 architectures, * so don't make this check, otherwise it * would introduce useless complexity because * of the multiple ABI support. */ #if defined(ARCH_X86) || defined(ARCH_X86_64) # define RESTORE(sysarg) (REG(tracee, CURRENT, sysarg) = REG(tracee, ORIGINAL, sysarg)) #else # define RESTORE(sysarg) (void) (reg_offset[SYSARG_RESULT] != reg_offset[sysarg] && \ (REG(tracee, CURRENT, sysarg) = REG(tracee, ORIGINAL, sysarg))) #endif RESTORE(SYSARG_NUM); RESTORE(SYSARG_1); RESTORE(SYSARG_2); RESTORE(SYSARG_3); RESTORE(SYSARG_4); RESTORE(SYSARG_5); RESTORE(SYSARG_6); RESTORE(STACK_POINTER); } #if defined(ARCH_ARM64) struct iovec regs; regs.iov_base = &tracee->_regs[CURRENT]; regs.iov_len = sizeof(tracee->_regs[CURRENT]); status = ptrace(PTRACE_SETREGSET, tracee->pid, NT_PRSTATUS, ®s); #else status = ptrace(PTRACE_SETREGS, tracee->pid, NULL, &tracee->_regs[CURRENT]); #endif if (status < 0) return status; } return 0; } proot-3.0.2/src/tracee/event.c0000644000175000017500000003524512156552156015573 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _GNU_SOURCE /* CLONE_*, */ #include /* CLONE_*, */ #include /* pid_t, */ #include /* ptrace(1), PTRACE_*, */ #include /* PTRACE_*, */ #include /* waitpid(2), */ #include /* waitpid(2), */ #include /* personality(2), ADDR_NO_RANDOMIZE, */ #include /* *rlimit(2), */ #include /* *rlimit(2), */ #include /* fork(2), chdir(2), getpid(2), */ #include /* strcmp(3), */ #include /* errno(3), */ #include /* bool, true, false, */ #include /* assert(3), */ #include /* atexit(3), getenv(3), */ #include /* talloc_*, */ #include "tracee/event.h" #include "notice.h" #include "path/path.h" #include "path/binding.h" #include "syscall/syscall.h" #include "syscall/seccomp.h" #include "extension/extension.h" #include "attribute.h" #include "compat.h" #include "build.h" /** * Launch the first process as specified by @tracee->cmdline[]. This * function returns -errno if an error occurred, otherwise 0. */ int launch_process(Tracee *tracee) { struct rlimit rlimit; long status; pid_t pid; pid = fork(); switch(pid) { case -1: notice(tracee, ERROR, SYSTEM, "fork()"); return -errno; case 0: /* child */ /* Declare myself as ptraceable before executing the * requested program. */ status = ptrace(PTRACE_TRACEME, 0, NULL, NULL); if (status < 0) { notice(tracee, ERROR, SYSTEM, "ptrace(TRACEME)"); return -errno; } /* Warn about open file descriptors. They won't be * translated until they are closed. */ if (tracee->verbose > 0) list_open_fd(tracee); /* RHEL4 uses an ASLR mechanism that creates conflicts * between the layout of QEMU and the layout of the * target program. */ status = personality(0xffffffff); if ( status < 0 || personality(status | ADDR_NO_RANDOMIZE) < 0) notice(tracee, WARNING, INTERNAL, "can't disable ASLR"); /* 1. The ELF interpreter is explicitly used as a * loader by PRoot, it means the Linux kernel * allocates the heap segment for this loader, not * for the application. It isn't really a problem * since the application re-uses the loader's heap * transparently, i.e. its own brk points there. * * 2. When the current stack limit is set to a "high" * value, the ELF interpreter is loaded to a "low" * address, I guess it is the way the Linux kernel * deals with this constraint. * * This two behaviors can be observed by running the * command "/usr/bin/cat /proc/self/maps", with/out * the ELF interpreter and with/out a high current * stack limit. * * When this two behaviors are combined, the dynamic * ELF linker might mmap a shared libraries to the * same address as the start of its heap if no heap * allocation was made prior this mmap. This problem * was first observed with something similar to the * example below (because GNU make sets the current * limit to the maximum): * * #!/usr/bin/make -f * * SHELL=/lib64/ld-linux-x86-64.so.2 /usr/bin/bash * FOO:=$(shell test -e /dev/null && echo OK) * * all: * @/usr/bin/test -n "$(FOO)" * * The code below is a workaround to this wrong * combination of behaviors, however it might create * conflict with tools that assume a "standard" stack * layout, like libgomp and QEMU. */ status = getrlimit(RLIMIT_STACK, &rlimit); if (status >= 0 && rlimit.rlim_max == RLIM_INFINITY) { /* "No one will need more than 256MB of stack memory" (tm). */ rlimit.rlim_max = 256 * 1024 * 1024; if (rlimit.rlim_cur > rlimit.rlim_max) rlimit.rlim_cur = rlimit.rlim_max; status = setrlimit(RLIMIT_STACK, &rlimit); } if (status < 0) notice(tracee, WARNING, SYSTEM, "can't set the maximum stack size"); /* Synchronize with the tracer's event loop. Without * this trick the tracer only sees the "return" from * the next execve(2) so PRoot wouldn't handle the * interpreter/runner. I also verified that strace * does the same thing. */ kill(getpid(), SIGSTOP); #if defined(HAVE_SECCOMP_FILTER) /* Improve performance by using seccomp mode 2, unless * this support is explicitly disabled. */ if (getenv("PROOT_NO_SECCOMP") == NULL) { status = enable_syscall_filtering(tracee); if (status < 0) notice(tracee, WARNING, INTERNAL, "can't enable ptrace acceleration (seccomp mode 2)"); } #endif /* Now process is ptraced, so the current rootfs is already the * guest rootfs. Note: Valgrind can't handle execve(2) on * "foreign" binaries (ENOEXEC) but can handle execvp(3) on such * binaries. */ execvp(tracee->exe, tracee->cmdline); return -errno; default: /* parent */ /* We know the pid of the first tracee now. */ tracee->pid = pid; return 0; } /* Never reached. */ return -ENOSYS; } /* Send the KILL signal to all tracees when PRoot has received a fatal * signal. */ static void kill_all_tracees2(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED) { notice(NULL, WARNING, INTERNAL, "signal %d received from process %d", signum, siginfo->si_pid); kill_all_tracees(); /* Exit immediately for system signals (segmentation fault, * illegal instruction, ...), otherwise exit cleanly through * the event loop. */ if (signum != SIGQUIT) _exit(EXIT_FAILURE); } /* Print on stderr the complete talloc hierarchy. */ static void print_talloc_hierarchy(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED) { void print_talloc_chunk(const void *ptr, int depth, int max_depth UNUSED, int is_ref, void *data UNUSED) { const char *name; size_t count; size_t size; name = talloc_get_name(ptr); size = talloc_get_size(ptr); count = talloc_reference_count(ptr); if (depth == 0) return; while (depth-- > 1) fprintf(stderr, "\t"); fprintf(stderr, "%-16s ", name); if (is_ref) fprintf(stderr, "-> %-8p", ptr); else { fprintf(stderr, "%-8p %zd bytes %zd ref'", ptr, size, count); if (name[0] == '$') { fprintf(stderr, "\t(\"%s\")", (char *)ptr); } if (name[0] == '@') { char **argv; int i; fprintf(stderr, "\t("); for (i = 0, argv = (char **)ptr; argv[i] != NULL; i++) fprintf(stderr, "\"%s\", ", argv[i]); fprintf(stderr, ")"); } else if (strcmp(name, "Tracee") == 0) { fprintf(stderr, "\t(pid = %d)", ((Tracee *)ptr)->pid); } else if (strcmp(name, "Bindings") == 0) { Tracee *tracee; tracee = TRACEE(ptr); if (ptr == tracee->fs->bindings.pending) fprintf(stderr, "\t(pending)"); else if (ptr == tracee->fs->bindings.guest) fprintf(stderr, "\t(guest)"); else if (ptr == tracee->fs->bindings.host) fprintf(stderr, "\t(host)"); } else if (strcmp(name, "Binding") == 0) { Binding *binding = (Binding *)ptr; fprintf(stderr, "\t(%s:%s)", binding->host.path, binding->guest.path); } } fprintf(stderr, "\n"); } switch (signum) { case SIGUSR1: talloc_report_depth_cb(NULL, 0, 100, print_talloc_chunk, NULL); break; case SIGUSR2: talloc_report_depth_file(NULL, 0, 100, stderr); break; default: break; } } /** * Wait then handle any event from any tracee. This function returns * the exit status of the last terminated program. */ int event_loop() { enum __ptrace_request default_restart_how = PTRACE_SYSCALL; struct sigaction signal_action; int last_exit_status = -1; long status; int signum; /* Kill all tracees when exiting. */ status = atexit(kill_all_tracees); if (status != 0) notice(NULL, WARNING, INTERNAL, "atexit() failed"); /* All signals are blocked when the signal handler is called. * SIGINFO is used to know which process has signaled us and * RESTART is used to restart waitpid(2) seamlessly. */ bzero(&signal_action, sizeof(signal_action)); signal_action.sa_flags = SA_SIGINFO | SA_RESTART; status = sigfillset(&signal_action.sa_mask); if (status < 0) notice(NULL, WARNING, SYSTEM, "sigfillset()"); /* Handle all signals. */ for (signum = 0; signum < SIGRTMAX; signum++) { switch (signum) { case SIGQUIT: case SIGILL: case SIGABRT: case SIGFPE: case SIGSEGV: /* Kill all tracees on abnormal termination * signals. This ensures no process is left * untraced. */ signal_action.sa_sigaction = kill_all_tracees2; break; case SIGUSR1: case SIGUSR2: /* Print on stderr the complete talloc * hierarchy, useful for debug purpose. */ signal_action.sa_sigaction = print_talloc_hierarchy; break; case SIGCHLD: case SIGCONT: case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: /* The default action is OK for these signals, * they are related to tty and job control. */ continue; default: /* Ignore all other signals, including * terminating ones (^C for instance). */ signal_action.sa_sigaction = (void *)SIG_IGN; break; } status = sigaction(signum, &signal_action, NULL); if (status < 0 && errno != EINVAL) notice(NULL, WARNING, SYSTEM, "sigaction(%d)", signum); } while (1) { static bool seccomp_enabled = false; int tracee_status; Tracee *tracee; pid_t pid; /* Not a signal-stop by default. */ int signal = 0; /* Wait for the next tracee's stop. */ pid = waitpid(-1, &tracee_status, __WALL); if (pid < 0) { if (errno != ECHILD) { notice(NULL, ERROR, SYSTEM, "waitpid()"); return EXIT_FAILURE; } break; } /* Get the information about this tracee. */ tracee = get_tracee(NULL, pid, true); assert(tracee != NULL); tracee->restart_how = default_restart_how; status = notify_extensions(tracee, NEW_STATUS, tracee_status, 0); if (status != 0) continue; if (WIFEXITED(tracee_status)) { last_exit_status = WEXITSTATUS(tracee_status); VERBOSE(tracee, 1, "pid %d: exited with status %d", pid, last_exit_status); } else if (WIFSIGNALED(tracee_status)) { VERBOSE(tracee, (int) (last_exit_status != -1), "pid %d: terminated with signal %d", pid, WTERMSIG(tracee_status)); } else if (WIFSTOPPED(tracee_status)) { /* Don't use WSTOPSIG() to extract the signal * since it clears the PTRACE_EVENT_* bits. */ signal = (tracee_status & 0xfff00) >> 8; switch (signal) { static bool deliver_sigtrap = false; case SIGTRAP: { const unsigned long default_ptrace_options = ( PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT); /* Distinguish some events from others and * automatically trace each new process with * the same options. * * Note that only the first bare SIGTRAP is * related to the tracing loop, others SIGTRAP * carry tracing information because of * TRACE*FORK/CLONE/EXEC. */ if (deliver_sigtrap) break; /* Deliver this signal as-is. */ deliver_sigtrap = true; /* Try to enable seccomp mode 2... */ status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL, default_ptrace_options | PTRACE_O_TRACESECCOMP); if (status < 0) { /* ... otherwise use default options only. */ status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL, default_ptrace_options); if (status < 0) notice(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)"); } } /* Fall through. */ case SIGTRAP | 0x80: signal = 0; translate_syscall(tracee); break; /* With *vanilla* kernels PTRACE_EVENT_SECCOMP == 7. */ #if PTRACE_EVENT_SECCOMP == 8 case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: #endif case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: { unsigned long translate_syscall_now = 0; signal = 0; if (!seccomp_enabled) { VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2) enabled"); default_restart_how = PTRACE_CONT; seccomp_enabled = true; } status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &translate_syscall_now); if (status < 0) break; /* For yet unknown reasons, execve(2) * behaves differently under seccomp: * its sysenter stage has to be * handled right now. All other * syscalls are handled as usual. */ if (translate_syscall_now == 0) { tracee->restart_how = PTRACE_SYSCALL; break; } translate_syscall(tracee); /* No need for its sysexit stage. */ tracee->restart_how = PTRACE_CONT; tracee->status = 0; break; } case SIGTRAP | PTRACE_EVENT_VFORK << 8: signal = 0; (void) new_child(tracee, CLONE_VFORK); break; case SIGTRAP | PTRACE_EVENT_FORK << 8: signal = 0; (void) new_child(tracee, 0); break; case SIGTRAP | PTRACE_EVENT_CLONE << 8: { word_t clone_flags; signal = 0; status = fetch_regs(tracee); if (status < 0) break; clone_flags = peek_reg(tracee, CURRENT, SYSARG_1); (void) new_child(tracee, clone_flags); break; } case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8: case SIGTRAP | PTRACE_EVENT_EXEC << 8: case SIGTRAP | PTRACE_EVENT_EXIT << 8: signal = 0; break; case SIGSTOP: /* Stop this tracee until PRoot has received * the EVENT_*FORK|CLONE notification. */ if (tracee->exe == NULL) { tracee->sigstop = SIGSTOP_PENDING; signal = -1; } /* For each tracee, the first SIGSTOP * is only used to notify the tracer. */ if (tracee->sigstop == SIGSTOP_IGNORED) { tracee->sigstop = SIGSTOP_ALLOWED; signal = 0; } break; default: /* Deliver this signal as-is. */ break; } } /* Keep in the stopped state?. */ if (signal == -1) continue; /* Restart the tracee and stop it at the next entry or * exit of a system call. */ status = ptrace(tracee->restart_how, tracee->pid, NULL, signal); if (status < 0) TALLOC_FREE(tracee); } while (0); return last_exit_status; } proot-3.0.2/src/tracee/tracee.h0000644000175000017500000001226112156552156015713 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef TRACEE_H #define TRACEE_H #include /* pid_t, size_t, */ #include /* struct user*, */ #include #include /* LIST_*, */ #include /* enum __ptrace_request */ #include /* talloc_*, */ #include "arch.h" /* word_t, user_regs_struct, */ #include "compat.h" typedef enum { CURRENT = 0, ORIGINAL = 1, MODIFIED = 2, NB_REG_VERSION } RegVersion; struct bindings; struct extensions; /* Information related to a file-system name-space. */ typedef struct { struct { /* List of bindings as specified by the user but not canonicalized yet. */ struct bindings *pending; /* List of bindings canonicalized and sorted in the "guest" order. */ struct bindings *guest; /* List of bindings canonicalized and sorted in the "host" order. */ struct bindings *host; } bindings; /* Current working directory, à la /proc/self/pwd. */ char *cwd; } FileSystemNameSpace; /* Information related to a tracee process. */ typedef struct tracee { /********************************************************************** * Private resources * **********************************************************************/ /* Link for the list of all tracees. */ LIST_ENTRY(tracee) link; /* Process identifier. */ pid_t pid; /* Current status: * 0: enter syscall * 1: exit syscall no error * -errno: exit syscall with error. */ int status; /* How this tracee is restarted. */ enum __ptrace_request restart_how; /* Value of the tracee's general purpose registers. */ struct user_regs_struct _regs[NB_REG_VERSION]; bool _regs_were_changed; bool restore_original_regs; /* State for the special handling of SIGSTOP. */ enum { SIGSTOP_IGNORED = 0, /* Ignore SIGSTOP (once the parent is known). */ SIGSTOP_ALLOWED, /* Allow SIGSTOP (once the parent is known). */ SIGSTOP_PENDING, /* Block SIGSTOP until the parent is unknown. */ } sigstop; /* Context used to collect all the temporary dynamic memory * allocations. */ TALLOC_CTX *ctx; /* Specify the type of the final component during the * initialization of a binding. This variable is first * defined in bind_path() then used in build_glue(). */ mode_t glue_type; /* During a sub-reconfiguration, the new setup is relatively * to @tracee's file-system name-space. Also, @paths holds * its $PATH environment variable in order to emulate the * execvp(3) behavior. */ struct { struct tracee *tracee; const char *paths; } reconf; /********************************************************************** * Shared or private resources, depending on the CLONE_FS flag. * **********************************************************************/ FileSystemNameSpace *fs; /********************************************************************** * Shared resources until the tracee makes a call to execve(). * **********************************************************************/ /* Path to the executable, à la /proc/self/exe. */ char *exe; /* Initial command-line, à la /proc/self/cmdline. */ char **cmdline; /********************************************************************** * Shared or private resources, depending on the (re-)configuration * **********************************************************************/ /* Runner command-line. */ char **qemu; /* Can the ELF interpreter for QEMU be safely skipped? */ bool qemu_pie_workaround; /* Path to glue between the guest rootfs and the host rootfs. */ char *glue; /* List of extensions enabled for this tracee. */ struct extensions *extensions; /********************************************************************** * Private resources * **********************************************************************/ /* Verbose level. */ int verbose; } Tracee; #define HOST_ROOTFS "/host-rootfs" #define TRACEE(a) talloc_get_type_abort(talloc_parent(talloc_parent(a)), Tracee) extern Tracee *get_tracee(const Tracee *tracee, pid_t pid, bool create); extern int new_child(Tracee *parent, word_t clone_flags); extern int swap_config(Tracee *tracee1, Tracee *tracee2); extern int parse_config(Tracee *tracee, size_t argc, char *argv[]); extern void kill_all_tracees(); #endif /* TRACEE_H */ proot-3.0.2/src/tracee/tracee.c0000644000175000017500000001642312156552156015712 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _GNU_SOURCE /* CLONE_*, */ #include /* CLONE_*, */ #include /* pid_t, size_t, */ #include /* NULL, */ #include /* assert(3), */ #include /* bzero(3), */ #include /* bool, true, false, */ #include /* LIST_*, */ #include /* talloc_*, */ #include /* kill(2), SIGKILL, */ #include /* ptrace(2), PTRACE_*, */ #include /* E*, */ #include "tracee/tracee.h" #include "extension/extension.h" #include "notice.h" #include "compat.h" typedef LIST_HEAD(tracees, tracee) Tracees; static Tracees tracees; /** * Remove @tracee from the list of tracees. */ static int remove_tracee(Tracee *tracee) { LIST_REMOVE(tracee, link); return 0; } /** * Allocate a new entry for the tracee @pid. */ static Tracee *new_tracee(pid_t pid) { Tracee *tracee; tracee = talloc_zero(NULL, Tracee); if (tracee == NULL) return NULL; talloc_set_destructor(tracee, remove_tracee); /* Allocate a memory collector. */ tracee->ctx = talloc_new(tracee); if (tracee->ctx == NULL) goto no_mem; /* By default new tracees have an empty file-system * name-space. */ tracee->fs = talloc_zero(tracee, FileSystemNameSpace); if (tracee->fs == NULL) goto no_mem; tracee->pid = pid; LIST_INSERT_HEAD(&tracees, tracee, link); return tracee; no_mem: TALLOC_FREE(tracee); return NULL; } /** * Return the entry related to the tracee @pid. If no entry were * found, a new one is created if @create is true, otherwise NULL is * returned. */ Tracee *get_tracee(const Tracee *current_tracee, pid_t pid, bool create) { Tracee *tracee; /* Don't reset the memory collector if the searched tracee is * the current one: there's likely pointers to the * sub-allocated data in the caller. */ if (current_tracee != NULL && current_tracee->pid == pid) return (Tracee *)current_tracee; LIST_FOREACH(tracee, &tracees, link) if (tracee->pid == pid) { /* Flush then allocate a new memory collector. */ TALLOC_FREE(tracee->ctx); tracee->ctx = talloc_new(tracee); return tracee; } return (create ? new_tracee(pid) : NULL); } /** * Make new @parent's child inherit from it. Depending on * @clone_flags, some information are copied or shared. This function * returns -errno if an error occured, otherwise 0. */ int new_child(Tracee *parent, word_t clone_flags) { unsigned long pid; Tracee *child; int status; /* Get the pid of the parent's new child. */ status = ptrace(PTRACE_GETEVENTMSG, parent->pid, NULL, &pid); if (status < 0) { notice(parent, WARNING, SYSTEM, "ptrace(GETEVENTMSG)"); return status; } child = get_tracee(parent, (pid_t) pid, true); if (child == NULL) { notice(parent, WARNING, SYSTEM, "running out of memory"); return -ENOMEM; } /* Sanity checks. */ assert(child != NULL && child->exe == NULL && child->cmdline == NULL && child->fs->cwd == NULL && child->fs->bindings.pending == NULL && child->fs->bindings.guest == NULL && child->fs->bindings.host == NULL && child->qemu == NULL && child->glue == NULL); child->verbose = parent->verbose; /* If CLONE_FS is set, the parent and the child process share * the same file system information. This includes the root * of the file system, the current working directory, and the * umask. Any call to chroot(2), chdir(2), or umask(2) * performed by the parent process or the child process also * affects the other process. * * If CLONE_FS is not set, the child process works on a copy * of the file system information of the parent process at the * time of the clone() call. Calls to chroot(2), chdir(2), * umask(2) performed later by one of the processes do not * affect the other process. * * -- clone(2) man-page */ TALLOC_FREE(child->fs); if ((clone_flags & CLONE_FS) != 0) { /* File-system name-space is shared. */ child->fs = talloc_reference(child, parent->fs); } else { /* File-system name-space is copied. */ child->fs = talloc_zero(child, FileSystemNameSpace); if (child->fs == NULL) return -ENOMEM; child->fs->cwd = talloc_strdup(child->fs, parent->fs->cwd); if (child->fs->cwd == NULL) return -ENOMEM; talloc_set_name_const(child->fs->cwd, "$cwd"); /* Bindings are shared across file-system name-spaces since a * "mount --bind" made by a process affects all other processes * under Linux. Actually they are copied when a sub * reconfiguration occured (nested proot or chroot(2)). */ child->fs->bindings.guest = talloc_reference(child->fs, parent->fs->bindings.guest); child->fs->bindings.host = talloc_reference(child->fs, parent->fs->bindings.host); } /* The path to the executable and the command-line are unshared only * once the child process does a call to execve(2). */ child->exe = talloc_reference(child, parent->exe); child->cmdline = talloc_reference(child, parent->cmdline); child->qemu_pie_workaround = parent->qemu_pie_workaround; child->qemu = talloc_reference(child, parent->qemu); child->glue = talloc_reference(child, parent->glue); inherit_extensions(child, parent, false); /* Restart the child tracee if it was already alive but * stopped until that moment. */ if (child->sigstop == SIGSTOP_PENDING) { int status; child->sigstop = SIGSTOP_ALLOWED; status = ptrace(PTRACE_SYSCALL, child->pid, NULL, 0); if (status < 0) TALLOC_FREE(child); } return 0; } /** * Swap configuration (pointers and parentality) between @tracee1 and @tracee2. */ int swap_config(Tracee *tracee1, Tracee *tracee2) { Tracee *tmp; tmp = talloc_zero(tracee1->ctx, Tracee); if (tmp == NULL) return -ENOMEM; #if defined(TALLOC_VERSION_MAJOR) && TALLOC_VERSION_MAJOR >= 2 void reparent_config(Tracee *new_parent, Tracee *old_parent) { new_parent->verbose = old_parent->verbose; new_parent->qemu_pie_workaround = old_parent->qemu_pie_workaround; #define REPARENT(field) do { \ talloc_reparent(old_parent, new_parent, old_parent->field); \ new_parent->field = old_parent->field; \ } while(0); REPARENT(fs); REPARENT(exe); REPARENT(cmdline); REPARENT(qemu); REPARENT(glue); REPARENT(extensions); } reparent_config(tmp, tracee1); reparent_config(tracee1, tracee2); reparent_config(tracee2, tmp); return 0; #else return -ENOSYS; #endif } /* Send the KILL signal to all tracees. */ void kill_all_tracees() { Tracee *tracee; LIST_FOREACH(tracee, &tracees, link) kill(tracee->pid, SIGKILL); } proot-3.0.2/src/tracee/mem.h0000644000175000017500000000327112156552156015227 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef TRACEE_MEM_H #define TRACEE_MEM_H #include /* PATH_MAX, */ #include /* pid_t, size_t, */ #include /* struct iovec, */ #include "arch.h" /* word_t, */ #include "tracee/tracee.h" extern int write_data(const Tracee *tracee, word_t dest_tracee, const void *src_tracer, word_t size); extern int writev_data(const Tracee *tracee, word_t dest_tracee, const struct iovec *src_tracer, int src_tracer_count); extern int read_data(const Tracee *tracee, void *dest_tracer, word_t src_tracee, word_t size); extern int read_string(const Tracee *tracee, char *dest_tracer, word_t src_tracee, word_t max_size); extern word_t peek_mem(const Tracee *tracee, word_t address); extern void poke_mem(const Tracee *tracee, word_t address, word_t value); extern word_t alloc_mem(Tracee *tracee, ssize_t size); #endif /* TRACEE_MEM_H */ proot-3.0.2/src/tracee/abi.h0000644000175000017500000000540112156552156015201 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef TRACEE_ABI_H #define TRACEE_ABI_H #include #include /* offsetof(), */ #include "tracee/tracee.h" #include "tracee/reg.h" #include "arch.h" #include "attribute.h" typedef enum { ABI_DEFAULT, ABI_2, /* x86_32 on x86_64. */ ABI_3, /* x32 on x86_64. */ } Abi; /** * Return the ABI currently used by the given @tracee. */ #if defined(ARCH_X86_64) static inline Abi get_abi(const Tracee *tracee) { /* The ABI can be changed by a syscall ("execve" typically), * however the change is only effective once the syscall has * *fully* returned, hence the use of _regs[ORIGINAL]. */ switch (tracee->_regs[ORIGINAL].cs) { case 0x23: return ABI_2; case 0x33: if (tracee->_regs[ORIGINAL].ds == 0x2B) return ABI_3; /* Fall through. */ default: return ABI_DEFAULT; } } #else static inline Abi get_abi(const Tracee *tracee UNUSED) { return ABI_DEFAULT; } #endif /** * Return true if @tracee is a 32-bit process running on a 64-bit * kernel. */ static inline bool is_32on64_mode(const Tracee *tracee) { return (get_abi(tracee) != ABI_DEFAULT); } /** * Return the size of a word according to the ABI currently used by * the given @tracee. */ static inline size_t sizeof_word(const Tracee *tracee) { return (is_32on64_mode(tracee) ? sizeof(word_t) / 2 : sizeof(word_t)); } #include /** * Return the offset of the 'uid' field in a 'stat' structure * according to the ABI currently used by the given @tracee. */ static inline off_t offsetof_stat_uid(const Tracee *tracee) { return (is_32on64_mode(tracee) ? OFFSETOF_STAT_UID_32 : offsetof(struct stat, st_uid)); } /** * Return the offset of the 'gid' field in a 'stat' structure * according to the ABI currently used by the given @tracee. */ static inline off_t offsetof_stat_gid(const Tracee *tracee) { return (is_32on64_mode(tracee) ? OFFSETOF_STAT_GID_32 : offsetof(struct stat, st_gid)); } #endif /* TRACEE_ABI_H */ proot-3.0.2/src/tracee/array.c0000644000175000017500000002561712156552156015572 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* ARG_MAX, */ #include /* assert(3), */ #include /* strlen(3), memcmp(3), memcpy(3), */ #include /* bzero(3), */ #include /* bool, true, false, */ #include /* E*, */ #include /* va_*, */ #include /* uint32_t, */ #include /* talloc_*, */ #include "arch.h" struct array_entry { /* Pointer (tracee's address space) to the current value, if * local == NULL. */ word_t remote; /* Pointer (tracer's address space) to the current value, if * local != NULL. */ void *local; }; #include "tracee/tracee.h" #include "tracee/array.h" #include "tracee/mem.h" #include "tracee/abi.h" #include "build.h" /** * Set *@value to the address of the data pointed to by the entry in * @array at the given @index. This function returns -errno when an * error occured, otherwise 0. */ int read_item_data(Array *array, size_t index, void **value) { int status; int size; assert(index < array->length); /* Already cached locally? */ if (array->_cache[index].local != NULL) goto end; /* Remote NULL is mapped to local NULL. */ if (array->_cache[index].remote == 0) { array->_cache[index].local = NULL; goto end; } size = sizeof_item(array, index); if (size < 0) return size; array->_cache[index].local = talloc_size(array, size); if (array->_cache[index].local == NULL) return -ENOMEM; /* Copy locally the remote data. */ status = read_data(TRACEE(array), array->_cache[index].local, array->_cache[index].remote, size); if (status < 0) { array->_cache[index].local = NULL; return status; } end: *value = array->_cache[index].local; return 0; } /** * Set *@value to the address of the string pointed to by the entry in * @array at the given @index. This function returns -errno when an * error occured, otherwise 0. */ int read_item_string(Array *array, size_t index, char **value) { char tmp[ARG_MAX]; int status; assert(index < array->length); /* Already cached locally? */ if (array->_cache[index].local != NULL) goto end; /* Remote NULL is mapped to local NULL. */ if (array->_cache[index].remote == 0) { array->_cache[index].local = NULL; goto end; } /* Copy locally the remote string into a temporary buffer. */ status = read_string(TRACEE(array), tmp, array->_cache[index].remote, ARG_MAX); if (status < 0) return status; if (status >= ARG_MAX) return -ENOMEM; /* Save the local string in a "persistent" buffer. */ array->_cache[index].local = talloc_strdup(array, tmp); if (array->_cache[index].local == NULL) return -ENOMEM; end: *value = array->_cache[index].local; return 0; } /** * This function returns the number of bytes of the string pointed to * by the entry in @array at the given @index, otherwise -errno when * an error occured. */ int sizeof_item_string(Array *array, size_t index) { char *value; int status; assert(index < array->length); status = read_item_string(array, index, &value); if (status < 0) return status; if (value == NULL) return 0; return strlen(value) + 1; } /** * This function returns 1 or 0 depending on the equivalence of the * @reference item and the one pointed to by the entry in @array at * the given @index, otherwise it returns -errno when an error * occured. */ int compare_item_generic(Array *array, size_t index, const void *reference) { void *value; int status; assert(index < array->length); status = read_item(array, index, &value); if (status < 0) return status; if (value == NULL && reference == NULL) return 1; if (value == NULL && reference != NULL) return 0; if (value != NULL && reference == NULL) return 0; status = sizeof_item(array, index); if (status < 0) return status; return (int)(memcmp(value, reference, status) == 0); } /** * This function returns the index in @array of the first item * equivalent to the @reference item, otherwise it returns -errno when * an error occured. */ int find_item(Array *array, const void *reference) { size_t i; for (i = 0; i < array->length; i++) { int status; status = compare_item(array, i, reference); if (status < 0) return status; if (status != 0) break; } return i; } /** * Make the entry at the given @index in @array points to a copy of * the string pointed to by @value. This function returns -errno when * an error occured, otherwise 0. */ int write_item_string(Array *array, size_t index, const char *value) { assert(index < array->length); array->_cache[index].local = talloc_strdup(array, value); if (array->_cache[index].local == NULL) return -ENOMEM; return 0; } /** * Make the @nb_items entries at the given @index in @array points to * a copy of the item pointed to by the variadic arguments. This * function returns -errno when an error occured, otherwise 0. */ int write_items(Array *array, size_t index, size_t nb_items, ...) { va_list va_items; int status; size_t i; va_start(va_items, nb_items); for (i = 0; i < nb_items; i++) { void *value = va_arg(va_items, void *); status = write_item(array, index + i, value); if (status < 0) goto end; } status = 0; end: va_end(va_items); return status; } /** * Resize the @array at the given @index by the @delta_nb_entries. * This function returns -errno when an error occured, otherwise 0. */ int resize_array(Array *array, size_t index, ssize_t delta_nb_entries) { size_t nb_moved_entries; size_t new_length; void *tmp; assert(index < array->length); if (delta_nb_entries == 0) return 0; new_length = array->length + delta_nb_entries; nb_moved_entries = array->length - index; if (delta_nb_entries > 0) { tmp = talloc_realloc(array, array->_cache, ArrayEntry, new_length); if (tmp == NULL) return -ENOMEM; array->_cache = tmp; memmove(array->_cache + index + delta_nb_entries, array->_cache + index, nb_moved_entries * sizeof(ArrayEntry)); bzero(array->_cache + index, delta_nb_entries * sizeof(ArrayEntry)); } else { assert(delta_nb_entries <= 0); assert(index >= (size_t) -delta_nb_entries); memmove(array->_cache + index + delta_nb_entries, array->_cache + index, nb_moved_entries * sizeof(ArrayEntry)); tmp = talloc_realloc(array, array->_cache, ArrayEntry, new_length); if (tmp == NULL) return -ENOMEM; array->_cache = tmp; } array->length = new_length; return 0; } /** * Copy and cache into @array the pointer table pointed to by @reg * from the @tracee memory space general. Only the first @nb_entries * are copied, unless it is 0 then all the entries up to the NULL * pointer are copied. This function returns -errno when an error * occured, otherwise 0. */ int fetch_array(Tracee *tracee, Array **array_, Reg reg, size_t nb_entries) { word_t pointer = (word_t)-1; word_t address; Array *array; size_t i; assert(array_ != NULL); *array_ = talloc_zero(tracee->ctx, Array); if (*array_ == NULL) return -ENOMEM; array = *array_; address = peek_reg(tracee, CURRENT, reg); for (i = 0; nb_entries != 0 ? i < nb_entries : pointer != 0; i++) { void *tmp = talloc_realloc(array, array->_cache, ArrayEntry, i + 1); if (tmp == NULL) return -ENOMEM; array->_cache = tmp; pointer = peek_mem(tracee, address + i * sizeof_word(tracee)); if (errno != 0) return -EFAULT; array->_cache[i].remote = pointer; array->_cache[i].local = NULL; } array->length = i; /* By default, assume it is an array of string pointers. */ array->read_item = (read_item_t)read_item_string; array->sizeof_item = sizeof_item_string; array->write_item = (write_item_t)write_item_string; /* By default, use generic callbacks: they rely on * array->read_item() and array->sizeof_item(). */ array->compare_item = compare_item_generic; return 0; } /** * Copy -- if needed -- the pointer table and new items cached by * @array into the tracee's memory, and make @reg points to the new * pointer table. This function returns -errno if an error occured, * otherwise 0. */ int push_array(Array *array, Reg reg) { Tracee *tracee; struct iovec *local; size_t local_count; size_t total_size; word_t *pod_array; word_t tracee_ptr; int status; size_t i; /* Nothing to do, for sure. */ if (array == NULL) return 0; tracee = TRACEE(array); /* The pointer table is a POD array in the tracee's memory. */ pod_array = talloc_zero_size(tracee->ctx, array->length * sizeof_word(tracee)); if (pod_array == NULL) return -ENOMEM; /* There's one vector per modified item + one vector for the * pod array. */ local = talloc_zero_array(tracee->ctx, struct iovec, array->length + 1); if (local == NULL) return -ENOMEM; /* The pod array is expected to be at the beginning of the * allocated memory by the caller. */ total_size = array->length * sizeof_word(tracee); local[0].iov_base = pod_array; local[0].iov_len = total_size; local_count = 1; /* Create one vector for each modified item. */ for (i = 0; i < array->length; i++) { ssize_t size; if (array->_cache[i].local == NULL) continue; /* At this moment, we only know the offsets in the * tracee's memory block. */ array->_cache[i].remote = total_size; size = sizeof_item(array, i); if (size < 0) return size; total_size += size; local[local_count].iov_base = array->_cache[i].local; local[local_count].iov_len = size; local_count++; } /* Nothing has changed, don't update anything. */ if (local_count == 1) return 0; assert(local_count < array->length + 1); /* Modified items and the pod array are stored in a tracee's * memory block. */ tracee_ptr = alloc_mem(tracee, total_size); if (tracee_ptr == 0) return -E2BIG; /* Now, we know the absolute addresses in the tracee's * memory. */ for (i = 0; i < array->length; i++) { if (array->_cache[i].local != NULL) array->_cache[i].remote += tracee_ptr; if (is_32on64_mode(tracee)) ((uint32_t *)pod_array)[i] = array->_cache[i].remote; else pod_array[i] = array->_cache[i].remote; } /* Write all the modified items and the pod array at once. */ status = writev_data(tracee, tracee_ptr, local, local_count); if (status < 0) return status; poke_reg(tracee, reg, tracee_ptr); return 0; } proot-3.0.2/src/notice.h0000644000175000017500000000270212156552156014465 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef NOTICE_H #define NOTICE_H #include "tracee/tracee.h" #include "attribute.h" /* Specify where a notice is coming from. */ typedef enum { SYSTEM, INTERNAL, USER, TALLOC, } Origin; /* Specify the severity of a notice. */ typedef enum { ERROR, WARNING, INFO, } Severity; #define VERBOSE(tracee, level, message, args...) do { \ if (tracee == NULL || tracee->verbose >= (level)) \ notice(tracee, INFO, INTERNAL, (message), ## args); \ } while (0) extern void notice(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...) FORMAT(gnu_printf, 4, 5); #endif /* NOTICE_H */ proot-3.0.2/src/arch.h0000644000175000017500000000613112156552156014121 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* linux.git:c0a3a20b */ #include /* AUDIT_ARCH_*, */ #ifndef ARCH_H #define ARCH_H typedef unsigned long word_t; #define SYSCALL_AVOIDER ((word_t) -2) #if !defined(ARCH_X86_64) && !defined(ARCH_ARM_EABI) && !defined(ARCH_X86) && !defined(ARCH_SH4) # if defined(__x86_64__) # define ARCH_X86_64 1 # elif defined(__ARM_EABI__) # define ARCH_ARM_EABI 1 # elif defined(__aarch64__) # define ARCH_ARM64 1 # elif defined(__arm__) # error "Only EABI is currently supported for ARM" # elif defined(__i386__) # define ARCH_X86 1 # elif defined(__SH4__) # define ARCH_SH4 1 # else # error "Unsupported architecture" # endif #endif /* Architecture specific definitions. */ #if defined(ARCH_X86_64) #define SYSNUM_HEADER "syscall/sysnum-x86_64.h" #define SYSNUM_HEADER2 "syscall/sysnum-i386.h" #define SYSNUM_HEADER3 "syscall/sysnum-x32.h" #define HOST_ELF_MACHINE {62, 3, 6, 0} #define RED_ZONE_SIZE 128 #define OFFSETOF_STAT_UID_32 24 #define OFFSETOF_STAT_GID_32 28 #elif defined(ARCH_ARM_EABI) #define user_regs_struct user_regs #define SYSNUM_HEADER "syscall/sysnum-arm.h" #define HOST_ELF_MACHINE {40, 0}; #define RED_ZONE_SIZE 0 #define OFFSETOF_STAT_UID_32 0 #define OFFSETOF_STAT_GID_32 0 #define EM_ARM 40 #define AUDIT_ARCH_NUM AUDIT_ARCH_ARM #elif defined(ARCH_ARM64) #define user_regs_struct user_pt_regs #define SYSNUM_HEADER "syscall/sysnum-arm64.h" #define HOST_ELF_MACHINE {183, 0}; #define RED_ZONE_SIZE 0 #define OFFSETOF_STAT_UID_32 0 #define OFFSETOF_STAT_GID_32 0 #elif defined(ARCH_X86) #define SYSNUM_HEADER "syscall/sysnum-i386.h" #define HOST_ELF_MACHINE {3, 6, 0}; #define RED_ZONE_SIZE 0 #define OFFSETOF_STAT_UID_32 0 #define OFFSETOF_STAT_GID_32 0 #define AUDIT_ARCH_NUM AUDIT_ARCH_I386 #elif defined(ARCH_SH4) #define user_regs_struct pt_regs #define SYSNUM_HEADER "syscall/sysnum-sh4.h" #define HOST_ELF_MACHINE {42, 0}; #define RED_ZONE_SIZE 0 #define OFFSETOF_STAT_UID_32 0 #define OFFSETOF_STAT_GID_32 0 #define NO_MISALIGNED_ACCESS 1 #else #error "Unsupported architecture" #endif #endif /* ARCH_H */ proot-3.0.2/src/.check_seccomp_filter.c0000644000175000017500000000171612156552156017414 0ustar ivoireivoire#include /* prctl(2), PR_* */ #include /* SECCOMP_MODE_FILTER, */ #include /* struct sock_*, */ #include /* AUDIT_ARCH_*, */ #include /* offsetof(3), */ int main(void) { const size_t arch_offset = offsetof(struct seccomp_data, arch); const size_t syscall_offset = offsetof(struct seccomp_data, nr); struct sock_fprog program; #define ARCH_NR AUDIT_ARCH_X86_64 struct sock_filter filter[] = { BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_offset), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, AUDIT_ARCH_X86_64, 0, 1), BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_offset), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE) }; program.filter = filter; program.len = sizeof(filter) / sizeof(struct sock_filter); (void) prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); (void) prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program); return 1; } proot-3.0.2/src/path/0000755000175000017500000000000012156552156013766 5ustar ivoireivoireproot-3.0.2/src/path/canon.h0000644000175000017500000000213712156552156015240 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef CANON_H #define CANON_H #include #include #include "tracee/tracee.h" extern int canonicalize(Tracee *tracee, const char *user_path, bool deref_final, char guest_path[PATH_MAX], unsigned int nb_recursion); #endif /* CANON_H */ proot-3.0.2/src/path/canon.c0000644000175000017500000002021412156552156015227 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* pid_t */ #include /* PATH_MAX, */ #include /* MAXSYMLINKS, */ #include /* E*, */ #include /* lstat(2), S_ISREG(), */ #include /* access(2), lstat(2), */ #include /* string(3), */ #include /* assert(3), */ #include "path/canon.h" #include "path/path.h" #include "path/binding.h" #include "path/glue.h" #include "path/proc.h" #include "extension/extension.h" /** * Resolve bindings (if any) in @guest_path and copy the translated * path into @host_path. Also, this function checks that a non-final * component is either a directory (returned value is 0) or a symlink * (returned value is 1), otherwise it returns -errno (-ENOENT or * -ENOTDIR). */ static inline int substitute_binding_stat(Tracee *tracee, Finality is_final, const char guest_path[PATH_MAX], char host_path[PATH_MAX]) { struct stat statl; int status; strcpy(host_path, guest_path); status = substitute_binding(tracee, GUEST, host_path); if (status < 0) return status; /* Don't notify extensions during the initialization of a binding. */ if (tracee->glue_type == 0) { status = notify_extensions(tracee, HOST_PATH, (intptr_t)host_path, is_final); if (status < 0) return status; } statl.st_mode = 0; status = lstat(host_path, &statl); /* Build the glue between the hostfs and the guestfs during * the initialization of a binding. */ if (status < 0 && tracee->glue_type != 0) { statl.st_mode = build_glue(tracee, guest_path, host_path, is_final); if (statl.st_mode == 0) status = -1; } /* Return an error if a non-final component isn't a * directory nor a symlink. The error is "No such * file or directory" if this component doesn't exist, * otherwise the error is "Not a directory". */ if (!is_final && !S_ISDIR(statl.st_mode) && !S_ISLNK(statl.st_mode)) return (status < 0 ? -ENOENT : -ENOTDIR); return (S_ISLNK(statl.st_mode) ? 1 : 0); } /** * Copy in @guest_path the canonicalization (see `man 3 realpath`) of * @user_path regarding to @tracee->root. The path to canonicalize * could be either absolute or relative to @guest_path. When the last * component of @user_path is a link, it is dereferenced only if * @deref_final is true -- it is useful for syscalls like lstat(2). * The parameter @recursion_level should be set to 0 unless you know * what you are doing. This function returns -errno if an error * occured, otherwise it returns 0. */ int canonicalize(Tracee *tracee, const char *user_path, bool deref_final, char guest_path[PATH_MAX], unsigned int recursion_level) { char scratch_path[PATH_MAX]; bool pending_dot = false; Finality is_final; const char *cursor; int status; /* Avoid infinite loop on circular links. */ if (recursion_level > MAXSYMLINKS) return -ELOOP; /* Sanity checks. */ assert(user_path != NULL); assert(guest_path != NULL); assert(user_path != guest_path); if (user_path[0] != '/') { /* Ensure 'guest_path' contains an absolute base of * the relative `user_path`. */ if (guest_path[0] != '/') return -EINVAL; } else strcpy(guest_path, "/"); /* Canonicalize recursely 'user_path' into 'guest_path'. */ cursor = user_path; is_final = NOT_FINAL; while (!is_final) { Comparison comparison; char component[NAME_MAX]; char host_path[PATH_MAX]; status = next_component(component, &cursor); if (status < 0) return status; if (status == FINAL_SLASH && pending_dot) is_final = FINAL_DOT; else is_final = status; if (strcmp(component, ".") == 0) { if (is_final) is_final = FINAL_DOT; pending_dot = true; continue; } pending_dot = false; if (strcmp(component, "..") == 0) { pop_component(guest_path); if (is_final) is_final = FINAL_SLASH; continue; } status = join_paths(2, scratch_path, guest_path, component); if (status < 0) return status; /* Resolve bindings and check that a non-final * component exists and either is a directory or is a * symlink. For this latter case, we check that the * symlink points to a directory once it is * canonicalized, at the end of this loop. */ status = substitute_binding_stat(tracee, is_final, scratch_path, host_path); if (status < 0) return status; /* Nothing special to do if it's not a link or if we * explicitly ask to not dereference 'user_path', as * required by syscalls like lstat(2). Obviously, this * later condition does not apply to intermediate path * components. Errors are explicitly ignored since * they should be handled by the caller. */ if (status <= 0 || (is_final == FINAL_NORMAL && !deref_final)) { strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, component); if (status < 0) return status; continue; } /* It's a link, so we have to dereference *and* * canonicalize to ensure we are not going outside the * new root. */ comparison = compare_paths("/proc", guest_path); switch (comparison) { case PATHS_ARE_EQUAL: case PATH1_IS_PREFIX: /* Some links in "/proc" are generated * dynamically by the kernel. PRoot has to * emulate some of them. */ status = readlink_proc(tracee, scratch_path, guest_path, component, comparison); switch (status) { case CANONICALIZE: /* The symlink is already dereferenced, * now canonicalize it. */ goto canon; case DONT_CANONICALIZE: /* If and only very final, this symlink * shouldn't be dereferenced nor canonicalized. */ if (is_final == FINAL_NORMAL && recursion_level == 0) { strcpy(guest_path, scratch_path); return 0; } break; default: if (status < 0) return status; } default: break; } status = readlink(host_path, scratch_path, sizeof(scratch_path)); if (status < 0) return status; else if (status == sizeof(scratch_path)) return -ENAMETOOLONG; scratch_path[status] = '\0'; /* Remove the leading "root" part if needed, it's * useful for "/proc/self/cwd/" for instance. */ status = detranslate_path(tracee, scratch_path, host_path); if (status < 0) return status; canon: /* Canonicalize recursively the referee in case it * is/contains a link, moreover if it is not an * absolute link then it is relative to * 'guest_path'. */ status = canonicalize(tracee, scratch_path, true, guest_path, recursion_level + 1); if (status < 0) return status; /* Check that a non-final canonicalized/dereferenced * symlink exists and is a directory. */ status = substitute_binding_stat(tracee, is_final, guest_path, host_path); if (status < 0) return status; /* Here, 'guest_path' shouldn't be a symlink anymore. */ assert(status != 1); } /* At the exit stage of the first level of recursion, * `guest_path` is fully canonicalized but a terminating '/' * or a terminating '.' may be required to keep the initial * semantic of `user_path`. */ if (recursion_level == 0) { switch (is_final) { case FINAL_NORMAL: break; case FINAL_SLASH: strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, ""); if (status < 0) return status; break; case FINAL_DOT: strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, "."); if (status < 0) return status; break; default: assert(0); } } return 0; } proot-3.0.2/src/path/binding.h0000644000175000017500000000327512156552156015560 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef BINDING_H #define BINDING_H #include /* PATH_MAX, */ #include #include "tracee/tracee.h" #include "path.h" typedef struct binding { Path host; Path guest; bool need_substitution; bool must_exist; struct { CIRCLEQ_ENTRY(binding) pending; CIRCLEQ_ENTRY(binding) guest; CIRCLEQ_ENTRY(binding) host; } link; } Binding; typedef CIRCLEQ_HEAD(bindings, binding) Bindings; extern void insort_binding2(Tracee *tracee, Binding *binding); extern Binding *new_binding(Tracee *tracee, const char *host, const char *guest, bool must_exist); extern int initialize_bindings(Tracee *tracee); extern const char *get_path_binding(Tracee* tracee, Side side, const char path[PATH_MAX]); extern const char *get_root(const Tracee* tracee); extern int substitute_binding(Tracee* tracee, Side side, char path[PATH_MAX]); #endif /* BINDING_H */ proot-3.0.2/src/path/path.h0000644000175000017500000000547712156552156015110 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef PATH_H #define PATH_H #include /* pid_t, */ #include /* AT_FDCWD, */ #include /* PATH_MAX, */ #include #include "tracee/tracee.h" /* File type. */ typedef enum { REGULAR, SYMLINK, } Type; /* Path point-of-view. */ typedef enum { GUEST, HOST, /* Used for bindings as specified by the user but not * canonicalized yet (new_binding, initialize_binding). */ PENDING, } Side; /* Path with cached attributes. */ typedef struct { char path[PATH_MAX]; size_t length; Side side; } Path; /* Path ending type. */ typedef enum { NOT_FINAL, FINAL_NORMAL, FINAL_SLASH, FINAL_DOT } Finality; /* Comparison between two paths. */ typedef enum Comparison { PATHS_ARE_EQUAL, PATH1_IS_PREFIX, PATH2_IS_PREFIX, PATHS_ARE_NOT_COMPARABLE, } Comparison; extern int which(Tracee *tracee, const char *paths, char host_path[PATH_MAX], char *const command); extern int realpath2(Tracee *tracee, char host_path[PATH_MAX], const char *path, bool deref_final); extern int getcwd2(Tracee *tracee, char guest_path[PATH_MAX]); extern void chop_finality(char *path); extern int translate_path(Tracee *tracee, char host_path[PATH_MAX], int dir_fd, const char *guest_path, bool deref_final); extern int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]); extern bool belongs_to_guestfs(const Tracee *tracee, const char *path); extern int join_paths(int number_paths, char result[PATH_MAX], ...); extern Finality next_component(char component[NAME_MAX], const char **cursor); extern void pop_component(char *path); extern int list_open_fd(const Tracee *tracee); extern Comparison compare_paths(const char *path1, const char *path2); extern Comparison compare_paths2(const char *path1, size_t length1, const char *path2, size_t length2); /* Check if path interpretable relatively to dirfd, see openat(2) for details. */ #define AT_FD(dirfd, path) ((dirfd) != AT_FDCWD && ((path) != NULL && (path)[0] != '/')) #endif /* PATH_H */ proot-3.0.2/src/path/proc.h0000644000175000017500000000303112156552156015077 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef PROC_H #define PROC_H #include #include "tracee/tracee.h" #include "path/path.h" /* Action to do after a call to readlink_proc(). */ typedef enum { DEFAULT, /* Nothing special to do, treat it as a regular link. */ CANONICALIZE, /* The symlink was dereferenced, now canonicalize it. */ DONT_CANONICALIZE, /* The symlink shouldn't be dereferenced nor canonicalized. */ } Action; extern Action readlink_proc(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX], const char component[NAME_MAX], Comparison comparison); extern size_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX]); #endif /* PROC_H */ proot-3.0.2/src/path/path.c0000644000175000017500000004301012156552156015064 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _ATFILE_SOURCE /* readlinkat(2), */ #include /* string(3), */ #include /* va_*(3), */ #include /* assert(3), */ #include /* AT_*, */ #include /* readlink*(2), *stat(2), getpid(2), */ #include /* pid_t, */ #include /* S_ISDIR, */ #include /* opendir(3), readdir(3), */ #include /* snprintf(3), */ #include /* E*, */ #include /* ptrdiff_t, */ #include "path/path.h" #include "path/binding.h" #include "path/canon.h" #include "path/proc.h" #include "extension/extension.h" #include "notice.h" #include "build.h" #include "compat.h" /** * Copy in @component the first path component pointed to by @cursor, * this later is updated to point to the next component for a further * call. This function returns: * * - -errno if an error occured. * * - FINAL_SLASH if it the last component of the path but we * really expect a directory. * * - FINAL_NORMAL if it the last component of the path. * * - 0 otherwise. */ Finality next_component(char component[NAME_MAX], const char **cursor) { const char *start; ptrdiff_t length; bool want_dir; /* Sanity checks. */ assert(component != NULL); assert(cursor != NULL); /* Skip leading path separators. */ while (**cursor != '\0' && **cursor == '/') (*cursor)++; /* Find the next component. */ start = *cursor; while (**cursor != '\0' && **cursor != '/') (*cursor)++; length = *cursor - start; if (length >= NAME_MAX) return -ENAMETOOLONG; /* Extract the component. */ strncpy(component, start, length); component[length] = '\0'; /* Check if a [link to a] directory is expected. */ want_dir = (**cursor == '/'); /* Skip trailing path separators. */ while (**cursor != '\0' && **cursor == '/') (*cursor)++; if (**cursor == '\0') return (want_dir ? FINAL_SLASH : FINAL_NORMAL); return NOT_FINAL; } /** * Put an end-of-string ('\0') right before the last component of @path. */ void pop_component(char *path) { int offset; /* Sanity checks. */ assert(path != NULL); offset = strlen(path) - 1; assert(offset >= 0); /* Don't pop over "/", it doesn't mean anything. */ if (offset == 0) { assert(path[0] == '/' && path[1] == '\0'); return; } /* Skip trailing path separators. */ while (offset > 1 && path[offset] == '/') offset--; /* Search for the previous path separator. */ while (offset > 1 && path[offset] != '/') offset--; /* Cut the end of the string before the last component. */ path[offset] = '\0'; assert(path[0] == '/'); } /** * Copy in @result the concatenation of several paths (@number_paths) * and adds a path separator ('/') in between when needed. This * function returns -errno if an error occured, otherwise it returns 0. */ int join_paths(int number_paths, char result[PATH_MAX], ...) { va_list paths; size_t length; int i; result[0] = '\0'; /* Parse the list of variadic arguments. */ va_start(paths, result); length = 0; for (i = 0; i < number_paths; i++) { const char *path; int need_separator; size_t old_length; path = va_arg(paths, const char *); if (path == NULL) continue; /* Check if a path separator is needed. */ if (length > 0 && result[length - 1] != '/' && path[0] != '/') need_separator = 1; else if (length > 0 && result[length - 1] == '/' && path[0] == '/') need_separator = -1; else need_separator = 0; old_length = length; length += strlen(path) + need_separator; if (length >= PATH_MAX) { va_end(paths); return -ENAMETOOLONG; } if (need_separator == -1) { path++; } else if (need_separator == 1) { strcat(result + old_length, "/"); old_length++; } strcat(result + old_length, path); } va_end(paths); return 0; } /** * Put in @host_path the full path to the given shell @command. The * @command is searched in @paths if not null, otherwise in $PATH * (relatively to the @tracee's file-system name-space). This * function always returns -1 on error, otherwise 0. */ int which(Tracee *tracee, const char *paths, char host_path[PATH_MAX], char *const command) { char path[PATH_MAX]; const char *cursor; struct stat statr; int status; assert(command != NULL); /* Is the command available without any $PATH look-up? */ status = realpath2(tracee, host_path, command, true); if (status == 0 && stat(host_path, &statr) == 0 && S_ISREG(statr.st_mode) && (statr.st_mode & S_IXUSR) != 0) return 0; /* Is it an absolute path and it wasn't found? */ if (command[0] == '/') { notice(tracee, WARNING, USER, "'%s' not found (root = %s)", command, get_root(tracee)); return -1; } /* Is it a relative path and it wasn't found? */ if (command[0] == '.') { status = getcwd2(tracee, path); if (status < 0) strcpy(path, ""); notice(tracee, WARNING, USER, "'%s' not found (root = %s, cwd = %s)", command, get_root(tracee), path); return -1; } /* Otherwise search the command in $PATH. */ paths = paths ?: getenv("PATH"); if (paths == NULL) { status = getcwd2(tracee, path); if (status < 0) strcpy(path, ""); notice(tracee, WARNING, USER, "'%s' not found (root = %s, cwd = %s) and $PATH isn't set", command, get_root(tracee), path); return -1; } cursor = paths; do { size_t length; length = strcspn(cursor, ":"); cursor += length + 1; if (length >= PATH_MAX) continue; else if (length == 0) strcpy(path, "."); else { strncpy(path, cursor - length - 1, length); path[length] = '\0'; } /* Avoid buffer-overflow. */ if (length + strlen(command) + 2 >= PATH_MAX) continue; strcat(path, "/"); strcat(path, command); status = realpath2(tracee, host_path, path, true); if (status == 0 && stat(host_path, &statr) == 0 && S_ISREG(statr.st_mode) && (statr.st_mode & S_IXUSR) != 0) return 0; } while (*(cursor - 1) != '\0'); notice(tracee, WARNING, USER, "'%s' not found in $PATH=%s (root = %s)", command, paths, get_root(tracee)); return -1; } /** * Put in @host_path the canonicalized form of @path. In the nominal * case (@tracee == NULL), this function is barely equivalent to * realpath(), but when doing sub-reconfiguration, the path is * canonicalized relatively to the current @tracee's file-system * name-space. This function returns -errno on error, otherwise 0. */ int realpath2(Tracee *tracee, char host_path[PATH_MAX], const char *path, bool deref_final) { int status; if (tracee == NULL) status = (realpath(path, host_path) == NULL ? -errno : 0); else status = translate_path(tracee, host_path, AT_FDCWD, path, deref_final); return status; } /** * Put in @guest_path the canonicalized current working directory. In * the nominal case (@tracee == NULL), this function is barely * equivalent to realpath(), but when doing sub-reconfiguration, the * path is canonicalized relatively to the current @tracee's * file-system name-space. This function returns -errno on error, * otherwise 0. */ int getcwd2(Tracee *tracee, char guest_path[PATH_MAX]) { if (tracee == NULL) { if (getcwd(guest_path, PATH_MAX) == NULL) return -errno; } else { if (strlen(tracee->fs->cwd) >= PATH_MAX) return -ENAMETOOLONG; strcpy(guest_path, tracee->fs->cwd); } return 0; } /** * Remove the trailing "/" or "/.". */ void chop_finality(char *path) { size_t length = strlen(path); if (path[length - 1] == '.') { assert(length >= 2); /* Special case for "/." */ if (length == 2) path[length - 1] = '\0'; else path[length - 2] = '\0'; } else if (path[length - 1] == '/') { /* Special case for "/" */ if (length > 1) path[length - 1] = '\0'; } } /** * Copy in @host_path the equivalent of "@tracee->root + * canonicalize(@dir_fd + @guest_path)". If @guest_path is not * absolute then it is relative to the directory referred by the * descriptor @dir_fd (AT_FDCWD is for the current working directory). * See the documentation of canonicalize() for the meaning of * @deref_final. This function returns -errno if an error occured, * otherwise 0. */ int translate_path(Tracee *tracee, char host_path[PATH_MAX], int dir_fd, const char *guest_path, bool deref_final) { int status; /* Use "/" as the base if it is an absolute guest path. */ if (guest_path[0] == '/') { strcpy(host_path, "/"); } /* It is relative to the current working directory or to a * directory referred by a descriptors, see openat(2) for * details. */ else if (dir_fd == AT_FDCWD) { status = getcwd2(tracee, host_path); if (status < 0) return status; } else { char link[32]; /* 32 > sizeof("/proc//cwd") + sizeof(#ULONG_MAX) */ struct stat statl; /* Format the path to the "virtual" link. */ status = snprintf(link, sizeof(link), "/proc/%d/fd/%d", tracee->pid, dir_fd); if (status < 0) return -EPERM; if ((size_t) status >= sizeof(link)) return -EPERM; /* Read the value of this "virtual" link. */ status = readlink(link, host_path, PATH_MAX); if (status < 0) return -EPERM; if (status >= PATH_MAX) return -ENAMETOOLONG; host_path[status] = '\0'; /* Ensure it points to a directory. */ status = stat(host_path, &statl); if (!S_ISDIR(statl.st_mode)) return -ENOTDIR; /* Remove the leading "root" part of the base * (required!). */ status = detranslate_path(tracee, host_path, NULL); if (status < 0) return status; } VERBOSE(tracee, 2, "pid %d: translate(\"%s\" + \"%s\")", tracee != NULL ? tracee->pid : 0, host_path, guest_path); status = notify_extensions(tracee, GUEST_PATH, (intptr_t)host_path, (intptr_t)guest_path); if (status < 0) return status; if (status > 0) goto skip; /* Canonicalize regarding the new root. */ status = canonicalize(tracee, guest_path, deref_final, host_path, 0); if (status < 0) return status; /* Final binding substitution to convert "host_path" into a host * path, since canonicalize() works from the guest * point-of-view. */ status = substitute_binding(tracee, GUEST, host_path); if (status < 0) return status; skip: VERBOSE(tracee, 2, "pid %d: -> \"%s\"", tracee != NULL ? tracee->pid : 0, host_path); return 0; } /** * Remove/substitute the leading part of a "translated" @path. It * returns 0 if no transformation is required (ie. symmetric binding), * otherwise it returns the size in bytes of the updated @path, * including the end-of-string terminator. On error it returns * -errno. */ int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]) { size_t prefix_length; size_t new_length; bool sanity_check; bool follow_binding; /* Don't try to detranslate relative paths (typically the * target of a relative symbolic link). */ if (path[0] != '/') return 0; /* Is it a symlink? */ if (t_referrer != NULL) { Comparison comparison; sanity_check = false; follow_binding = false; /* In some cases bindings have to be resolved. */ comparison = compare_paths("/proc", t_referrer); if (comparison == PATH1_IS_PREFIX) { /* Some links in "/proc" are generated * dynamically by the kernel. PRoot has to * emulate some of them. */ char proc_path[PATH_MAX]; strcpy(proc_path, path); new_length = readlink_proc2(tracee, proc_path, t_referrer); if (new_length != 0) { strcpy(path, proc_path); return new_length + 1; } /* Always resolve bindings for symlinks in * "/proc", they always point to the emulated * file-system namespace by design. */ follow_binding = true; } else if (!belongs_to_guestfs(tracee, t_referrer)) { const char *binding_referree; const char *binding_referrer; binding_referree = get_path_binding(tracee, HOST, path); binding_referrer = get_path_binding(tracee, HOST, t_referrer); assert(binding_referrer != NULL); /* Resolve bindings for symlinks that belong * to a binding and point to the same binding. * For example, if "-b /lib:/foo" is specified * and the symlink "/lib/a -> /lib/b" exists * in the host rootfs namespace, then it * should appear as "/foo/a -> /foo/b" in the * guest rootfs namespace for consistency * reasons. */ if (binding_referree != NULL) { comparison = compare_paths(binding_referree, binding_referrer); follow_binding = (comparison == PATHS_ARE_EQUAL); } } } else { sanity_check = true; follow_binding = true; } if (follow_binding) { switch (substitute_binding(tracee, HOST, path)) { case 0: return 0; case 1: return strlen(path) + 1; default: break; } } switch (compare_paths(get_root(tracee), path)) { case PATH1_IS_PREFIX: /* Remove the leading part, that is, the "root". */ prefix_length = strlen(get_root(tracee)); /* Special case when path to the guest rootfs == "/". */ if (prefix_length == 1) prefix_length = 0; new_length = strlen(path) - prefix_length; memmove(path, path + prefix_length, new_length); path[new_length] = '\0'; break; case PATHS_ARE_EQUAL: /* Special case when path == root. */ new_length = 1; strcpy(path, "/"); break; default: /* Ensure the path is within the new root. */ if (sanity_check) return -EPERM; else return 0; } return new_length + 1; } /** * Check if the translated @t_path belongs to the guest rootfs, that * is, isn't from a binding. */ bool belongs_to_guestfs(const Tracee *tracee, const char *host_path) { Comparison comparison; comparison = compare_paths(get_root(tracee), host_path); return (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX); } /** * Compare @path1 with @path2, which are respectively @length1 and * @length2 long. * * This function works only with paths canonicalized in the same * namespace (host/guest)! */ Comparison compare_paths2(const char *path1, size_t length1, const char *path2, size_t length2) { size_t length_min; bool is_prefix; char sentinel; #if defined DEBUG_OPATH assert(length(path1) == length1); assert(length(path2) == length2); #endif assert(length1 > 0); assert(length2 > 0); if (!length1 || !length2) { return PATHS_ARE_NOT_COMPARABLE; } /* Remove potential trailing '/' for the comparison. */ if (path1[length1 - 1] == '/') length1--; if (path2[length2 - 1] == '/') length2--; if (length1 < length2) { length_min = length1; sentinel = path2[length_min]; } else { length_min = length2; sentinel = path1[length_min]; } /* Optimize obvious cases. */ if (sentinel != '/' && sentinel != '\0') return PATHS_ARE_NOT_COMPARABLE; is_prefix = (strncmp(path1, path2, length_min) == 0); if (!is_prefix) return PATHS_ARE_NOT_COMPARABLE; if (length1 == length2) return PATHS_ARE_EQUAL; else if (length1 < length2) return PATH1_IS_PREFIX; else if (length1 > length2) return PATH2_IS_PREFIX; assert(0); return PATHS_ARE_NOT_COMPARABLE; } Comparison compare_paths(const char *path1, const char *path2) { return compare_paths2(path1, strlen(path1), path2, strlen(path2)); } typedef int (*foreach_fd_t)(const Tracee *tracee, int fd, char path[PATH_MAX]); /** * Call @callback on each open file descriptors of @pid. It returns * the status of the first failure, that is, if @callback returned * seomthing lesser than 0, otherwise 0. */ static int foreach_fd(const Tracee *tracee, foreach_fd_t callback) { struct dirent *dirent; char path[PATH_MAX]; char proc_fd[32]; /* 32 > sizeof("/proc//fd") + sizeof(#ULONG_MAX) */ int status; DIR *dirp; /* Format the path to the "virtual" directory. */ status = snprintf(proc_fd, sizeof(proc_fd), "/proc/%d/fd", tracee->pid); if (status < 0 || (size_t) status >= sizeof(proc_fd)) return 0; /* Open the virtual directory "/proc/$pid/fd". */ dirp = opendir(proc_fd); if (dirp == NULL) return 0; while ((dirent = readdir(dirp)) != NULL) { /* Read the value of this "virtual" link. */ #ifdef HAVE_READLINKAT status = readlinkat(dirfd(dirp), dirent->d_name, path, PATH_MAX); #else char tmp[PATH_MAX]; if (strlen(proc_fd) + strlen(dirent->d_name) + 1 >= PATH_MAX) continue; strcpy(tmp, proc_fd); strcat(tmp, "/"); strcat(tmp, dirent->d_name); status = readlink(tmp, path, PATH_MAX); #endif if (status < 0 || status >= PATH_MAX) continue; path[status] = '\0'; /* Ensure it points to a path (not a socket or somethink like that). */ if (path[0] != '/') continue; status = callback(tracee, atoi(dirent->d_name), path); if (status < 0) goto end; } status = 0; end: closedir(dirp); return status; } /** * Warn for files that are open. It is useful right after PRoot has * attached a process. */ int list_open_fd(const Tracee *tracee) { int list_open_fd_callback(const Tracee *tracee, int fd, char path[PATH_MAX]) { VERBOSE(tracee, 1, "pid %d: access to \"%s\" (fd %d) won't be translated until closed", tracee->pid, path, fd); return 0; } return foreach_fd(tracee, list_open_fd_callback); } proot-3.0.2/src/path/glue.c0000644000175000017500000001216012156552156015066 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* mkdir(2), */ #include /* mkdir(2), */ #include /* mknod(2), */ #include /* mknod(2), */ #include /* mkdtemp(3), */ #include /* string(3), */ #include /* assert(3), */ #include /* PATH_MAX, */ #include /* errno, E* */ #include /* talloc_*, */ #include "path/binding.h" #include "path/path.h" #include "notice.h" #include "compat.h" /** * Delete only empty files and directories from the glue: the files * created by the user inside this glue are left. * * Note: this is a Talloc desctructor. */ static int remove_glue(char *path) { char *command; command = talloc_asprintf(NULL, "find %s -empty -delete 2>/dev/null", path); if (command != NULL) { int status; status = system(command); if (status != 0) notice(NULL, INFO, USER, "can't delete '%s'", path); } TALLOC_FREE(command); return 0; } /** * Build in a temporary filesystem the glue between the guest part and * the host part of the @binding_path. This function returns the type * of the bound path, otherwise 0 if an error occured. * * For example, assuming the host path "/opt" is mounted/bound to the * guest path "/black/holes/and/revelations", and assuming this path * can't be created in the guest rootfs (eg. permission denied), then * it is created in a temporary rootfs and all these paths are glued * that way: * * $GUEST/black/ --> $GLUE/black/ * ./holes * ./holes/and * ./holes/and/revelations --> $HOST/opt/ * * This glue allows operations on paths that do not exist in the guest * rootfs but that were specified as the guest part of a binding. */ mode_t build_glue(Tracee *tracee, const char *guest_path, char host_path[PATH_MAX], Finality is_final) { bool belongs_to_gluefs; Comparison comparison; Binding *binding; mode_t type; mode_t mode; int status; assert(tracee->glue_type != 0); /* Create the temporary directory where the "glue" rootfs will * lie. */ if (tracee->glue == NULL) { tracee->glue = talloc_asprintf(tracee, "/tmp/proot-%d-XXXXXX", getpid()); if (tracee->glue == NULL) return 0; talloc_set_name_const(tracee->glue, "$glue"); if (mkdtemp(tracee->glue) == NULL) { TALLOC_FREE(tracee->glue); return 0; } talloc_set_destructor(tracee->glue, remove_glue); } comparison = compare_paths(tracee->glue, host_path); belongs_to_gluefs = (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX); /* If it's not a final component then it is a directory. I definitely * hate how the potential type of the final component is propagated * from initialize_binding() down to here, sadly there's no elegant way * to know its type at this stage. */ if (is_final) { type = tracee->glue_type; mode = (belongs_to_gluefs ? 0777 : 0); } else { type = S_IFDIR; mode = 0777; } if (getenv("PROOT_DONT_POLLUTE_ROOTFS") != NULL && !belongs_to_gluefs) goto create_binding; /* Try to create this component into the "guest" or "glue" * rootfs (depending if there were a glue previously). */ if (S_ISDIR(type)) status = mkdir(host_path, mode); else /* S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO or S_IFSOCK. */ status = mknod(host_path, mode | type, 0); /* Nothing else to do if the path already exists or if it is * the final component since it will be pointed to by the * binding being initialized (from the example, * "$GUEST/black/holes/and/revelations" -> "$HOST/opt"). */ if (status >= 0 || errno == EEXIST || is_final) return type; /* mkdir/mknod are supposed to always succeed in * tracee->glue. */ if (belongs_to_gluefs) { notice(tracee, WARNING, SYSTEM, "mkdir/mknod"); return 0; } create_binding: /* From the example, create the binding "/black" -> * "$GLUE/black". */ binding = talloc_zero(tracee->glue, Binding); if (binding == NULL) return 0; strcpy(binding->host.path, tracee->glue); strcpy(binding->guest.path, guest_path); binding->host.length = strlen(binding->host.path); binding->guest.length = strlen(binding->guest.path); insort_binding2(tracee, binding); /* TODO: emulation of getdents(parent(guest_path)) to finalize * the glue, "black" in getdents("/") from the example. */ return type; } proot-3.0.2/src/path/binding.c0000644000175000017500000005210412156552156015546 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* lstat(2), */ #include /* getcwd(2), lstat(2), */ #include /* string(3), */ #include /* bzero(3), */ #include /* assert(3), */ #include /* PATH_MAX, */ #include /* E* */ #include /* CIRCLEQ_*, */ #include /* talloc_*, */ #include "path/binding.h" #include "path/path.h" #include "path/canon.h" #include "notice.h" #include "compat.h" #define HEAD(tracee, side) \ (side == GUEST \ ? (tracee)->fs->bindings.guest \ : (side == HOST \ ? (tracee)->fs->bindings.host \ : (tracee)->fs->bindings.pending)) #define NEXT(binding, side) \ (side == GUEST \ ? CIRCLEQ_NEXT(binding, link.guest) \ : (side == HOST \ ? CIRCLEQ_NEXT(binding, link.host) \ : CIRCLEQ_NEXT(binding, link.pending))) #define CIRCLEQ_FOREACH_(tracee, binding, side) \ for (binding = CIRCLEQ_FIRST(HEAD(tracee, side)); \ binding != (void *) HEAD(tracee, side); \ binding = NEXT(binding, side)) #define CIRCLEQ_INSERT_AFTER_(tracee, previous, binding, side) do { \ switch (side) { \ case GUEST: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.guest); break; \ case HOST: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.host); break; \ default: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.pending); break; \ } \ (void) talloc_reference(HEAD(tracee, side), binding); \ } while (0) #define CIRCLEQ_INSERT_BEFORE_(tracee, next, binding, side) do { \ switch (side) { \ case GUEST: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.guest); break; \ case HOST: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.host); break; \ default: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.pending); break; \ } \ (void) talloc_reference(HEAD(tracee, side), binding); \ } while (0) #define CIRCLEQ_INSERT_HEAD_(tracee, binding, side) do { \ switch (side) { \ case GUEST: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.guest); break; \ case HOST: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.host); break; \ default: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.pending); break; \ } \ (void) talloc_reference(HEAD(tracee, side), binding); \ } while (0) #define IS_LINKED(binding, link) \ ((binding)->link.cqe_next != NULL && (binding)->link.cqe_prev != NULL) #define CIRCLEQ_REMOVE_(tracee, binding, name) do { \ CIRCLEQ_REMOVE((tracee)->fs->bindings.name, binding, link.name);\ (binding)->link.name.cqe_next = NULL; \ (binding)->link.name.cqe_prev = NULL; \ talloc_unlink((tracee)->fs->bindings.name, binding); \ } while (0) /** * Print all bindings (verbose purpose). */ static void print_bindings(const Tracee *tracee) { const Binding *binding; if (tracee->fs->bindings.guest == NULL) return; CIRCLEQ_FOREACH_(tracee, binding, GUEST) { if (compare_paths(binding->host.path, binding->guest.path) == PATHS_ARE_EQUAL) notice(tracee, INFO, USER, "binding = %s", binding->host.path); else notice(tracee, INFO, USER, "binding = %s:%s", binding->host.path, binding->guest.path); } } /** * Get the binding for the given @path (relatively to the given * binding @side). */ static const Binding *get_binding(Tracee *tracee, Side side, const char path[PATH_MAX]) { const Binding *binding; size_t path_length = strlen(path); /* Sanity checks. */ assert(path != NULL && path[0] == '/'); CIRCLEQ_FOREACH_(tracee, binding, side) { Comparison comparison; const Path *ref; switch (side) { case GUEST: ref = &binding->guest; break; case HOST: ref = &binding->host; break; default: assert(0); return NULL; } comparison = compare_paths2(ref->path, ref->length, path, path_length); if ( comparison != PATHS_ARE_EQUAL && comparison != PATH1_IS_PREFIX) continue; /* Avoid false positive when a prefix of the rootfs is * used as an asymmetric binding, ex.: * * proot -m /usr:/location /usr/local/slackware */ if ( side == HOST && compare_paths(get_root(tracee), "/") != PATHS_ARE_EQUAL && belongs_to_guestfs(tracee, path)) continue; return binding; } return NULL; } /** * Get the binding path for the given @path (relatively to the given * binding @side). */ const char *get_path_binding(Tracee *tracee, Side side, const char path[PATH_MAX]) { const Binding *binding; binding = get_binding(tracee, side, path); if (!binding) return NULL; switch (side) { case GUEST: return binding->guest.path; case HOST: return binding->host.path; default: assert(0); return NULL; } } /** * Return the path to the guest rootfs for the given @tracee, from the * host point-of-view obviously. Depending on whether * initialize_bindings() was called or not, the path is retrieved from * the "bindings.guest" list or from the "bindings.pending" list, * respectively. */ const char *get_root(const Tracee* tracee) { const Binding *binding; if (tracee == NULL || tracee->fs == NULL) return NULL; if (tracee->fs->bindings.guest == NULL) { if (tracee->fs->bindings.pending == NULL || CIRCLEQ_EMPTY(tracee->fs->bindings.pending)) return NULL; binding = CIRCLEQ_LAST(tracee->fs->bindings.pending); if (compare_paths(binding->guest.path, "/") != PATHS_ARE_EQUAL) return NULL; return binding->host.path; } assert(!CIRCLEQ_EMPTY(tracee->fs->bindings.guest)); binding = CIRCLEQ_LAST(tracee->fs->bindings.guest); assert(strcmp(binding->guest.path, "/") == 0); return binding->host.path; } /** * Substitute the guest path (if any) with the host path in @path. * This function returns: * * * -errno if an error occured * * * 0 if it is a binding location but no substitution is needed * ("symetric" binding) * * * 1 if it is a binding location and a substitution was performed * ("asymmetric" binding) */ int substitute_binding(Tracee *tracee, Side side, char path[PATH_MAX]) { const Path *reverse_ref; const Path *ref; const Binding *binding; size_t path_length; size_t new_length; binding = get_binding(tracee, side, path); if (!binding) return -ENOENT; /* Is it a "symetric" binding? */ if (!binding->need_substitution) return 0; path_length = strlen(path); switch (side) { case GUEST: ref = &binding->guest; reverse_ref = &binding->host; break; case HOST: ref = &binding->host; reverse_ref = &binding->guest; break; default: assert(0); return -EACCES; } /* Substitute the leading ref' with reverse_ref'. */ if (reverse_ref->length == 1) { /* Special case when "-b /:/foo". Substitute * "/foo/bin" with "/bin" not "//bin". */ new_length = path_length - ref->length; if (new_length != 0) memmove(path, path + ref->length, new_length); else { /* Translating "/". */ path[0] = '/'; new_length = 1; } } else if (ref->length == 1) { /* Special case when "-b /:/foo". Substitute * "/bin" with "/foo/bin" not "/foobin". */ new_length = reverse_ref->length + path_length; if (new_length >= PATH_MAX) return -ENAMETOOLONG; if (path_length > 1) { memmove(path + reverse_ref->length, path, path_length); memcpy(path, reverse_ref->path, reverse_ref->length); } else { /* Translating "/". */ memcpy(path, reverse_ref->path, reverse_ref->length); new_length = reverse_ref->length; } } else { /* Generic substitution case. */ new_length = path_length - ref->length + reverse_ref->length; if (new_length >= PATH_MAX) return -ENAMETOOLONG; memmove(path + reverse_ref->length, path + ref->length, path_length - ref->length); memcpy(path, reverse_ref->path, reverse_ref->length); } path[new_length] = '\0'; return 1; } /** * Remove @binding from all the @tracee's lists of bindings it belongs to. */ static int remove_binding_from_all_lists(const Tracee *tracee, Binding *binding) { if (IS_LINKED(binding, link.pending)) CIRCLEQ_REMOVE_(tracee, binding, pending); if (IS_LINKED(binding, link.guest)) CIRCLEQ_REMOVE_(tracee, binding, guest); if (IS_LINKED(binding, link.host)) CIRCLEQ_REMOVE_(tracee, binding, host); return 0; } /** * Insert @binding into the list of @bindings, in a sorted manner so * as to make the substitution of nested bindings determistic, ex.: * * -b /bin:/foo/bin -b /usr/bin/more:/foo/bin/more * * Note: "nested" from the @side point-of-view. */ static void insort_binding(const Tracee *tracee, Side side, Binding *binding) { Binding *iterator; Binding *previous = NULL; Binding *next = CIRCLEQ_FIRST(HEAD(tracee, side)); /* Find where it should be added in the list. */ CIRCLEQ_FOREACH_(tracee, iterator, side) { Comparison comparison; const Path *binding_path; const Path *iterator_path; switch (side) { case PENDING: case GUEST: binding_path = &binding->guest; iterator_path = &iterator->guest; break; case HOST: binding_path = &binding->host; iterator_path = &iterator->host; break; default: assert(0); return; } comparison = compare_paths2(binding_path->path, binding_path->length, iterator_path->path, iterator_path->length); switch (comparison) { case PATHS_ARE_EQUAL: if (side == HOST) { previous = iterator; break; } if (tracee->verbose > 0) { notice(tracee, WARNING, USER, "both '%s' and '%s' are bound to '%s', " "only the last binding is active.", iterator->host.path, binding->host.path, binding->guest.path); } /* Replace this iterator with the new binding. */ CIRCLEQ_INSERT_AFTER_(tracee, iterator, binding, side); remove_binding_from_all_lists(tracee, iterator); return; case PATH1_IS_PREFIX: /* The new binding contains the iterator. */ previous = iterator; break; case PATH2_IS_PREFIX: /* The iterator contains the new binding. * Use the deepest container. */ if (next == (void *) HEAD(tracee, side)) next = iterator; break; case PATHS_ARE_NOT_COMPARABLE: break; default: assert(0); return; } } /* Insert this binding in the list. */ if (previous != NULL) CIRCLEQ_INSERT_AFTER_(tracee, previous, binding, side); else if (next != (void *) HEAD(tracee, side)) CIRCLEQ_INSERT_BEFORE_(tracee, next, binding, side); else CIRCLEQ_INSERT_HEAD_(tracee, binding, side); } /** * c.f. function above. */ void insort_binding2(Tracee *tracee, Binding *binding) { binding->need_substitution = compare_paths(binding->host.path, binding->guest.path) != PATHS_ARE_EQUAL; insort_binding(tracee, GUEST, binding); insort_binding(tracee, HOST, binding); } /** * Free all bindings from @bindings. * * Note: this is a Talloc destructor. */ static int remove_bindings(Bindings *bindings) { Binding *binding; Tracee *tracee; /* Unlink all bindings from the @link list. */ #define CIRCLEQ_REMOVE_ALL(name) do { \ binding = CIRCLEQ_FIRST(bindings); \ while (binding != (void *) bindings) { \ Binding *next = CIRCLEQ_NEXT(binding, link.name);\ CIRCLEQ_REMOVE_(tracee, binding, name); \ binding = next; \ } \ } while (0) /* Search which link is used by this list. */ tracee = TRACEE(bindings); if (bindings == tracee->fs->bindings.pending) CIRCLEQ_REMOVE_ALL(pending); else if (bindings == tracee->fs->bindings.guest) CIRCLEQ_REMOVE_ALL(guest); else if (bindings == tracee->fs->bindings.host) CIRCLEQ_REMOVE_ALL(host); bzero(bindings, sizeof(Bindings)); return 0; } /** * Allocate a new binding "@host:@guest" and attach it to * @tracee->fs->bindings.pending. This function complains about * missing @host path only if @must_exist is true. This function * returns the allocated binding on success, NULL on error. */ Binding *new_binding(Tracee *tracee, const char *host, const char *guest, bool must_exist) { Binding *binding; char base[PATH_MAX]; int status; /* Lasy allocation of the list of bindings specified by the * user. This list will be used by initialize_bindings(). */ if (tracee->fs->bindings.pending == NULL) { tracee->fs->bindings.pending = talloc_zero(tracee->fs, Bindings); if (tracee->fs->bindings.pending == NULL) return NULL; CIRCLEQ_INIT(tracee->fs->bindings.pending); talloc_set_destructor(tracee->fs->bindings.pending, remove_bindings); } /* Allocate an empty binding. */ binding = talloc_zero(tracee->ctx, Binding); if (binding == NULL) return NULL; /* Expand environment variables like $HOME. */ if (host[0] == '$' && getenv(&host[1])) host = getenv(&host[1]); /* Canonicalize the host part of the binding, as expected by * get_binding(). */ status = realpath2(tracee->reconf.tracee, binding->host.path, host, true); if (status < 0) { if (must_exist) notice(tracee, WARNING, INTERNAL, "can't sanitize binding \"%s\": %s", host, strerror(-status)); goto error; } binding->host.length = strlen(binding->host.path); /* Symetric binding? */ guest = guest ?: host; /* When not absolute, assume the guest path is relative to the * current working directory, as with ``-b .`` for instance. */ if (guest[0] != '/') { status = getcwd2(tracee->reconf.tracee, base); if (status < 0) { notice(tracee, WARNING, INTERNAL, "can't sanitize binding \"%s\": %s", binding->guest.path, strerror(-status)); goto error; } } else strcpy(base, "/"); status = join_paths(2, binding->guest.path, base, guest); if (status < 0) { notice(tracee, WARNING, SYSTEM, "can't sanitize binding \"%s\"", binding->guest.path); goto error; } binding->guest.length = strlen(binding->guest.path); /* Keep the list of bindings specified by the user ordered, * for the sake of consistency. For instance binding to "/" * has to be the last in the list. */ insort_binding(tracee, PENDING, binding); return binding; error: TALLOC_FREE(binding); return NULL; } /** * Canonicalize the guest part of the given @binding, insert it into * @tracee->fs->bindings.guest and @tracee->fs->bindings.host, then * remove it from @tracee->fs->bindings.pending. This function * returns -1 if an error occured, 0 otherwise. */ static void initialize_binding(Tracee *tracee, Binding *binding) { char path[PATH_MAX]; struct stat statl; int status; /* All bindings but "/" must be canonicalized. The exception * for "/" is required to bootstrap the canonicalization. */ if (compare_paths(binding->guest.path, "/") != PATHS_ARE_EQUAL) { bool dereference; size_t length; strcpy(path, binding->guest.path); length = strlen(path); assert(length > 0); /* Do the user explicitly tell not to dereference * guest path? */ dereference = (path[length - 1] != '!'); if (!dereference) path[length - 1] = '\0'; /* Initial state before canonicalization. */ strcpy(binding->guest.path, "/"); /* Remember the type of the final component, it will * be used in build_glue() later. */ status = lstat(binding->host.path, &statl); tracee->glue_type = (status < 0 || S_ISBLK(statl.st_mode) || S_ISCHR(statl.st_mode) ? S_IFREG : statl.st_mode & S_IFMT); /* Sanitize the guest path of the binding within the alternate rootfs since it is assumed by substitute_binding(). */ status = canonicalize(tracee, path, dereference, binding->guest.path, 0); if (status < 0) { notice(tracee, WARNING, INTERNAL, "sanitizing the guest path (binding) \"%s\": %s", path, strerror(-status)); return; } /* Remove the trailing "/" or "/." as expected by * substitute_binding(). */ chop_finality(binding->guest.path); /* Disable definitively the creation of the glue for * this binding. */ tracee->glue_type = 0; } binding->guest.length = strlen(binding->guest.path); insort_binding2(tracee, binding); } /** * Add bindings induced by @new_binding when @tracee is being sub-reconfigured. * For example, if the previous configuration ("-r /rootfs1") contains this * binding: * * -b /home/ced:/usr/local/ced * * and if the current configuration ("-r /rootfs2") introduces such a new * binding: * * -b /usr:/media * * then the following binding is induced: * * -b /home/ced:/media/local/ced */ static void add_induced_bindings(Tracee *tracee, const Binding *new_binding) { Binding *old_binding; char path[PATH_MAX]; int status; /* Only for reconfiguration. */ if (tracee->reconf.tracee == NULL) return; /* From the example, PRoot has already converted "-b /usr:/media" into * "-b /rootfs1/usr:/media" in order to ensure the host part is really a * host path. Here, the host part is converted back to "/usr" since the * comparison can't be made on "/rootfs1/usr". */ strcpy(path, new_binding->host.path); status = detranslate_path(tracee->reconf.tracee, path, NULL); if (status < 0) return; CIRCLEQ_FOREACH_(tracee->reconf.tracee, old_binding, GUEST) { Binding *induced_binding; Comparison comparison; char path2[PATH_MAX]; size_t prefix_length; /* Check if there's an induced binding by searching a common * path prefix in between new/old bindings: * * -b /home/ced:[/usr]/local/ced * -b [/usr]:/media */ comparison = compare_paths(path, old_binding->guest.path); if (comparison != PATH1_IS_PREFIX) continue; /* Convert the path of this induced binding to the new * filesystem namespace. From the example, "/usr/local/ced" is * converted into "/media/local/ced". Note: substitute_binding * can't be used in this case since it would expect * "/rootfs1/usr/local/ced instead". */ prefix_length = strlen(path); if (prefix_length == 1) prefix_length = 0; status = join_paths(2, path2, new_binding->guest.path, old_binding->guest.path + prefix_length); if (status < 0) continue; /* Install the induced binding. From the example: * * -b /home/ced:/media/local/ced */ induced_binding = talloc_zero(tracee->ctx, Binding); if (induced_binding == NULL) continue; strcpy(induced_binding->host.path, old_binding->host.path); strcpy(induced_binding->guest.path, path2); induced_binding->host.length = strlen(induced_binding->host.path); induced_binding->guest.length = strlen(induced_binding->guest.path); VERBOSE(tracee, 2, "induced binding: %s:%s (old) & %s:%s (new) -> %s:%s (induced)", old_binding->host.path, old_binding->guest.path, path, new_binding->guest.path, induced_binding->host.path, induced_binding->guest.path); insort_binding2(tracee, induced_binding); } } /** * Allocate @tracee->fs->bindings.guest and * @tracee->fs->bindings.host, then call initialize_binding() on each * binding listed in @tracee->fs->bindings.pending. */ int initialize_bindings(Tracee *tracee) { Binding *binding; /* Sanity checks. */ assert(tracee->fs->bindings.pending != NULL); assert(tracee->fs->bindings.guest == NULL); assert(tracee->fs->bindings.host == NULL); /* Allocate @tracee->fs->bindings.guest and * @tracee->fs->bindings.host. */ tracee->fs->bindings.guest = talloc_zero(tracee->fs, Bindings); tracee->fs->bindings.host = talloc_zero(tracee->fs, Bindings); if (tracee->fs->bindings.guest == NULL || tracee->fs->bindings.host == NULL) { notice(tracee, ERROR, INTERNAL, "can't allocate enough memory"); TALLOC_FREE(tracee->fs->bindings.guest); TALLOC_FREE(tracee->fs->bindings.host); return -1; } CIRCLEQ_INIT(tracee->fs->bindings.guest); CIRCLEQ_INIT(tracee->fs->bindings.host); talloc_set_destructor(tracee->fs->bindings.guest, remove_bindings); talloc_set_destructor(tracee->fs->bindings.host, remove_bindings); /* The binding to "/" has to be installed before other * bindings since this former is required to canonicalize * these latters. */ binding = CIRCLEQ_LAST(tracee->fs->bindings.pending); assert(compare_paths(binding->guest.path, "/") == PATHS_ARE_EQUAL); /* Call initialize_binding() on each pending binding in * reverse order: the last binding "/" is used to bootstrap * the canonicalization. */ while (binding != (void *) tracee->fs->bindings.pending) { Binding *previous; previous = CIRCLEQ_PREV(binding, link.pending); /* Canonicalize then insert this binding into * tracee->fs->bindings.guest/host. */ initialize_binding(tracee, binding); /* Add induced bindings on sub-reconfiguration. */ add_induced_bindings(tracee, binding); binding = previous; } TALLOC_FREE(tracee->fs->bindings.pending); if (tracee->verbose > 0) print_bindings(tracee); return 0; } proot-3.0.2/src/path/proc.c0000644000175000017500000001253512156552156015103 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* snprintf(3), */ #include /* strcmp(3), */ #include /* atoi(3), strtol(3), */ #include /* E*, */ #include /* assert(3), */ #include "path/proc.h" #include "tracee/tracee.h" #include "path/path.h" /** * This function emulates the @result of readlink("@base/@component") * with respect to @tracee, where @base belongs to "/proc" (according * to @comparison). This function returns -errno on error, an enum * @action otherwise (c.f. above). * * Unlike readlink(), this function includes the nul terminating byte * to @result. */ Action readlink_proc(const Tracee *tracee, char result[PATH_MAX], const char base[PATH_MAX], const char component[NAME_MAX], Comparison comparison) { const Tracee *known_tracee; char proc_path[64]; /* 64 > sizeof("/proc//fd/") + 2 * sizeof(#ULONG_MAX) */ int status; pid_t pid; assert(comparison == compare_paths("/proc", base)); /* Remember: comparison = compare_paths("/proc", base) */ switch (comparison) { case PATHS_ARE_EQUAL: /* Substitute "/proc/self" with "/proc/". */ if (strcmp(component, "self") != 0) return DEFAULT; status = snprintf(result, PATH_MAX, "/proc/%d", tracee->pid); if (status < 0 || status >= PATH_MAX) return -EPERM; return CANONICALIZE; case PATH1_IS_PREFIX: /* Handle "/proc/" below, where is process * monitored by PRoot. */ break; default: return DEFAULT; } pid = atoi(base + strlen("/proc/")); if (pid == 0) return DEFAULT; known_tracee = get_tracee(tracee, pid, false); if (!known_tracee) return DEFAULT; /* Handle links in "/proc//". */ status = snprintf(proc_path, sizeof(proc_path), "/proc/%d", pid); if (status < 0 || (size_t) status >= sizeof(proc_path)) return -EPERM; comparison = compare_paths(proc_path, base); switch (comparison) { case PATHS_ARE_EQUAL: #define SUBSTITUTE(name, field) \ do { \ if (strcmp(component, #name) != 0) \ break; \ \ status = strlen(known_tracee->field); \ if (status >= PATH_MAX) \ return -EPERM; \ \ strncpy(result, known_tracee->field, status + 1); \ return CANONICALIZE; \ } while (0) /* Substitute link "/proc//???" with the content * of tracee->???. */ SUBSTITUTE(exe, exe); SUBSTITUTE(cwd, fs->cwd); //SUBSTITUTE(root); #undef SUBSTITUTE return DEFAULT; case PATH1_IS_PREFIX: /* Handle "/proc//???" below. */ break; default: return DEFAULT; } /* Handle links in "/proc//fd/". */ status = snprintf(proc_path, sizeof(proc_path), "/proc/%d/fd", pid); if (status < 0 || (size_t) status >= sizeof(proc_path)) return -EPERM; comparison = compare_paths(proc_path, base); switch (comparison) { char *end_ptr; case PATHS_ARE_EQUAL: /* Sanity check: a number is expected. */ errno = 0; (void) strtol(component, &end_ptr, 10); if (errno != 0 || end_ptr == component) return -EPERM; /* Don't dereference "/proc//fd/???" now: they * can point to anonymous pipe, socket, ... otherwise * they point to a path already canonicalized by the * kernel. * * Note they are still correctly detranslated in * syscall/exit.c if a monitored process uses * readlink() against any of them. */ status = snprintf(result, PATH_MAX, "%s/%s", base, component); if (status < 0 || status >= PATH_MAX) return -EPERM; return DONT_CANONICALIZE; default: return DEFAULT; } return DEFAULT; } /** * This function emulates the @result of readlink("@referer") with * respect to @tracee, where @referer is a strict subpath of "/proc". * This function returns the length of @result if the readlink was * emulated, 0 otherwise. * * Unlike readlink(), this function includes the nul terminating byte * to @result (but this byte is not counted in the returned value). */ size_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char referer[PATH_MAX]) { Action action; char base[PATH_MAX]; char *component; assert(compare_paths("/proc", referer) == PATH1_IS_PREFIX); /* It's safe to use strrchr() here since @referer was * previously canonicalized. */ strcpy(base, referer); component = strrchr(base, '/'); /* These cases are not possible: @referer is supposed to be a * canonicalized subpath of "/proc". */ assert(component != NULL && component != base); component[0] = '\0'; component++; if (component[0] == '\0') return 0; action = readlink_proc(tracee, result, base, component, PATH1_IS_PREFIX); return (action == CANONICALIZE ? strlen(result) : 0); } proot-3.0.2/src/path/glue.h0000644000175000017500000000211512156552156015072 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef GLUE_H #define GLUE_H #include /* PATH_MAX, */ #include "tracee/tracee.h" #include "path.h" extern mode_t build_glue(Tracee *tracee, const char *guest_path, char host_path[PATH_MAX], Finality is_final); #endif /* GLUE_H */ proot-3.0.2/src/GNUmakefile0000644000175000017500000000735112156552156015112 0ustar ivoireivoire# If you want to build outside of the source tree, use the -f option: # make -f ${SOMEWHERE}/proot/src/GNUmakefile # the VPATH variable must points to the actual makefile directory VPATH := $(dir $(lastword $(MAKEFILE_LIST))) GIT = git RM = rm INSTALL = install CC = gcc LD = $(CC) CPPFLAGS = -I. -I$(VPATH) CFLAGS = -Wall -Wextra -O2 LDFLAGS = -ltalloc OBJECTS = cli.o \ notice.o \ execve/execve.o \ execve/interp.o \ execve/elf.o \ execve/ldso.o \ path/binding.o \ path/glue.o \ path/canon.o \ path/path.o \ path/proc.o \ syscall/seccomp.o \ syscall/syscall.o \ syscall/socket.o \ tracee/tracee.o \ tracee/array.o \ tracee/mem.o \ tracee/reg.o \ tracee/event.o \ extension/extension.o \ extension/kompat/kompat.o \ extension/fake_id0/fake_id0.o .DEFAULT_GOAL = proot all: proot ###################################################################### # Beautified output quiet_GEN = @echo " GEN $@"; $(GEN) quiet_CC = @echo " CC $@"; $(CC) quiet_LD = @echo " LD $@"; $(LD) quiet_INSTALL = @echo " INSTALL $?"; $(INSTALL) V = 0 ifeq ($(V), 0) quiet = quiet_ Q = @ silently = >/dev/null 2>&1 else quiet = Q = silently = endif ###################################################################### # Auto-configuration CHECK_VERSION = VERSION=$$($(GIT) describe --tags --dirty --abbrev=8 --always 2>/dev/null); \ if [ ! -z "$${VERSION}" ]; \ then /bin/echo -e "\#undef VERSION\n\#define VERSION \"$${VERSION}\""; \ fi; CHECK_FEATURES = readlinkat process_vm seccomp_filter CHECK_PROGRAMS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature)) CHECK_OBJECTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).o) .SILENT .IGNORE .INTERMEDIATE: $(CHECK_OBJECTS) .check_%.o: .check_%.c $(COMPILE:echo=false) $(silently) || true .check_%: .check_%.o $(LINK:echo=false) $(silently) || true # TODO: generate these lines automatically from $(CHECK_FEATURES) CHECK_READLINKAT = if [ -e .check_readlinkat ]; then echo "\#define HAVE_READLINKAT"; fi CHECK_PROCESS_VM = if [ -e .check_process_vm ]; then echo "\#define HAVE_PROCESS_VM"; fi CHECK_SECCOMP_FILTER = if [ -e .check_seccomp_filter ]; then echo "\#define HAVE_SECCOMP_FILTER"; fi build.h: $(CHECK_PROGRAMS) $($(quiet)GEN) $(Q)echo "/* This file is auto-generated, edit at your own risk. */" > $@ $(Q)echo "#ifndef BUILD_H" >> $@ $(Q)echo "#define BUILD_H" >> $@ $(Q)sh -c '$(CHECK_VERSION)' >> $@ $(Q)sh -c '$(CHECK_READLINKAT)' >> $@ $(Q)sh -c '$(CHECK_PROCESS_VM)' >> $@ $(Q)sh -c '$(CHECK_SECCOMP_FILTER)' >> $@ $(Q)echo "#endif /* BUILD_H */" >> $@ ###################################################################### # Build rules SRC = $(dir $(firstword $(MAKEFILE_LIST))) COMPILE = $($(quiet)CC) $(CPPFLAGS) $(CFLAGS) -MD -c $(SRC)$(@:.o=.c) -o $@ LINK = $($(quiet)LD) -o $@ $^ $(LDFLAGS) proot: $(OBJECTS) $(LINK) # Special case to compute which files depend on the auto-generated # file "build.h". USE_BUILD_H := $(patsubst $(SRC)%,%,$(patsubst %.c,%.o,$(shell egrep -l 'include[[:space:]]+"build.h"' $(SRC)*.c $(SRC)*/*.c))) $(USE_BUILD_H): build.h %.o: %.c @mkdir -p $(dir $@) $(COMPILE) ###################################################################### # Dependencies .DELETE_ON_ERROR: $(OBJECTS) build.h: $(firstword $(MAKEFILE_LIST)) DEPS = $(OBJECTS:.o=.d) $(CHECK_OBJECTS:.o=.d) -include $(DEPS) ###################################################################### # PHONY targets PREFIX = /usr/local DESTDIR = $(PREFIX)/bin .PHONY: clean distclean install uninstall clean distclean: -$(RM) -f $(CHECK_PROGRAMS) $(CHECK_OBJECTS) $(OBJECTS) proot $(DEPS) build.h install: proot $($(quiet)INSTALL) -D $< $(DESTDIR)/$< uninstall: -$(RM) -f $(DESTDIR)/proot proot-3.0.2/src/execve/0000755000175000017500000000000012156552156014311 5ustar ivoireivoireproot-3.0.2/src/execve/interp.h0000644000175000017500000000254612156552156015772 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef INTERP_H #define INTERP_H #include /* PATH_MAX, ARG_MAX, */ #include "tracee/tracee.h" typedef int (* extract_interp_t)(const Tracee *tracee, const char *t_path, char u_interp[PATH_MAX], char argument[ARG_MAX]); extern int extract_script_interp(const Tracee *tracee, const char *t_path, char u_interp[PATH_MAX], char argument[ARG_MAX]); extern int extract_elf_interp(const Tracee *tracee, const char *t_path, char u_interp[PATH_MAX], char argument[ARG_MAX]); #endif /* INTERP_H */ proot-3.0.2/src/execve/interp.c0000644000175000017500000001342412156552156015762 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _XOPEN_SOURCE 500 /* pread(2), */ #include /* open(2), */ #include /* read(2), close(2), */ #include /* PATH_MAX, ARG_MAX, */ #include /* ENAMETOOLONG, */ #include /* strcpy(3), */ #include "execve/interp.h" #include "execve/elf.h" #include "attribute.h" #include "compat.h" /** * Extract the shebang of @t_path in @u_interp and @arg_max. This * function returns -errno if an error occured, 1 if a shebang was * found and extracted, otherwise 0. * * Extract from "man 2 execve": * * On Linux, the entire string following the interpreter name is * passed as a *single* argument to the interpreter, and this * string can include white space. */ int extract_script_interp(const Tracee *tracee UNUSED, const char *t_path, char u_interp[PATH_MAX], char argument[ARG_MAX]) { char tmp; int status; int fd; int i; argument[0] = '\0'; /* Inspect the executable. */ fd = open(t_path, O_RDONLY); if (fd < 0) return -errno; status = read(fd, u_interp, 2 * sizeof(char)); if (status < 0) { status = -errno; goto end; } if ((size_t) status < 2 * sizeof(char)) { /* EOF */ status = 0; goto end; } /* Check if it really is a script text. */ if (u_interp[0] != '#' || u_interp[1] != '!') { status = 0; goto end; } /* Skip leading spaces. */ do { status = read(fd, &tmp, sizeof(char)); if (status < 0) { status = -errno; goto end; } if ((size_t) status < sizeof(char)) { /* EOF */ status = 0; goto end; } } while (tmp == ' ' || tmp == '\t'); /* Slurp the interpreter path until the first space or end-of-line. */ for (i = 0; i < PATH_MAX; i++) { switch (tmp) { case ' ': case '\t': /* Remove spaces in between the interpreter * and the hypothetical argument. */ u_interp[i] = '\0'; break; case '\n': case '\r': /* There is no argument. */ u_interp[i] = '\0'; argument[0] = '\0'; status = 1; goto end; default: /* There is an argument if the previous * character in u_interp[] is '\0'. */ if (i > 1 && u_interp[i - 1] == '\0') goto argument; else u_interp[i] = tmp; break; } status = read(fd, &tmp, sizeof(char)); if (status < 0) { status = -errno; goto end; } if ((size_t) status < sizeof(char)) { /* EOF */ u_interp[i] = '\0'; argument[0] = '\0'; status = 1; goto end; } } /* The interpreter path is too long. */ status = -ENAMETOOLONG; goto end; argument: /* Slurp the argument until the end-of-line. */ for (i = 0; i < ARG_MAX; i++) { switch (tmp) { case '\n': case '\r': argument[i] = '\0'; /* Remove trailing spaces. */ for (i--; i > 0 && (argument[i] == ' ' || argument[i] == '\t'); i--) argument[i] = '\0'; status = 1; goto end; default: argument[i] = tmp; break; } status = read(fd, &tmp, sizeof(char)); if (status < 0) { status = -errno; goto end; } if ((size_t) status < sizeof(char)) { /* EOF */ argument[0] = '\0'; status = 1; goto end; } } /* The argument is too long, just ignore it. */ argument[0] = '\0'; end: close(fd); /* Did an error occur or isn't a script? */ if (status <= 0) return status; return 1; } /** * Extract the ELF interpreter of @path in @u_interp. This function * returns -errno if an error occured, 1 if a ELF interpreter was * found and extracted, otherwise 0. */ int extract_elf_interp(const Tracee *tracee, const char *t_path, char u_interp[PATH_MAX], char argument[ARG_MAX]) { ElfHeader elf_header; ProgramHeader program_header; size_t extra_size; int status; int fd; uint64_t segment_offset; uint64_t segment_size; u_interp[0] = '\0'; argument[0] = '\0'; fd = open_elf(t_path, &elf_header); if (fd < 0) return fd; status = find_program_header(tracee, fd, &elf_header, &program_header, PT_INTERP, (uint64_t) -1); if (status <= 0) goto end; segment_offset = PROGRAM_FIELD(elf_header, program_header, offset); segment_size = PROGRAM_FIELD(elf_header, program_header, filesz); /* If we are executing a host binary under a QEMUlated * environment, we have to access its ELF interpreter through * the "host-rootfs" binding. Technically it means the host * ELF interpreter "/lib/ld-linux.so.2" is accessed as * "${HOST_ROOTFS}/lib/ld-linux.so.2" to avoid conflict with * the guest "/lib/ld-linux.so.2". */ if (tracee->qemu != NULL) { strcpy(u_interp, HOST_ROOTFS); extra_size = strlen(HOST_ROOTFS); } else extra_size = 0; if (segment_size + extra_size >= PATH_MAX) { status = -EACCES; goto end; } status = pread(fd, u_interp + extra_size, segment_size, segment_offset); if (status < 0) goto end; if ((size_t) status != segment_size) { /* Unexpected size. */ status = -EACCES; goto end; } u_interp[segment_size + extra_size] = '\0'; end: close(fd); /* Delayed error handling */ if (status < 0) return status; /* Is there an INTERP entry? */ if (u_interp[0] == '\0') return 0; else return 1; } proot-3.0.2/src/execve/ldso.h0000644000175000017500000000232512156552156015425 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef LDSO_H #define LDSO_H #include #include "tracee/array.h" extern int ldso_env_passthru(Array *envp, Array *argv, const char *define, const char *undefine); extern int rebuild_host_ldso_paths(const Tracee *tracee, const char t_program[PATH_MAX], Array *envp); extern int compare_item_env(Array *array, size_t index, const char *name); #endif /* LDSO_H */ proot-3.0.2/src/execve/ldso.c0000644000175000017500000002157412156552156015427 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* bool, true, false, */ #include /* strlen(3), strcpy(3), strcat(3), strcmp(3), */ #include /* getenv(3), */ #include /* assert(3), */ #include /* ENOMEM, */ #include /* close(2), */ #include /* PATH_MAX, ARG_MAX, */ #include "execve/ldso.h" #include "execve/execve.h" #include "execve/elf.h" #include "tracee/tracee.h" #include "tracee/array.h" #include "notice.h" /** * Check if the environment @variable has the given @name. */ static inline bool is_env_name(const char *variable, const char *name) { size_t length = strlen(name); return (variable[0] == name[0] && length < strlen(variable) && variable[length] == '=' && strncmp(variable, name, length) == 0); } /** * This function returns 1 or 0 depending on the equivalence of the * @reference environment variable and the one pointed to by the entry * in @array at the given @index, otherwise it returns -errno when an * error occured. */ int compare_item_env(Array *array, size_t index, const char *reference) { char *value; int status; assert(index < array->length); status = read_item_string(array, index, &value); if (status < 0) return status; if (value == NULL) return 0; return (int)is_env_name(value, reference); } /** * This function ensures that environment variables related to the * dynamic linker are applied to the emulated program, not to QEMU * itself. For instance, let's say the user has entered the * command-line below: * * env LD_TRACE_LOADED_OBJECTS=1 /bin/ls * * It should be converted to: * * qemu -E LD_TRACE_LOADED_OBJECTS=1 /bin/ls * * instead of: * * env LD_TRACE_LOADED_OBJECTS=1 qemu /bin/ls * * Note that the LD_LIBRARY_PATH variable is always required to run * QEMU (a host binary): * * env LD_LIBRARY_PATH=... qemu -U LD_LIBRARY_PATH /bin/ls * * or when LD_LIBRARY_PATH was also specified by the user: * * env LD_LIBRARY_PATH=... qemu -E LD_LIBRARY_PATH=... /bin/ls * * This funtion returns -errno if an error occured, otherwise 0. */ int ldso_env_passthru(Array *envp, Array *argv, const char *define, const char *undefine) { bool has_seen_library_path = false; int status; size_t i; for (i = 0; i < envp->length; i++) { bool is_known = false; char *env; status = read_item_string(envp, i, &env); if (status < 0) return status; /* Skip variables that do not start with "LD_". */ if (env == NULL || strncmp(env, "LD_", sizeof("LD_") - 1) != 0) continue; #define PASSTHRU(check, name) \ if (is_env_name(env, name)) { \ check |= true; \ /* Errors are not fatal here. */ \ status = resize_array(argv, 1, 2); \ if (status >= 0) { \ status = write_items(argv, 1, 2, define, env); \ if (status < 0) \ return status; \ } \ write_item(envp, i, ""); \ continue; \ } \ PASSTHRU(has_seen_library_path, "LD_LIBRARY_PATH"); PASSTHRU(is_known, "LD_PRELOAD"); PASSTHRU(is_known, "LD_BIND_NOW"); PASSTHRU(is_known, "LD_TRACE_LOADED_OBJECTS"); PASSTHRU(is_known, "LD_AOUT_LIBRARY_PATH"); PASSTHRU(is_known, "LD_AOUT_PRELOAD"); PASSTHRU(is_known, "LD_AUDIT"); PASSTHRU(is_known, "LD_BIND_NOT"); PASSTHRU(is_known, "LD_DEBUG"); PASSTHRU(is_known, "LD_DEBUG_OUTPUT"); PASSTHRU(is_known, "LD_DYNAMIC_WEAK"); PASSTHRU(is_known, "LD_HWCAP_MASK"); PASSTHRU(is_known, "LD_KEEPDIR"); PASSTHRU(is_known, "LD_NOWARN"); PASSTHRU(is_known, "LD_ORIGIN_PATH"); PASSTHRU(is_known, "LD_POINTER_GUARD"); PASSTHRU(is_known, "LD_PROFILE"); PASSTHRU(is_known, "LD_PROFILE_OUTPUT"); PASSTHRU(is_known, "LD_SHOW_AUXV"); PASSTHRU(is_known, "LD_USE_LOAD_BIAS"); PASSTHRU(is_known, "LD_VERBOSE"); PASSTHRU(is_known, "LD_WARN"); } if (!has_seen_library_path) { /* Errors are not fatal here. */ status = resize_array(argv, 1, 2); if (status >= 0) { status = write_items(argv, 1, 2, undefine, "LD_LIBRARY_PATH"); if (status < 0) return status; } } return 0; } /** * Add to @host_ldso_paths the list of @paths prefixed with the path * to the host rootfs. */ static int add_host_ldso_paths(char host_ldso_paths[ARG_MAX], const char *paths) { char *cursor1; const char *cursor2; cursor1 = host_ldso_paths + strlen(host_ldso_paths); cursor2 = paths; do { bool is_absolute; size_t length1; size_t length2 = strcspn(cursor2, ":"); is_absolute = (*cursor2 == '/'); length1 = 1 + length2; if (is_absolute) length1 += strlen(HOST_ROOTFS); /* Check there's enough room. */ if (cursor1 + length1 >= host_ldso_paths + ARG_MAX) return -ENOEXEC; if (cursor1 != host_ldso_paths) { strcpy(cursor1, ":"); cursor1++; } /* Since we are executing a host binary under a * QEMUlated environment, we have to access its * library paths through the "host-rootfs" binding. * Technically it means a path like "/lib" is accessed * as "${HOST_ROOTFS}/lib" to avoid conflict with the * guest "/lib". */ if (is_absolute) { strcpy(cursor1, HOST_ROOTFS); cursor1 += strlen(HOST_ROOTFS); } strncpy(cursor1, cursor2, length2); cursor1 += length2; cursor2 += length2 + 1; } while (*(cursor2 - 1) != '\0'); *(cursor1++) = '\0'; return 0; } /** * Rebuild the variable LD_LIBRARY_PATH in @envp for @t_program * according to its RPATH, RUNPATH, and the initial LD_LIBRARY_PATH. * This function returns -errno if an error occured, 1 if * RPATH/RUNPATH entries were found, 0 otherwise. */ int rebuild_host_ldso_paths(const Tracee *tracee, const char t_program[PATH_MAX], Array *envp) { static char *initial_ldso_paths = NULL; ElfHeader elf_header; char host_ldso_paths[ARG_MAX] = ""; bool inhibit_rpath = false; char *rpaths = NULL; char *runpaths = NULL; size_t length1; size_t length2; size_t index; int status; int fd; fd = open_elf(t_program, &elf_header); if (fd < 0) return fd; status = read_ldso_rpaths(tracee, fd, &elf_header, &rpaths, &runpaths); close(fd); if (status < 0) return status; /* 1. DT_RPATH */ if (rpaths && !runpaths) { status = add_host_ldso_paths(host_ldso_paths, rpaths); if (status < 0) return 0; /* Not fatal. */ inhibit_rpath = true; } /* 2. LD_LIBRARY_PATH */ if (initial_ldso_paths == NULL) initial_ldso_paths = strdup(getenv("LD_LIBRARY_PATH") ?: "/"); if (initial_ldso_paths != NULL && initial_ldso_paths[0] != '\0') { status = add_host_ldso_paths(host_ldso_paths, initial_ldso_paths); if (status < 0) return 0; /* Not fatal. */ } /* 3. DT_RUNPATH */ if (runpaths) { status = add_host_ldso_paths(host_ldso_paths, runpaths); if (status < 0) return 0; /* Not fatal. */ inhibit_rpath = true; } /* 4. /etc/ld.so.cache NYI. */ /* 5. /lib[32|64], /usr/lib[32|64] + /usr/local/lib[32|64] */ /* 6. /lib, /usr/lib + /usr/local/lib */ if (IS_CLASS32(elf_header)) status = add_host_ldso_paths(host_ldso_paths, #if defined(ARCH_X86) || defined(ARCH_X86_64) "/lib/i386-linux-gnu:/usr/lib/i386-linux-gnu:" #endif "/lib32:/usr/lib32:/usr/local/lib32" ":/lib:/usr/lib:/usr/local/lib"); else status = add_host_ldso_paths(host_ldso_paths, #if defined(ARCH_X86_64) "/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:" #endif "/lib64:/usr/lib64:/usr/local/lib64" ":/lib:/usr/lib:/usr/local/lib"); if (status < 0) return 0; /* Not fatal. */ status = find_item(envp, "LD_LIBRARY_PATH"); if (status < 0) return 0; /* Not fatal. */ index = (size_t) status; /* Allocate a new entry at the end of envp[] if * LD_LIBRARY_PATH was not found. */ if (index == envp->length) { index = (envp->length > 0 ? envp->length - 1 : 0); status = resize_array(envp, index, 1); if (status < 0) return 0; /* Not fatal. */ } /* Forge the new LD_LIBRARY_PATH variable from * host_ldso_paths. */ length1 = strlen("LD_LIBRARY_PATH="); length2 = strlen(host_ldso_paths); if (ARG_MAX - length2 - 1 < length1) return 0; /* Not fatal. */ memmove(host_ldso_paths + length1, host_ldso_paths, length2 + 1); memcpy(host_ldso_paths, "LD_LIBRARY_PATH=" , length1); write_item(envp, index, host_ldso_paths); return (int) inhibit_rpath; } proot-3.0.2/src/execve/execve.c0000644000175000017500000003514012156552156015737 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* lstat(2), S_ISREG(), */ #include /* access(2), lstat(2), */ #include /* string(3), */ #include /* assert(3), */ #include /* E*, */ #include /* talloc_, */ #include "execve/execve.h" #include "execve/interp.h" #include "execve/elf.h" #include "execve/ldso.h" #include "tracee/array.h" #include "tracee/tracee.h" #include "syscall/syscall.h" #include "notice.h" #include "path/path.h" #include "path/binding.h" #include "extension/extension.h" #include "compat.h" /** * Translate @u_path into @t_path and check if this latter exists, is * executable and is a regular file. This function returns -errno if * an error occured, 0 otherwise. */ static int translate_n_check(Tracee *tracee, char t_path[PATH_MAX], const char *u_path) { struct stat statl; int status; status = translate_path(tracee, t_path, AT_FDCWD, u_path, true); if (status < 0) return status; status = access(t_path, F_OK); if (status < 0) return -ENOENT; status = access(t_path, X_OK); if (status < 0) return -EACCES; status = lstat(t_path, &statl); if (status < 0) return -EPERM; return 0; } /** * Substitute *@argv[0] with the interpreter (and its argument) of the * program pointed to by @u_path. The paths to the interpreter (or to * the program itself if it doesn't use an interpreter) are stored in * @t_interp and @u_interp (respectively translated and untranslated). */ static int expand_interp(Tracee *tracee, const char *u_path, char t_interp[PATH_MAX], char u_interp[PATH_MAX], Array *argv, extract_interp_t callback, bool ignore_interpreter) { char argument[ARG_MAX]; char dummy_path[PATH_MAX]; char dummy_arg[ARG_MAX]; int status; status = translate_n_check(tracee, t_interp, u_path); if (status < 0) return status; /* Skip the extraction of the interpreter on demand, in * this case we execute the translation of u_path (t_interp) * directly. */ if (ignore_interpreter) { strcpy(u_interp, u_path); return 0; } /* Extract the interpreter of t_interp in u_interp + argument. */ status = callback(tracee, t_interp, u_interp, argument); if (status < 0) return status; /* No interpreter was found, in this case we execute the * translation of u_path (t_interp) directly. */ if (status == 0) { strcpy(u_interp, u_path); return 0; } status = translate_n_check(tracee, t_interp, u_interp); if (status < 0) return status; /* Sanity check: an interpreter doesn't request an[other] * interpreter on Linux. In the case of ELF this check * ensures the interpreter is in the ELF format. */ status = callback(tracee, t_interp, dummy_path, dummy_arg); if (status < 0) return status; if (status != 0) return -EPERM; /* * Substitute argv[0] with the ELF/script interpreter: * * execve("/bin/sh", { "/bin/sh", "/test.sh", NULL }, { NULL }); * * becomes: * * execve("/lib/ld.so", { "/lib/ld.so", "/bin/sh", "/test.sh", NULL }, { NULL }); * * Note: actually the first parameter of execve() is changed * in the caller because it depends on the use of a runner. */ VERBOSE(tracee, 3, "expand shebang: -> %s %s %s", u_interp, argument, u_path); if (argument[0] != '\0') { status = resize_array(argv, 0, 2); if (status < 0) return status; status = write_items(argv, 0, 3, u_interp, argument, u_path); if (status < 0) return status; } else { status = resize_array(argv, 0, 1); if (status < 0) return status; status = write_items(argv, 0, 2, u_interp, u_path); if (status < 0) return status; } /* Remember at this point t_interp is the translation of * u_interp. */ return 1; } /** * Check if the binary @host_path is PRoot itself. In that case, @argv and * @envp are used to reconfigure the current @tracee. This function returns * -errno if an error occured, 0 if the binary isn't PRoot, and 1 if @tracee was * reconfigured correctly. */ static int handle_sub_reconf(Tracee *tracee, Array *argv, const char *host_path) { static char *self_exe = NULL; Tracee *dummy = NULL; char path[PATH_MAX]; char **argv_pod; int status; size_t i; /* The path to PRoot itself is cached. */ if (self_exe == NULL) { status = readlink("/proc/self/exe", path, PATH_MAX); if (status < 0 || status >= PATH_MAX) return 0; path[status] = '\0'; self_exe = strdup(path); if (self_exe == NULL) return -ENOMEM; } /* Check if the executed program is PRoot itself. */ if (strcmp(host_path, self_exe) != 0 || argv->length <= 1) return 0; /* Rebuild a POD argv[], as expected by parse_config(). */ argv_pod = talloc_size(tracee->ctx, argv->length * sizeof(char **)); if (argv_pod == NULL) return -ENOMEM; for (i = 0; i < argv->length; i++) { status = read_item_string(argv, i, &argv_pod[i]); if (status < 0) return status; } /* This dummy tracee holds the new configuration that will be copied * back to the original tracee if everything is OK. */ dummy = talloc_zero(tracee->ctx, Tracee); if (dummy == NULL) return -ENOMEM; dummy->fs = talloc_zero(dummy, FileSystemNameSpace); if (dummy->fs == NULL) return -ENOMEM; dummy->ctx = talloc_new(dummy); if (dummy->ctx == NULL) return -ENOMEM; /* Inform parse_config() that paths are relative to the current tracee. * For instance, "-w ./foo" will be translated to "-w * ${tracee->cwd}/foo". */ dummy->reconf.tracee = tracee; dummy->reconf.paths = NULL; status = parse_config(dummy, argv->length - 1, argv_pod); if (status < 0) return -ECANCELED; bzero(&dummy->reconf, sizeof(dummy->reconf)); /* How many arguments for the actual command? */ for (i = 0; dummy->cmdline[i] != NULL; i++) ; /* Sanity checks. */ if (i < 1 || i >= argv->length) { notice(tracee, WARNING, INTERNAL, "wrong number of arguments (%zd)", i); return -ECANCELED; } /* Write the actual command back to the tracee's memory. */ status = resize_array(argv, argv->length - 1, i + 1 - argv->length); if (status < 0) return status; for (i = 0; dummy->cmdline[i] != NULL; i++) write_item_string(argv, i, dummy->cmdline[i]); write_item_string(argv, i, NULL); status = push_array(argv, SYSARG_2); if (status < 0) return status; status = set_sysarg_path(tracee, dummy->exe, SYSARG_1); if (status < 0) return status; /* Commit the new configuration. */ status = swap_config(tracee, dummy); if (status < 0) return status; /* Restart the execve() but with the actual command. */ status = translate_execve(tracee); if (status < 0) { /* Something went wrong, revert the new configuration. Maybe * this should be done in syscall/exit.c. */ (void) swap_config(tracee, dummy); return status; } inherit_extensions(tracee, dummy, true); return 1; } /** * Translate the arguments of the execve() syscall made by the @tracee * process. This function return -errno if an error occured, * otherwise 0. * * The execve() syscall needs a very special treatment for script * files because according to "man 2 execve": * * An interpreter script is a text file [...] whose first line is * of the form: * * #! interpreter [optional-arg] * * The interpreter must be a valid pathname for an executable * which is not itself a script. If the filename argument of * execve() specifies an interpreter script, then interpreter will * be invoked with the following arguments: * * interpreter [optional-arg] filename arg... * * where arg... is the series of words pointed to by the argv * argument of execve(). * * Let's take the following example: * * execve("/bin/script.sh", argv = [ "script.sh", "arg1", arg2", ... ], envp); * * We can't just translate the first parameter because the kernel * will actually run the interpreter "/bin/sh" with the translated * path to the script file "/tmp/new_root/bin/script.sh" as its first * argument. Technically, we want the opposite behaviour, that is, we * want to run the translated path to the interpreter * "/tmp/new_root/bin/sh" with the de-translated path to the script * "/bin/script.sh" as its first parameter (will be translated later): * * execve("/tmp/new_root/bin/sh", argv = [ "/bin/sh", "/bin/script.sh", "arg1", arg2", ... ], envp); */ int translate_execve(Tracee *tracee) { char u_path[PATH_MAX]; char t_interp[PATH_MAX]; char u_interp[PATH_MAX]; Array *envp = NULL; Array *argv = NULL; char *argv0 = NULL; char **new_cmdline; char *new_exe; size_t i; bool ignore_elf_interpreter; bool inhibit_rpath = false; bool is_script; int status; status = get_sysarg_path(tracee, u_path, SYSARG_1); if (status < 0) return status; status = fetch_array(tracee, &argv, SYSARG_2, 0); if (status < 0) return status; if (tracee->qemu) { status = read_item_string(argv, 0, &argv0); if (status < 0) return status; /* Save the initial argv[0] since it will be replaced * by tracee->qemu[0]. Errors are not fatal here. */ if (argv0 != NULL) argv0 = talloc_strdup(tracee->ctx, argv0); status = fetch_array(tracee, &envp, SYSARG_3, 0); if (status < 0) return status; /* Environment variables should be compared with the * "name" part in the "name=value" string format. */ envp->compare_item = (compare_item_t)compare_item_env; } status = expand_interp(tracee, u_path, t_interp, u_interp, argv, extract_script_interp, false); if (status < 0) /* The Linux kernel actually returns -EACCES when * trying to execute a directory. */ return status == -EISDIR ? -EACCES : status; is_script = (status > 0); /* It's the rigth place to check if the binary is PRoot itself. */ status = handle_sub_reconf(tracee, argv, t_interp); if (status < 0) return status; if (status > 0) return 0; /* Remember the value for "/proc/self/exe". Note that it points to a * canonicalized guest path, hence detranslate_path(). We re-use u_path * since it is not useful anymore. */ strcpy(u_path, t_interp); (void) detranslate_path(tracee, u_path, NULL); new_exe = talloc_strdup(tracee->ctx, u_path); if (new_exe == NULL) return -ENOMEM; /* Remember the value for "/proc/self/cmdline". */ new_cmdline = talloc_zero_array(tracee->ctx, char *, argv->length); if (new_cmdline == NULL) return -ENOMEM; for (i = 0; i < argv->length; i++) { char *ptr; status = read_item_string(argv, i, &ptr); if (status < 0) return status; /* It's safe to reference these strings since they are never * overwritten, they are just replaced. */ new_cmdline[i] = talloc_reference(new_cmdline, ptr); } if (tracee->qemu != NULL) { /* Prepend the QEMU command to the initial argv[] if * it's a "foreign" binary. */ if (!is_host_elf(tracee, t_interp)) { int i; status = resize_array(argv, 0, 3); if (status < 0) return status; /* For example, the second argument of: * execve("/bin/true", { "true", NULL }, ...) * becomes: * { "/usr/bin/qemu", "-0", "true", "/bin/true"} */ status = write_items(argv, 0, 4, tracee->qemu[0], "-0", !is_script && argv0 != NULL ? argv0 : u_interp, u_interp); if (status < 0) return status; status = ldso_env_passthru(envp, argv, "-E", "-U"); if (status < 0) return status; /* Compute the number of QEMU's arguments and * add them to the modified argv[]. */ for (i = 1; tracee->qemu[i] != NULL; i++) ; status = resize_array(argv, 1, i - 1); if (status < 0) return status; for (i--; i > 0; i--) { status = write_item(argv, i, tracee->qemu[i]); if (status < 0) return status; } /* Launch the runner actually. */ strcpy(t_interp, tracee->qemu[0]); status = join_paths(2, u_interp, HOST_ROOTFS, tracee->qemu[0]); if (status < 0) return status; } /* Provide information to the host dynamic linker to * find host libraries (remember the guest root * file-system contains libraries for the guest * architecture only). */ status = rebuild_host_ldso_paths(tracee, t_interp, envp); if (status < 0) return status; inhibit_rpath = (status > 0); } /* Dont't use the ELF interpreter as a loader if the host one * is compatible (currently the test is only "guest rootfs == * host rootfs") or if there's no need for RPATH inhibition in * mixed-mode. */ ignore_elf_interpreter = (compare_paths(get_root(tracee), "/") == PATHS_ARE_EQUAL || (tracee->qemu_pie_workaround && !inhibit_rpath)); status = expand_interp(tracee, u_interp, t_interp, u_path /* dummy */, argv, extract_elf_interp, ignore_elf_interpreter); if (status < 0) return status; if (status > 0 && inhibit_rpath) { /* Tell the dynamic linker to ignore RPATHs specified * in the *main* program. To disable the RPATH * mechanism globally, we have to list all objects * here (NYI). Errors are not fatal here. */ status = resize_array(argv, 1, 2); if (status >= 0) { status = write_items(argv, 1, 2, "--inhibit-rpath", "''"); if (status < 0) return status; } } VERBOSE(tracee, 4, "execve: %s", t_interp); status = set_sysarg_path(tracee, t_interp, SYSARG_1); if (status < 0) return status; status = push_array(argv, SYSARG_2); if (status < 0) return status; status = push_array(envp, SYSARG_3); if (status < 0) return status; /* So far so good, we can now safely update tracee->exe and * tracee->cmdline. Actually it would be safer in syscall/exit.c * however I'm not able to write a test where execve(2) would fail at * kernel level but not in PRoot. Moreover this would require to store * temporarily the original values for exe and cmdline, that is, before * the insertion of the loader (ELF interpreter or QEMU). */ talloc_unlink(tracee, tracee->exe); tracee->exe = talloc_reference(tracee, new_exe); talloc_set_name_const(tracee->exe, "$exe"); talloc_unlink(tracee, tracee->cmdline); tracee->cmdline = talloc_reference(tracee, new_cmdline); talloc_set_name_const(tracee->cmdline, "@cmdline"); return 0; } proot-3.0.2/src/execve/execve.h0000644000175000017500000000176412156552156015751 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef EXECVE_H #define EXECVE_H #include "tracee/tracee.h" #include "tracee/array.h" extern int translate_execve(Tracee *tracee); #endif /* EXECVE_H */ proot-3.0.2/src/execve/elf.c0000644000175000017500000002333612156552156015232 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #define _GNU_SOURCE /* strnlen(3), */ #include /* open(2), */ #include /* read(2), close(2), */ #include /* EACCES, ENOTSUP, */ #include /* UINT64_MAX, */ #include /* PATH_MAX, */ #include /* strnlen(3), strcat(3), strcpy(3), */ #include /* assert(3), */ #include /* strnlen(3), */ #include /* talloc_*, */ #include /* bool, true, false, */ #include "execve/elf.h" #include "tracee/tracee.h" #include "notice.h" #include "arch.h" #include "compat.h" /** * Open the ELF file @t_path and extract its header into @elf_header. * This function returns -errno if an error occured, otherwise the * file descriptor for @t_path. */ int open_elf(const char *t_path, ElfHeader *elf_header) { int fd; int status; /* * Read the ELF header. */ fd = open(t_path, O_RDONLY); if (fd < 0) return -errno; /* Check if it is an ELF file. */ status = read(fd, elf_header, sizeof(ElfHeader)); if (status < 0) goto end; if ((size_t) status < sizeof(ElfHeader) || ELF_IDENT(*elf_header, 0) != 0x7f || ELF_IDENT(*elf_header, 1) != 'E' || ELF_IDENT(*elf_header, 2) != 'L' || ELF_IDENT(*elf_header, 3) != 'F') { status = -ENOEXEC; goto end; } /* Check if it is a known class (32-bit or 64-bit). */ if ( !IS_CLASS32(*elf_header) && !IS_CLASS64(*elf_header)) { status = -ENOEXEC; goto end; } status = 0; end: /* Delayed error handling. */ if (status < 0) { close(fd); return status; } return fd; } /** * Find in the file referenced by @fd -- which has the provided * @elf_header -- the first @program_header of a given @type and * loaded at the given @address (-1 for wherever). This function * returns -errno if an error occured, 1 if the program header was * found, otherwise 0. */ int find_program_header(const Tracee *tracee, int fd, const ElfHeader *elf_header, ProgramHeader *program_header, SegmentType type, uint64_t address) { uint64_t elf_phoff; uint16_t elf_phentsize; uint16_t elf_phnum; int status; int i; /* Get class-specific fields. */ elf_phnum = ELF_FIELD(*elf_header, phnum); elf_phentsize = ELF_FIELD(*elf_header, phentsize); elf_phoff = ELF_FIELD(*elf_header, phoff); /* * Some sanity checks regarding the current * support of the ELF specification in PRoot. */ if (elf_phnum >= 0xffff) { notice(tracee, WARNING, INTERNAL, "%d: big PH tables are not yet supported.", fd); return -ENOTSUP; } if (!KNOWN_PHENTSIZE(*elf_header, elf_phentsize)) { notice(tracee, WARNING, INTERNAL, "%d: unsupported size of program header.", fd); return -ENOTSUP; } /* * Search the first entry of the requested type into the * program header table. */ status = (int) lseek(fd, elf_phoff, SEEK_SET); if (status < 0) return -errno; for (i = 0; i < elf_phnum; i++) { status = read(fd, program_header, elf_phentsize); if (status != elf_phentsize) status = -errno; if (PROGRAM_FIELD(*elf_header, *program_header, type) == type) { uint64_t start; uint64_t end; if (address == (uint64_t) -1) return 1; start = PROGRAM_FIELD(*elf_header, *program_header, vaddr); end = start + PROGRAM_FIELD(*elf_header, *program_header, memsz); if (start < end && address >= start && address <= end) return 1; } } return 0; } /** * Check if @t_path is an ELF file for the host architecture. */ bool is_host_elf(const Tracee *tracee, const char *t_path) { int host_elf_machine[] = HOST_ELF_MACHINE; static int force_foreign = -1; ElfHeader elf_header; uint16_t elf_machine; int fd; int i; if (force_foreign < 0) force_foreign = (getenv("PROOT_FORCE_FOREIGN_BINARY") != NULL); if (force_foreign > 0 || !tracee->qemu) return false; fd = open_elf(t_path, &elf_header); if (fd < 0) return false; close(fd); elf_machine = ELF_FIELD(elf_header, machine); for (i = 0; host_elf_machine[i] != 0; i++) { if (host_elf_machine[i] == elf_machine) { VERBOSE(tracee, 1, "'%s' is a host ELF", t_path); return true; } } return false; } /** * Invoke @callback on each dynamic entry of the given @type for the * file referenced by @fd -- which has the provided @elf_header and * @program_header (type == PT_DYNAMIC) --. The iteration is stopped * if @callback returns an error (something < 0). This function * returns -errno if an error occured, otherwise 0. */ static int foreach_dynamic_entry(int fd, const ElfHeader *elf_header, const ProgramHeader *program_header, DynamicType type, int (*callback)(uint64_t)) { DynamicEntry dynamic_entry; size_t sizeof_dynamic_entry; uint64_t offset; uint64_t size; size_t i; assert(elf_header); assert(program_header); assert(callback); assert(PROGRAM_FIELD(*elf_header, *program_header, type) == PT_DYNAMIC); offset = PROGRAM_FIELD(*elf_header, *program_header, offset); size = PROGRAM_FIELD(*elf_header, *program_header, filesz); if (IS_CLASS32(*elf_header)) sizeof_dynamic_entry = sizeof(DynamicEntry32); else sizeof_dynamic_entry = sizeof(DynamicEntry64); if (size % sizeof_dynamic_entry != 0) return -ENOEXEC; for (i = 0; i < size / sizeof_dynamic_entry; i++) { int status; /* callback() may change the file offset. */ status = (int) lseek(fd, offset + i * sizeof_dynamic_entry, SEEK_SET); if (status < 0) return -errno; status = read(fd, &dynamic_entry, sizeof_dynamic_entry); if (status < 0) return status; if (DYNAMIC_FIELD(*elf_header, dynamic_entry, tag) != type) continue; status = callback(DYNAMIC_FIELD(*elf_header, dynamic_entry, val)); if (status < 0) return status; } return 0; } /** * Add to @xpaths the paths (':'-separated list) from the file * referenced by @fd at the given @offset. This function returns * -errno if an error occured, otherwise 0. */ static int add_xpaths(const Tracee *tracee, int fd, uint64_t offset, char **xpaths) { char *paths = NULL; char *tmp; size_t length; size_t size; int status; status = (int) lseek(fd, offset, SEEK_SET); if (status < 0) return -errno; /* Read the complete list of paths. */ length = 0; paths = NULL; do { size = length + 1024; tmp = talloc_realloc(tracee->ctx, paths, char, size); if (!tmp) return -ENOMEM; paths = tmp; status = read(fd, paths + length, 1024); if (status < 0) return status; length += strnlen(paths + length, 1024); } while (length == size); /* Concatene this list of paths to xpaths. */ if (!*xpaths) { *xpaths = talloc_array(tracee->ctx, char, length + 1); if (!*xpaths) return -ENOMEM; strcpy(*xpaths, paths); } else { length += strlen(*xpaths); length++; /* ":" separator */ tmp = talloc_realloc(tracee->ctx, *xpaths, char, length + 1); if (!tmp) return -ENOMEM; *xpaths = tmp; strcat(*xpaths, ":"); strcat(*xpaths, paths); } /* I don't know if DT_R*PATH entries are unique. In * doubt I support multiple entries. */ return 0; } /** * Put the RPATH and RUNPATH dynamic entries from the file referenced * by @fd -- which has the provided @elf_header -- in @rpaths and * @runpaths respectively. This function returns -errno if an error * occured, otherwise 0. */ int read_ldso_rpaths(const Tracee* tracee, int fd, const ElfHeader *elf_header, char **rpaths, char **runpaths) { ProgramHeader dynamic; ProgramHeader strtab_segment; uint64_t strtab_address = (uint64_t) -1; off_t strtab_offset; int status; status = find_program_header(tracee, fd, elf_header, &dynamic, PT_DYNAMIC, (uint64_t) -1); if (status <= 0) return status; /* Callback used to get the address of the *first* string * table. The ELF specification doesn't mention if it may * have several string table references. */ int get_strtab_address(uint64_t value) { strtab_address = value; return -1; /* Stop the loop. */ } status = foreach_dynamic_entry(fd, elf_header, &dynamic, DT_STRTAB, get_strtab_address); if (strtab_address == (uint64_t) -1) { if (status < 0) return status; else return 0; } /* Search the program header that contains the given string table. */ status = find_program_header(tracee, fd, elf_header, &strtab_segment, PT_LOAD, strtab_address); if (status < 0) return status; strtab_offset = PROGRAM_FIELD(*elf_header, strtab_segment, offset) + (strtab_address - PROGRAM_FIELD(*elf_header, strtab_segment, vaddr)); int add_rpaths(uint64_t index) { if (strtab_offset < 0 || (uint64_t) strtab_offset > UINT64_MAX - index) return -ENOEXEC; return add_xpaths(tracee, fd, strtab_offset + index, rpaths); } int add_runpaths(uint64_t index) { if (strtab_offset < 0 || (uint64_t) strtab_offset > UINT64_MAX - index) return -ENOEXEC; return add_xpaths(tracee, fd, strtab_offset + index, runpaths); } status = foreach_dynamic_entry(fd, elf_header, &dynamic, DT_RPATH, add_rpaths); if (status < 0) return status; status = foreach_dynamic_entry(fd, elf_header, &dynamic, DT_RUNPATH, add_runpaths); if (status < 0) return status; return 0; } proot-3.0.2/src/execve/elf.h0000644000175000017500000001017012156552156015227 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef ELF_H #define ELF_H #define EI_NIDENT 16 #include #include typedef struct { unsigned char e_ident[EI_NIDENT]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; uint32_t e_entry; uint32_t e_phoff; uint32_t e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; } ElfHeader32; typedef struct { unsigned char e_ident[EI_NIDENT]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; uint64_t e_entry; uint64_t e_phoff; uint64_t e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; } ElfHeader64; typedef union { ElfHeader32 class32; ElfHeader64 class64; } ElfHeader; typedef struct { uint32_t p_type; uint32_t p_offset; uint32_t p_vaddr; uint32_t p_paddr; uint32_t p_filesz; uint32_t p_memsz; uint32_t p_flags; uint32_t p_align; } ProgramHeader32; typedef struct { uint32_t p_type; uint32_t p_flags; uint64_t p_offset; uint64_t p_vaddr; uint64_t p_paddr; uint64_t p_filesz; uint64_t p_memsz; uint64_t p_align; } ProgramHeader64; typedef union { ProgramHeader32 class32; ProgramHeader64 class64; } ProgramHeader; typedef enum { PT_LOAD = 1, PT_DYNAMIC = 2, PT_INTERP = 3 } SegmentType; typedef struct { int32_t d_tag; uint32_t d_val; } DynamicEntry32; typedef struct { int64_t d_tag; uint64_t d_val; } DynamicEntry64; typedef union { DynamicEntry32 class32; DynamicEntry64 class64; } DynamicEntry; typedef enum { DT_STRTAB = 5, DT_RPATH = 15, DT_RUNPATH = 29 } DynamicType; /* The following macros are also compatible with ELF 64-bit. */ #define ELF_IDENT(header, index) (header).class32.e_ident[(index)] #define ELF_CLASS(header) ELF_IDENT(header, 4) #define IS_CLASS32(header) (ELF_CLASS(header) == 1) #define IS_CLASS64(header) (ELF_CLASS(header) == 2) /* Helper to access a @field of the structure ElfHeaderXX. */ #define ELF_FIELD(header, field) \ (IS_CLASS64(header) \ ? (header).class64. e_ ## field \ : (header).class32. e_ ## field) /* Helper to access a @field of the structure ProgramHeaderXX */ #define PROGRAM_FIELD(ehdr, phdr, field) \ (IS_CLASS64(ehdr) \ ? (phdr).class64. p_ ## field \ : (phdr).class32. p_ ## field) /* Helper to access a @field of the structure DynamicEntryXX */ #define DYNAMIC_FIELD(ehdr, dynent, field) \ (IS_CLASS64(ehdr) \ ? (dynent).class64. d_ ## field \ : (dynent).class32. d_ ## field) #define KNOWN_PHENTSIZE(header, size) \ ( (IS_CLASS32(header) && (size) == sizeof(ProgramHeader32)) \ || (IS_CLASS64(header) && (size) == sizeof(ProgramHeader64))) #include "tracee/tracee.h" extern int open_elf(const char *t_path, ElfHeader *elf_header); extern bool is_host_elf(const Tracee *tracee, const char *t_path); extern int find_program_header(const Tracee *tracee, int fd, const ElfHeader *elf_header, ProgramHeader *program_header, SegmentType type, uint64_t address); extern int read_ldso_rpaths(const Tracee *tracee, int fd, const ElfHeader *elf_header, char **rpath, char **runpath); #endif /* ELF_H */ proot-3.0.2/src/attribute.h0000644000175000017500000000177412156552156015217 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef ATTRIBUTE_H #define ATTRIBUTE_H #define UNUSED __attribute__((unused)) #define FORMAT(a, b, c) __attribute__ ((format (a, b, c))) #endif /* ATTRIBUTE_H */ proot-3.0.2/src/.check_process_vm.c0000644000175000017500000000027012156552156016570 0ustar ivoireivoire#define _GNU_SOURCE #include #include int main(void) { return process_vm_readv(0, NULL, 0, NULL, 0, 0) + process_vm_writev(0, NULL, 0, NULL, 0, 0); } proot-3.0.2/src/cli.h0000644000175000017500000002241012156552156013751 0ustar ivoireivoire/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ #ifndef CLI_H #define CLI_H #include #include "tracee/tracee.h" #include "build.h" typedef struct { const char *name; char separator; const char *value; } Argument; typedef int (*option_handler_t)(Tracee *tracee, char *value); typedef struct { const char *class; option_handler_t handler; const char *description; const char *detail; Argument arguments[5]; } Option; #ifndef VERSION #define VERSION "3.0.2" #endif static const char *version = VERSION; static const char *subtitle = "chroot, mount --bind, and binfmt_misc without privilege/setup"; static const char *synopsis = "proot [option] ... [command]"; static const char *colophon = "Visit http://proot.me for help, bug reports, suggestions, patchs, ...\n\ Copyright (C) 2013 STMicroelectronics, licensed under GPL v2 or later."; static char *recommended_bindings[] = { "/etc/host.conf", "/etc/hosts", "/etc/hosts.equiv", "/etc/mtab", "/etc/netgroup", "/etc/networks", "/etc/passwd", "/etc/group", "/etc/nsswitch.conf", "/etc/resolv.conf", "/etc/localtime", "/dev/", "/sys/", "/proc/", "/tmp/", "$HOME", NULL, }; static int handle_option_r(Tracee *tracee, char *value); static int handle_option_b(Tracee *tracee, char *value); static int handle_option_q(Tracee *tracee, char *value); static int handle_option_w(Tracee *tracee, char *value); static int handle_option_v(Tracee *tracee, char *value); static int handle_option_V(Tracee *tracee, char *value); static int handle_option_h(Tracee *tracee, char *value); static int handle_option_k(Tracee *tracee, char *value); static int handle_option_0(Tracee *tracee, char *value); static int handle_option_B(Tracee *tracee, char *value); static int handle_option_Q(Tracee *tracee, char *value); static Option options[] = { { .class = "Regular options", .arguments = { { .name = "-r", .separator = ' ', .value = "path" }, { .name = "--rootfs", .separator = '=', .value = "path" }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_r, .description = "Use *path* as the new guest root file-system, default is /.", .detail = "\tThe specified path typically contains a Linux distribution where\n\ \tall new programs will be confined. The default rootfs is /\n\ \twhen none is specified, this makes sense when the bind mechanism\n\ \tis used to relocate host files and directories, see the -b\n\ \toption and the Examples section for details.", }, { .class = "Regular options", .arguments = { { .name = "-b", .separator = ' ', .value = "path" }, { .name = "--bind", .separator = '=', .value = "path" }, { .name = "-m", .separator = ' ', .value = "path" }, { .name = "--mount", .separator = '=', .value = "path" }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_b, .description = "Make the content of *path* accessible in the guest rootfs.", .detail = "\tThis option makes any file or directory of the host rootfs\n\ \taccessible in the confined environment just as if it were part of\n\ \tthe guest rootfs. By default the host path is bound to the same\n\ \tpath in the guest rootfs but users can specify any other location\n\ \twith the syntax: -b *host_path*:*guest_location*. If the\n\ \tguest location is a symbolic link, it is dereferenced to ensure\n\ \tthe new content is accessible through all the symbolic links that\n\ \tpoint to the overlaid content. In most cases this default\n\ \tbehavior shouldn't be a problem, although it is possible to\n\ \texplicitly not dereference the guest location by appending it the\n\ \t! character: -b *host_path*:*guest_location!*.", }, { .class = "Regular options", .arguments = { { .name = "-q", .separator = ' ', .value = "command" }, { .name = "--qemu", .separator = '=', .value = "command" }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_q, .description = "Execute guest programs through QEMU as specified by *command*.", .detail = "\tEach time a guest program is going to be executed, PRoot inserts\n\ \tthe QEMU user-mode command in front of the initial request.\n\ \tThat way, guest programs actually run on a virtual guest CPU\n\ \temulated by QEMU user-mode. The native execution of host programs\n\ \tis still effective and the whole host rootfs is bound to\n\ \t/host-rootfs in the guest environment.\n\ \t\n\ \tThis option is automatically enabled by the -Q option.", }, { .class = "Regular options", .arguments = { { .name = "-w", .separator = ' ', .value = "path" }, { .name = "--pwd", .separator = '=', .value = "path" }, { .name = "--cwd", .separator = '=', .value = "path" }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_w, .description = "Set the initial working directory to *path*.", .detail = "\tSome programs expect to be launched from a given directory but do\n\ \tnot perform any chdir by themselves. This option avoids the\n\ \tneed for running a shell and then entering the directory manually.", }, { .class = "Regular options", .arguments = { { .name = "-v", .separator = ' ', .value = "value" }, { .name = "--verbose", .separator = '=', .value = "value" }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_v, .description = "Set the level of debug information to *value*.", .detail = "\tThe higher the integer value is, the more detailled debug\n\ \tinformation is printed to the standard error stream. A negative\n\ \tvalue makes PRoot quiet except on fatal errors.", }, { .class = "Regular options", .arguments = { { .name = "-V", .separator = '\0', .value = NULL }, { .name = "--version", .separator = '\0', .value = NULL }, { .name = "--about", .separator = '\0', .value = NULL }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_V, .description = "Print version, copyright, license and contact, then exit.", .detail = "", }, { .class = "Regular options", .arguments = { { .name = "-h", .separator = '\0', .value = NULL }, { .name = "--help", .separator = '\0', .value = NULL }, { .name = "--usage", .separator = '\0', .value = NULL }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_h, .description = "Print the version and the command-line usage, then exit.", .detail = "", }, { .class = "Extension options", .arguments = { { .name = "-k", .separator = ' ', .value = "string" }, { .name = "--kernel-release", .separator = '=', .value = "string" }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_k, .description = "Set the kernel release and compatibility level to *string*.", .detail = "\tIf a program is run on a kernel older than the one expected by its\n\ \tGNU C library, the following error is reported: \"FATAL: kernel too\n\ \told\". To be able to run such programs, PRoot can emulate some of\n\ \tthe syscalls that are available in the kernel release specified by\n\ \tstring but that are missing in the current kernel.", }, { .class = "Extension options", .arguments = { { .name = "-0", .separator = '\0', .value = NULL }, { .name = "--root-id", .separator = '\0', .value = NULL }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_0, .description = "Force some syscalls to behave as if executed by \"root\".", .detail = "\tSome programs will refuse to work if they are not run with \"root\"\n\ \tprivileges, even if there is no technical reason for that. This\n\ \tis typically the case with package managers. This option allows\n\ \tusers to bypass this kind of limitation by faking the user/group\n\ \tidentity, and by faking the success of some operations like\n\ \tchanging the ownership of files, changing the root directory to\n\ \t/, ... Note that this option is quite limited compared to\n\ \tfakeroot.", }, { .class = "Alias options", .arguments = { { .name = "-B", .separator = '\0', .value = NULL }, { .name = "-M", .separator = '\0', .value = NULL }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_B, .description = "Alias: -b for each path of a recommended list", .detail = "\tThere are a couple of bindings that are needed for most guest\n\ \tprograms to behave correctly regarding the configuration part of\n\ \tthe host computer which is not specific to the host Linux\n\ \tdistribution, such as: user/group information, network setup,\n\ \trun-time information, users' files, ... This highly recommended\n\ \toption enables the following bindings:\n\ \t\n\ \t * /etc/host.conf\n\ \t * /etc/hosts\n\ \t * /etc/hosts.equiv\n\ \t * /etc/mtab\n\ \t * /etc/netgroup\n\ \t * /etc/networks\n\ \t * /etc/passwd\n\ \t * /etc/group\n\ \t * /etc/nsswitch.conf\n\ \t * /etc/resolv.conf\n\ \t * /etc/localtime\n\ \t * /dev/\n\ \t * /sys/\n\ \t * /proc/\n\ \t * /tmp/\n\ \t * $HOME", }, { .class = "Alias options", .arguments = { { .name = "-Q", .separator = ' ', .value = "command" }, { .name = NULL, .separator = '\0', .value = NULL } }, .handler = handle_option_Q, .description = "Alias: -q *command* -B", .detail = "\tThis option is highly recommended when using QEMU user-mode; it\n\ \tenables all the recommended bindings.", }, }; #endif /* CLI_H */ proot-3.0.2/src/notice.c0000644000175000017500000000371612156552156014466 0ustar ivoireivoire/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- * * This file is part of PRoot. * * Copyright (C) 2013 STMicroelectronics * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include /* errno, */ #include /* strerror(3), */ #include /* va_*, */ #include /* vfprintf(3), */ #include /* INT_MAX, */ #include "notice.h" #include "tracee/tracee.h" /** * Print @message to the standard error stream according to its * @severity and @origin. */ void notice(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...) { va_list extra_params; int verbose_level; verbose_level = (tracee != NULL ? tracee->verbose : 0); if (verbose_level < 0 && severity != ERROR) return; switch (severity) { case WARNING: fprintf(stderr, "proot warning: "); break; case ERROR: fprintf(stderr, "proot error: "); break; case INFO: default: fprintf(stderr, "proot info: "); break; } if (origin == TALLOC) fprintf(stderr, "talloc: "); va_start(extra_params, message); vfprintf(stderr, message, extra_params); va_end(extra_params); switch (origin) { case SYSTEM: fprintf(stderr, ": "); perror(NULL); break; case TALLOC: break; case INTERNAL: case USER: default: fprintf(stderr, "\n"); break; } return; }