Mirroring proot at v 5.1.0 See: https://sources.debian.org/src/proot/5.1.0-1.3 Change-Id: I614f2b51eced88d3e3840c7e35a38f769c9d1765
diff --git a/5.1.0/.pc/.quilt_patches b/5.1.0/.pc/.quilt_patches new file mode 100644 index 0000000..6857a8d --- /dev/null +++ b/5.1.0/.pc/.quilt_patches
@@ -0,0 +1 @@ +debian/patches
diff --git a/5.1.0/.pc/.quilt_series b/5.1.0/.pc/.quilt_series new file mode 100644 index 0000000..c206706 --- /dev/null +++ b/5.1.0/.pc/.quilt_series
@@ -0,0 +1 @@ +series
diff --git a/5.1.0/.pc/.version b/5.1.0/.pc/.version new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/5.1.0/.pc/.version
@@ -0,0 +1 @@ +2
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/extension/kompat/kompat.c b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/extension/kompat/kompat.c new file mode 100644 index 0000000..c4e25a2 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/extension/kompat/kompat.c
@@ -0,0 +1,1044 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdint.h> /* intptr_t, */ +#include <stdlib.h> /* strtoul(3), */ +#include <linux/version.h> /* KERNEL_VERSION, */ +#include <assert.h> /* assert(3), */ +#include <sys/utsname.h> /* uname(2), utsname, */ +#include <string.h> /* str*(3), memcpy(3), */ +#include <talloc.h> /* talloc_*, */ +#include <fcntl.h> /* AT_*, */ +#include <sys/ptrace.h> /* linux.git:c0a3a20b */ +#include <errno.h> /* errno, */ +#include <linux/auxvec.h> /* AT_, */ +#include <linux/futex.h> /* FUTEX_PRIVATE_FLAG */ +#include <sys/param.h> /* MIN, */ + +#include "extension/extension.h" +#include "syscall/seccomp.h" +#include "syscall/sysnum.h" +#include "syscall/chain.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "tracee/mem.h" +#include "execve/auxv.h" +#include "cli/note.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; + +#define NONE {{0, 0, 0}} + +typedef struct { + int actual_release; + int virtual_release; + struct utsname utsname; + word_t hwcap; +} Config; + +/** + * Return whether the @expected_release is newer than + * @config->actual_release and older than @config->virtual_release. + */ +static bool needs_kompat(const Config *config, int expected_release) +{ + return (expected_release > config->actual_release + && expected_release <= config->virtual_release); +} + +/** + * Modify the current syscall of @tracee as described by @modif + * regarding the given @config. This function returns whether the + * syscall was modified or not. + */ +static bool modify_syscall(Tracee *tracee, const Config *config, const Modif *modif) +{ + size_t i, j; + word_t syscall; + + assert(config != NULL); + + if (!needs_kompat(config, modif->expected_release)) + return false; + + /* Check if this syscall is supported on this architecture. */ + syscall = detranslate_sysnum(get_abi(tracee), modif->new_sysarg_num); + if (syscall == SYSCALL_AVOIDER) + return false; + + set_sysnum(tracee, 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 true; +} + +/** + * 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); +} + +/** + * Remove @discarded_flags from the given @tracee's @sysarg register + * if the actual kernel release is not compatible with the + * @expected_release. + */ +static void discard_fd_flags(Tracee *tracee, const Config *config, + int discarded_flags, int expected_release, Reg sysarg) +{ + word_t flags; + + if (!needs_kompat(config, expected_release)) + return; + + flags = peek_reg(tracee, CURRENT, sysarg); + poke_reg(tracee, sysarg, flags & ~discarded_flags); +} + +/** + * Replace current @tracee's syscall with an older and compatible one + * whenever it's required, i.e. when the syscall is supported by the + * kernel as specified by @config->virtual_release but it isn't + * supported by the actual kernel. + */ +static int handle_sysenter_end(Tracee *tracee, Config *config) +{ + /* Note: syscalls like "openat" can be replaced by "open" since PRoot + * has canonicalized "fd + path" into "path". */ + switch (get_sysnum(tracee, ORIGINAL)) { + case PR_accept4: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,28), + .new_sysarg_num = PR_accept, + .shifts = NONE + }; + 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 = NONE + }; + + /* "If oldfd equals newfd, then dup3() fails with the + * error EINVAL" -- man dup3 */ + if (peek_reg(tracee, CURRENT, SYSARG_1) == peek_reg(tracee, CURRENT, SYSARG_2)) + return -EINVAL; + + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_epoll_create1: { + bool modified; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_epoll_create, + .shifts = NONE + }; + + /* "the size argument is ignored, but must be greater + * than zero" -- man epoll_create */ + modified = modify_syscall(tracee, config, &modif); + if (modified) + poke_reg(tracee, SYSARG_1, 1); + return 0; + } + + case PR_epoll_pwait: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,19), + .new_sysarg_num = PR_epoll_wait, + .shifts = NONE + }; + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_eventfd2: { + bool modified; + word_t flags; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_eventfd, + .shifts = NONE + }; + + modified = modify_syscall(tracee, config, &modif); + if (modified) { + /* EFD_SEMAPHORE can't be emulated with eventfd. */ + flags = peek_reg(tracee, CURRENT, SYSARG_2); + if ((flags & EFD_SEMAPHORE) != 0) + return -EINVAL; + } + 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 } + } + }; + 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 } + } + }; + 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); + + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_fcntl: { + word_t command; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,24))) + return 0; + + command = peek_reg(tracee, ORIGINAL, SYSARG_2); + if (command == F_DUPFD_CLOEXEC) + poke_reg(tracee, SYSARG_2, F_DUPFD); + + 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); + if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0) + return -EINVAL; /* Exposed by LTP. */ + +#if defined(ARCH_X86_64) + if ((flags & AT_SYMLINK_NOFOLLOW) != 0) + modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_lstat : PR_lstat64); + else + modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_stat : PR_stat64); +#else + if ((flags & AT_SYMLINK_NOFOLLOW) != 0) + modif.new_sysarg_num = PR_lstat64; + else + modif.new_sysarg_num = PR_stat64; +#endif + + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_futex: { + word_t operation; + static bool warned = false; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,22)) || config->actual_release == 0) + return 0; + + operation = peek_reg(tracee, CURRENT, SYSARG_2); + if ((operation & FUTEX_PRIVATE_FLAG) == 0) + return 0; + + if (!warned) { + warned = true; + note(tracee, WARNING, USER, + "kompat: this kernel doesn't support private futexes " + "and PRoot can't emulate them. Expect some troubles..."); + } + + poke_reg(tracee, SYSARG_2, operation & ~FUTEX_PRIVATE_FLAG); + 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 = NONE + }; + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_linkat: { + word_t flags; + 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 } + } + }; + + flags = peek_reg(tracee, CURRENT, SYSARG_5); + if ((flags & ~AT_SYMLINK_FOLLOW) != 0) + return -EINVAL; /* Exposed by LTP. */ + + 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: { + bool modified; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,16), + .new_sysarg_num = PR_open, + .shifts = { [0] = { + .sysarg = SYSARG_2, + .nb_args = 3, + .offset = -1 } + } + }; + modified = modify_syscall(tracee, config, &modif); + discard_fd_flags(tracee, config, O_CLOEXEC, KERNEL_VERSION(2,6,23), + modified ? SYSARG_2 : SYSARG_3); + return 0; + } + + case PR_open: + discard_fd_flags(tracee, config, O_CLOEXEC, KERNEL_VERSION(2,6,23), SYSARG_2); + return 0; + + case PR_pipe2: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_pipe, + .shifts = NONE + }; + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_pselect6: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,16), + .shifts = NONE + }; +#if defined(ARCH_X86_64) + modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_select : PR__newselect); +#else + modif.new_sysarg_num = PR__newselect; +#endif + + 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: { + bool modified; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_signalfd, + .shifts = NONE + }; + + /* "In Linux up to version 2.6.26, the flags argument + * is unused, and must be specified as zero." -- man + * signalfd */ + modified = modify_syscall(tracee, config, &modif); + if (modified) + poke_reg(tracee, SYSARG_4, 0); + return 0; + } + + case PR_socket: + case PR_socketpair: + case PR_timerfd_create: + discard_fd_flags(tracee, config, O_CLOEXEC | O_NONBLOCK, + KERNEL_VERSION(2,6,27), SYSARG_2); + 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); + + modify_syscall(tracee, config, &modif); + return 0; + } + + default: + return 0; + } +} + +/** + * Adjust some ELF auxiliary vectors to improve the compatibility. + * This function assumes the "argv, envp, auxv" stuff is pointed to by + * @tracee's stack pointer, as expected right after a successful call + * to execve(2). + */ +static void adjust_elf_auxv(Tracee *tracee, Config *config) +{ + ElfAuxVector *vectors; + ElfAuxVector *vector; + word_t vectors_address; + word_t stack_pointer; + void *argv_envp; + size_t size; + int status; + + vectors_address = get_elf_aux_vectors_address(tracee); + if (vectors_address == 0) + return; + + vectors = fetch_elf_aux_vectors(tracee, vectors_address); + if (vectors == NULL) + return; + + for (vector = vectors; vector->type != AT_NULL; vector++) { + switch (vector->type) { + /* Discard AT_SYSINFO* vectors: they can be used to + * get the OS release number from memory instead of + * from the uname syscall, and only this latter is + * currently hooked by PRoot. */ + case AT_SYSINFO_EHDR: + case AT_SYSINFO: + vector->type = AT_IGNORE; + vector->value = 0; + break; + + case AT_HWCAP: + if (config->hwcap != (word_t) -1) + vector->value = config->hwcap; + break; + + case AT_RANDOM: + /* Skip only if not in forced mode. */ + if (config->actual_release != 0) + goto end; + break; + + default: + break; + } + } + + /* Add the AT_RANDOM vector only if needed. */ + if (!needs_kompat(config, KERNEL_VERSION(2,6,29))) + goto end; + + status = add_elf_aux_vector(&vectors, AT_RANDOM, vectors_address); + if (status < 0) + goto end; /* Not fatal. */ + + /* Since a new vector needs to be added, the ELF auxiliary + * vectors array can't be pushed in place. As a consequence, + * argv[] and envp[] arrays are moved one vector downward to + * make room for the new ELF auxiliary vectors array. + * Remember, the stack layout is as follow right after execve: + * + * argv[], envp[], auxv[] + */ + stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER); + size = vectors_address - stack_pointer; + argv_envp = talloc_size(tracee->ctx, size); + if (argv_envp == NULL) + goto end; + + status = read_data(tracee, argv_envp, stack_pointer, size); + if (status < 0) + goto end; + + /* Allocate enough room in tracee's stack for the new ELF + * auxiliary vector. */ + stack_pointer -= 2 * sizeof_word(tracee); + vectors_address -= 2 * sizeof_word(tracee); + + /* Note that it is safe to update the stack pointer manually + * since we are in execve sysexit. However it should be done + * before transfering data since the kernel might not allow + * page faults below the stack pointer. */ + poke_reg(tracee, STACK_POINTER, stack_pointer); + + status = write_data(tracee, stack_pointer, argv_envp, size); + if (status < 0) + return; + +end: + push_elf_aux_vectors(tracee, vectors, vectors_address); + return; +} + +/** + * Append to the @tracee's current syscall enough calls to fcntl(@fd) + * in order to set the flags from the original @sysarg register, if + * there are also set in @emulated_flags. + */ +static void emulate_fd_flags(Tracee *tracee, word_t fd, Reg sysarg, int emulated_flags) +{ + word_t flags; + + flags = peek_reg(tracee, ORIGINAL, sysarg); + if (flags == 0) + return; + + if ((emulated_flags & flags & O_CLOEXEC) != 0) + register_chained_syscall(tracee, PR_fcntl, fd, F_SETFD, FD_CLOEXEC, 0, 0, 0); + + if ((emulated_flags & flags & O_NONBLOCK) != 0) + register_chained_syscall(tracee, PR_fcntl, fd, F_SETFL, O_NONBLOCK, 0, 0, 0); + + force_chain_final_result(tracee, peek_reg(tracee, CURRENT, SYSARG_RESULT)); +} + +/** + * Adjust the results/output parameters for syscalls that were + * modified in handle_sysenter_end(). This function returns -errno if + * an error occured, otherwise 0. + */ +static int handle_sysexit_end(Tracee *tracee, Config *config) +{ + word_t result; + word_t sysnum; + int status; + + result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + sysnum = get_sysnum(tracee, ORIGINAL); + + /* Error reported by the kernel. */ + status = (int) result; + if (status < 0) + return 0; + + switch (sysnum) { + case PR_uname: { + word_t address; + + address = peek_reg(tracee, ORIGINAL, SYSARG_1); + + /* The layout of struct utsname does not depend on the + * architecture, it only depends on the kernel + * version. In this regards, this structure is stable + * since < 2.6.0. */ + status = write_data(tracee, address, &config->utsname, sizeof(config->utsname)); + if (status < 0) + return status; + return 0; + } + + case PR_setdomainname: + case PR_sethostname: { + word_t address; + word_t length; + char *name; + + name = (sysnum == PR_setdomainname + ? config->utsname.domainname + : config->utsname.nodename); + + length = peek_reg(tracee, ORIGINAL, SYSARG_2); + if (length > sizeof(config->utsname.domainname) - 1) + return -EINVAL; + + /* Because of the test above. */ + assert(sizeof(config->utsname.domainname) == sizeof(config->utsname.nodename)); + + address = peek_reg(tracee, ORIGINAL, SYSARG_1); + status = read_data(tracee, name, address, length); + if (status < 0) + return status; + + /* "name does not require a terminating null byte." -- + * man 2 set{domain,host}name. */ + name[length] = '\0'; + + return 0; + } + + case PR_accept4: + if (get_sysnum(tracee, MODIFIED) == PR_accept) + emulate_fd_flags(tracee, result, SYSARG_4, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_dup3: + if (get_sysnum(tracee, MODIFIED) == PR_dup2) + emulate_fd_flags(tracee, peek_reg(tracee, ORIGINAL, SYSARG_2), + SYSARG_3, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_epoll_create1: + if (get_sysnum(tracee, MODIFIED) == PR_epoll_create) + emulate_fd_flags(tracee, result, SYSARG_1, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_eventfd2: + if (get_sysnum(tracee, MODIFIED) == PR_eventfd) + emulate_fd_flags(tracee, result, SYSARG_2, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_fcntl: { + word_t command; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,24))) + return 0; + + command = peek_reg(tracee, ORIGINAL, SYSARG_2); + if (command != F_DUPFD_CLOEXEC) + return 0; + + register_chained_syscall(tracee, PR_fcntl, result, F_SETFD, FD_CLOEXEC, 0, 0, 0); + force_chain_final_result(tracee, peek_reg(tracee, CURRENT, SYSARG_RESULT)); + return 0; + } + + case PR_inotify_init1: + if (get_sysnum(tracee, MODIFIED) == PR_inotify_init) + emulate_fd_flags(tracee, result, SYSARG_1, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_open: + if (needs_kompat(config, KERNEL_VERSION(2,6,23))) + emulate_fd_flags(tracee, result, SYSARG_2, O_CLOEXEC); + return 0; + + case PR_openat: + if (needs_kompat(config, KERNEL_VERSION(2,6,23))) + emulate_fd_flags(tracee, result, SYSARG_3, O_CLOEXEC); + return 0; + + case PR_pipe2: { + int fds[2]; + + if (get_sysnum(tracee, MODIFIED) != PR_pipe) + return 0; + + status = read_data(tracee, fds, peek_reg(tracee, MODIFIED, SYSARG_1), sizeof(fds)); + if (status < 0) + return 0; + + emulate_fd_flags(tracee, fds[0], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + emulate_fd_flags(tracee, fds[1], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + + return 0; + } + + case PR_signalfd4: + if (get_sysnum(tracee, MODIFIED) == PR_signalfd) + emulate_fd_flags(tracee, result, SYSARG_4, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_socket: + case PR_timerfd_create: + if (needs_kompat(config, KERNEL_VERSION(2,6,27))) + emulate_fd_flags(tracee, result, SYSARG_2, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_socketpair: { + int fds[2]; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,27))) + return 0; + + status = read_data(tracee, fds, peek_reg(tracee, MODIFIED, SYSARG_4), sizeof(fds)); + if (status < 0) + return 0; + + emulate_fd_flags(tracee, fds[0], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + emulate_fd_flags(tracee, fds[1], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + + return 0; + } + + default: + return 0; + } + + return 0; +} + +/** + * Fill @config->utsname and @config->hwcap according to the content + * of @string. This function returns -1 if there is a parsing error, + * otherwise 0. + */ +static int parse_utsname(Config *config, const char *string) +{ + struct utsname utsname; + int status; + + assert(string != NULL); + + status = uname(&utsname); + if (status < 0 || getenv("PROOT_FORCE_KOMPAT") != NULL) + config->actual_release = 0; + else + config->actual_release = parse_kernel_release(utsname.release); + + /* Check whether it is the simple format (ie. release number), + * or the complex one: + * + * '\sysname\nodename\release\version\machine\domainname\hwcap\' + * + * This complex format is ugly on purpose: it ain't to be used + * directly by users. */ + if (string[0] == '\\') { + const char *start; + const char *end; + char *end2; + + /* Initial state of the parser. */ + end = string; + +#define PARSE(field) do { \ + size_t length; \ + \ + start = end + 1; \ + end = strchr(start, '\\'); \ + if (end == NULL) { \ + note(NULL, ERROR, USER, \ + "can't find %s field in '%s'", #field, string); \ + return -1; \ + } \ + \ + length = end - start; \ + length = MIN(length, sizeof(config->utsname.field) - 1); \ + strncpy(config->utsname.field, start, length); \ + config->utsname.field[length] = '\0'; \ + } while(0) + + PARSE(sysname); + PARSE(nodename); + PARSE(release); + PARSE(version); + PARSE(machine); + PARSE(domainname); + +#undef PARSE + + /* The hwcap field is parsed as an hexadecimal value. */ + errno = 0; + config->hwcap = strtol(end + 1, &end2, 16); + if (errno != 0 || end2[0] != '\\') { + note(NULL, ERROR, USER, "can't find hwcap field in '%s'", string); + return -1; + } + } + else { + size_t length; + + memcpy(&config->utsname, &utsname, sizeof(config->utsname)); + + length = MIN(strlen(string), sizeof(config->utsname.release) - 1); + strncpy(config->utsname.release, string, length); + config->utsname.release[length] = '\0'; + + config->hwcap = (word_t) -1; + } + + config->virtual_release = parse_kernel_release(config->utsname.release); + + return 0; +} + +/* List of syscalls handled by this extensions. */ +static FilteredSysnum filtered_sysnums[] = { + { PR_accept4, FILTER_SYSEXIT }, + { PR_dup3, FILTER_SYSEXIT }, + { PR_epoll_create1, FILTER_SYSEXIT }, + { PR_epoll_pwait, 0 }, + { PR_eventfd2, FILTER_SYSEXIT }, + { PR_execve, FILTER_SYSEXIT }, + { PR_faccessat, 0 }, + { PR_fchmodat, 0 }, + { PR_fchownat, 0 }, + { PR_fcntl, FILTER_SYSEXIT }, + { PR_fstatat64, 0 }, + { PR_futimesat, 0 }, + { PR_futex, 0 }, + { PR_inotify_init1, FILTER_SYSEXIT }, + { PR_linkat, 0 }, + { PR_mkdirat, 0 }, + { PR_mknodat, 0 }, + { PR_newfstatat, 0 }, + { PR_open, FILTER_SYSEXIT }, + { PR_openat, FILTER_SYSEXIT }, + { PR_pipe2, FILTER_SYSEXIT }, + { PR_pselect6, 0 }, + { PR_readlinkat, 0 }, + { PR_renameat, 0 }, + { PR_setdomainname, FILTER_SYSEXIT }, + { PR_sethostname, FILTER_SYSEXIT }, + { PR_signalfd4, FILTER_SYSEXIT }, + { PR_socket, FILTER_SYSEXIT }, + { PR_socketpair, FILTER_SYSEXIT }, + { PR_symlinkat, 0 }, + { PR_timerfd_create, FILTER_SYSEXIT }, + { PR_uname, FILTER_SYSEXIT }, + { PR_unlinkat, 0 }, + FILTERED_SYSNUM_END, +}; + +/** + * 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: { + Config *config; + + extension->config = talloc_zero(extension, Config); + if (extension->config == NULL) + return -1; + config = extension->config; + + status = parse_utsname(config, (const char *) data1); + if (status < 0) + return -1; + + extension->filtered_sysnums = filtered_sysnums; + 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; + + return handle_sysenter_end(tracee, config); + } + + case SYSCALL_EXIT_END: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + + return handle_sysexit_end(tracee, config); + } + + case SYSCALL_EXIT_START: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + word_t result = peek_reg(tracee, CURRENT, SYSARG_RESULT);; + word_t sysnum = get_sysnum(tracee, ORIGINAL); + + /* Note: this can be done only before PRoot pushes the + * load script into tracee's stack. */ + if ((int) result >= 0 && sysnum == PR_execve) + adjust_elf_auxv(tracee, config); + return 0; + } + + default: + return 0; + } +}
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/enter.c b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/enter.c new file mode 100644 index 0000000..c133b72 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/enter.c
@@ -0,0 +1,575 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* errno(3), E* */ +#include <talloc.h> /* talloc_*, */ +#include <sys/un.h> /* struct sockaddr_un, */ +#include <linux/net.h> /* SYS_*, */ +#include <fcntl.h> /* AT_FDCWD, */ +#include <limits.h> /* PATH_MAX, */ + +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "syscall/socket.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "syscall/heap.h" +#include "extension/extension.h" +#include "execve/execve.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "path/path.h" +#include "path/canon.h" +#include "arch.h" + +/** + * 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); +} + +/** + * 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. + */ +int 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; + bool special = false; + + status = notify_extensions(tracee, SYSCALL_ENTER_START, 0, 0); + if (status < 0) + goto end; + if (status > 0) + return 0; + + /* Translate input arguments. */ + syscall_number = get_sysnum(tracee, ORIGINAL); + switch (syscall_number) { + default: + /* Nothing to do. */ + status = 0; + break; + + case PR_execve: + status = translate_execve_enter(tracee); + break; + + case PR_ptrace: + status = translate_ptrace_enter(tracee); + break; + + case PR_wait4: + case PR_waitpid: + status = translate_wait_enter(tracee); + break; + + case PR_brk: + status = translate_brk_enter(tracee); + break; + + case PR_getcwd: + set_sysnum(tracee, PR_void); + status = 0; + break; + + case PR_fchdir: + case PR_chdir: { + struct stat statl; + char *tmp; + + /* The ending "." ensures an error will be reported if + * path does not exist or if it is not a directory. */ + if (syscall_number == PR_chdir) { + status = get_sysarg_path(tracee, path, SYSARG_1); + if (status < 0) + break; + + status = join_paths(2, oldpath, path, "."); + if (status < 0) + break; + + dirfd = AT_FDCWD; + } + else { + strcpy(oldpath, "."); + dirfd = peek_reg(tracee, CURRENT, SYSARG_1); + } + + status = translate_path(tracee, path, dirfd, oldpath, true); + if (status < 0) + break; + + status = lstat(path, &statl); + if (status < 0) + break; + + /* Check this directory is accessible. */ + if ((statl.st_mode & S_IXUSR) == 0) + return -EACCES; + + /* 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 = 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"); + + set_sysnum(tracee, PR_void); + 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; + } + +#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) + +#define PEEK_WORD(addr, forced_errno) \ + peek_word(tracee, addr); \ + if (errno != 0) { \ + status = forced_errno ?: -errno; \ + break; \ + } + +#define POKE_WORD(addr, value) \ + poke_word(tracee, addr, value); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + + case PR_accept: + case PR_accept4: + /* Nothing special to do if no sockaddr was specified. */ + if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) { + status = 0; + break; + } + special = true; + /* Fall through. */ + case PR_getsockname: + case PR_getpeername:{ + int size; + + /* Remember: PEEK_WORD puts -errno in status and breaks if an + * error occured. */ + size = (int) PEEK_WORD(peek_reg(tracee, ORIGINAL, SYSARG_3), special ? -EINVAL : 0); + + /* 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; + + 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: + /* Nothing special to do if no sockaddr was specified. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2), 0); + if (sock_addr == 0) { + status = 0; + break; + } + special = true; + /* Fall through. */ + case SYS_GETSOCKNAME: + case SYS_GETPEERNAME: + /* Remember: PEEK_WORD puts -errno in status and breaks + * if an error occured. */ + size_addr = PEEK_WORD(SYSARG_ADDR(3), 0); + size = (int) PEEK_WORD(size_addr, special ? -EINVAL : 0); + + /* 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_WORD puts -errno in status and breaks if an + * error occured. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2), 0); + size = PEEK_WORD(SYSARG_ADDR(3), 0); + + 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_WORD puts -errno in status and breaks if an + * error occured. */ + POKE_WORD(SYSARG_ADDR(2), sock_addr); + POKE_WORD(SYSARG_ADDR(3), sizeof(struct sockaddr_un)); + + status = 0; + break; + } + +#undef SYSARG_ADDR +#undef PEEK_WORD +#undef POKE_WORD + + 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_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_fchmodat: + case PR_faccessat: + 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: + 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: + 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; + } + +end: + status2 = notify_extensions(tracee, SYSCALL_ENTER_END, status, 0); + if (status2 < 0) + status = status2; + + return status; +} +
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/exit.c b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/exit.c new file mode 100644 index 0000000..5243824 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/exit.c
@@ -0,0 +1,454 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* errno(3), E* */ +#include <sys/utsname.h> /* struct utsname, */ +#include <linux/net.h> /* SYS_*, */ +#include <string.h> /* strlen(3), */ + +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "syscall/socket.h" +#include "syscall/chain.h" +#include "syscall/heap.h" +#include "execve/execve.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "path/path.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "extension/extension.h" +#include "arch.h" + +/** + * 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. + */ +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: + * - break: update the syscall result register with "status" + * - goto end: nothing else to do. + */ + syscall_number = get_sysnum(tracee, ORIGINAL); + syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + switch (syscall_number) { + case PR_brk: + translate_brk_exit(tracee); + goto end; + + case PR_getcwd: { + char path[PATH_MAX]; + size_t new_size; + size_t size; + word_t output; + + size = (size_t) peek_reg(tracee, ORIGINAL, SYSARG_2); + if (size == 0) { + status = -EINVAL; + break; + } + + /* Ensure cwd still exists. */ + status = translate_path(tracee, path, AT_FDCWD, ".", false); + if (status < 0) + break; + + new_size = strlen(tracee->fs->cwd) + 1; + if (size < new_size) { + status = -ERANGE; + break; + } + + /* Overwrite the path. */ + output = peek_reg(tracee, ORIGINAL, SYSARG_1); + 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: + /* Nothing special to do if no sockaddr was specified. */ + if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) + goto end; + /* Fall through. */ + 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; + } + +#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) + +#define POKE_WORD(addr, value) \ + poke_word(tracee, addr, value); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + +#define PEEK_WORD(addr) \ + peek_word(tracee, addr); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + + 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: + /* Nothing special to do if no sockaddr was specified. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2)); + if (sock_addr == 0) + goto end; + /* Fall through. */ + 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_WORD + * puts -errno in status and breaks if an error + * occured. */ + POKE_WORD(SYSARG_ADDR(2), peek_reg(tracee, MODIFIED, SYSARG_5)); + POKE_WORD(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_WORD puts -errno in status and breaks if an + * error occured. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2)); + size_addr = PEEK_WORD(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; + } + +#undef SYSARG_ADDR +#undef PEEK_WORD +#undef POKE_WORD + + case PR_fchdir: + case PR_chdir: + /* These syscalls are fully emulated, see enter.c for details + * (like errors). */ + status = 0; + break; + + case PR_rename: + case PR_renameat: { + char old_path[PATH_MAX]; + char new_path[PATH_MAX]; + ssize_t old_length; + ssize_t new_length; + Comparison comparison; + Reg old_reg; + Reg new_reg; + char *tmp; + + /* Error reported by the kernel. */ + if ((int) syscall_result < 0) + goto end; + + if (syscall_number == PR_rename) { + old_reg = SYSARG_1; + new_reg = SYSARG_2; + } + else { + old_reg = SYSARG_2; + new_reg = SYSARG_4; + } + + /* Get the old path, then convert it to the same + * "point-of-view" as tracee->fs->cwd (guest). */ + status = read_path(tracee, old_path, peek_reg(tracee, MODIFIED, old_reg)); + if (status < 0) + break; + + status = detranslate_path(tracee, old_path, NULL); + if (status < 0) + break; + old_length = (status > 0 ? status - 1 : (ssize_t) strlen(old_path)); + + /* Nothing special to do if the moved path is not the + * current working directory. */ + comparison = compare_paths(old_path, tracee->fs->cwd); + if (comparison != PATH1_IS_PREFIX && comparison != PATHS_ARE_EQUAL) { + status = 0; + break; + } + + /* Get the new path, then convert it to the same + * "point-of-view" as tracee->fs->cwd (guest). */ + status = read_path(tracee, new_path, peek_reg(tracee, MODIFIED, new_reg)); + if (status < 0) + break; + + status = detranslate_path(tracee, new_path, NULL); + if (status < 0) + break; + new_length = (status > 0 ? status - 1 : (ssize_t) strlen(new_path)); + + /* Sanity check. */ + if (strlen(tracee->fs->cwd) >= PATH_MAX) { + status = 0; + break; + } + strcpy(old_path, tracee->fs->cwd); + + /* Update the virtual current working directory. */ + substitute_path_prefix(old_path, old_length, new_path, new_length); + + tmp = talloc_strdup(tracee->fs, old_path); + if (tmp == NULL) { + status = -ENOMEM; + break; + } + + TALLOC_FREE(tracee->fs->cwd); + tracee->fs->cwd = tmp; + + 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_path(tracee, referer, input); + 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: + translate_execve_exit(tracee); + goto end; + + case PR_ptrace: + status = translate_ptrace_exit(tracee); + break; + + case PR_wait4: + case PR_waitpid: + if (tracee->as_ptracer.waits_in != WAITS_IN_PROOT) + goto end; + + status = translate_wait_exit(tracee); + break; + + default: + goto end; + } + + 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); +}
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/seccomp.c b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/seccomp.c new file mode 100644 index 0000000..4b86fe8 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/seccomp.c
@@ -0,0 +1,511 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/prctl.h> /* prctl(2), PR_* */ +#include <linux/filter.h> /* struct sock_*, */ +#include <linux/seccomp.h> /* SECCOMP_MODE_FILTER, */ +#include <linux/filter.h> /* struct sock_*, */ +#include <linux/audit.h> /* AUDIT_, */ +#include <sys/queue.h> /* LIST_FOREACH, */ +#include <sys/types.h> /* size_t, */ +#include <talloc.h> /* talloc_*, */ +#include <errno.h> /* E*, */ +#include <string.h> /* memcpy(3), */ +#include <stddef.h> /* offsetof(3), */ +#include <stdint.h> /* uint*_t, UINT*_MAX, */ +#include <assert.h> /* assert(3), */ + +#include "syscall/seccomp.h" +#include "tracee/tracee.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "extension/extension.h" +#include "cli/note.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 arch, 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, arch, 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", + arch, 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) +{ + TALLOC_FREE(program->filter); + program->len = 0; +} + +/** + * Convert the given @sysnums into BPF 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 filtered syscall + * trace + * allow + * kill + * + * This function returns -errno if an error occurred, otherwise 0. + */ +static int set_seccomp_filters(const FilteredSysnum *sysnums) +{ + SeccompArch seccomp_archs[] = SECCOMP_ARCHS; + size_t nb_archs = sizeof(seccomp_archs) / sizeof(SeccompArch); + + struct sock_fprog program = { .len = 0, .filter = NULL }; + size_t nb_traced_syscalls; + size_t i, j, k; + int status; + + status = new_program_filter(&program); + if (status < 0) + goto end; + + /* For each handled architectures */ + for (i = 0; i < nb_archs; i++) { + word_t syscall; + + nb_traced_syscalls = 0; + + /* Pre-compute the number of traced syscalls for this architecture. */ + for (j = 0; j < seccomp_archs[i].nb_abis; j++) { + for (k = 0; sysnums[k].value != PR_void; k++) { + syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value); + if (syscall != SYSCALL_AVOIDER) + nb_traced_syscalls++; + } + } + + /* Filter: if handled architecture */ + status = start_arch_section(&program, seccomp_archs[i].value, nb_traced_syscalls); + if (status < 0) + goto end; + + for (j = 0; j < seccomp_archs[i].nb_abis; j++) { + for (k = 0; sysnums[k].value != PR_void; k++) { + /* Get the architecture specific syscall number. */ + syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value); + if (syscall == SYSCALL_AVOIDER) + continue; + + /* Filter: trace if handled syscall */ + status = add_trace_syscall(&program, syscall, sysnums[k].flags); + 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) + 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) + goto end; + + status = 0; +end: + free_program_filter(&program); + return status; +} + +/* List of sysnums handled by PRoot. */ +static FilteredSysnum proot_sysnums[] = { + { PR_accept, FILTER_SYSEXIT }, + { PR_accept4, FILTER_SYSEXIT }, + { PR_access, 0 }, + { PR_acct, 0 }, + { PR_bind, 0 }, + { PR_brk, FILTER_SYSEXIT }, + { PR_chdir, FILTER_SYSEXIT }, + { PR_chmod, 0 }, + { PR_chown, 0 }, + { PR_chown32, 0 }, + { PR_chroot, 0 }, + { PR_connect, 0 }, + { PR_creat, 0 }, + { PR_execve, FILTER_SYSEXIT }, + { PR_faccessat, 0 }, + { PR_fchdir, FILTER_SYSEXIT }, + { PR_fchmodat, 0 }, + { PR_fchownat, 0 }, + { PR_fstatat64, 0 }, + { PR_futimesat, 0 }, + { PR_getcwd, FILTER_SYSEXIT }, + { PR_getpeername, FILTER_SYSEXIT }, + { PR_getsockname, FILTER_SYSEXIT }, + { 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_ptrace, FILTER_SYSEXIT }, + { PR_readlink, FILTER_SYSEXIT }, + { PR_readlinkat, FILTER_SYSEXIT }, + { PR_removexattr, 0 }, + { PR_rename, FILTER_SYSEXIT }, + { PR_renameat, FILTER_SYSEXIT }, + { PR_rmdir, 0 }, + { PR_setxattr, 0 }, + { PR_socketcall, FILTER_SYSEXIT }, + { 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, FILTER_SYSEXIT }, + { PR_unlink, 0 }, + { PR_unlinkat, 0 }, + { PR_uselib, 0 }, + { PR_utime, 0 }, + { PR_utimensat, 0 }, + { PR_utimes, 0 }, + { PR_wait4, FILTER_SYSEXIT }, + { PR_waitpid, FILTER_SYSEXIT }, + FILTERED_SYSNUM_END, +}; + +/** + * Add the @new_sysnums to the list of filtered @sysnums, using the + * given Talloc @context. This function returns -errno if an error + * occurred, otherwise 0. + */ +static int merge_filtered_sysnums(TALLOC_CTX *context, FilteredSysnum **sysnums, + const FilteredSysnum *new_sysnums) +{ + size_t i, j; + + assert(sysnums != NULL); + + if (*sysnums == NULL) { + /* Start with no sysnums but the terminator. */ + *sysnums = talloc_array(context, FilteredSysnum, 1); + if (*sysnums == NULL) + return -ENOMEM; + + (*sysnums)[0].value = PR_void; + } + + for (i = 0; new_sysnums[i].value != PR_void; i++) { + /* Search for the given sysnum. */ + for (j = 0; (*sysnums)[j].value != PR_void + && (*sysnums)[j].value != new_sysnums[i].value; j++) + ; + + if ((*sysnums)[j].value == PR_void) { + /* No such sysnum, allocate a new entry. */ + (*sysnums) = talloc_realloc(context, (*sysnums), FilteredSysnum, j + 2); + if ((*sysnums) == NULL) + return -ENOMEM; + + (*sysnums)[j] = new_sysnums[i]; + + /* The last item is the terminator. */ + (*sysnums)[j + 1].value = PR_void; + } + else + /* The sysnum is already filtered, merge the + * flags. */ + (*sysnums)[j].flags |= new_sysnums[i].flags; + } + + return 0; +} + +/** + * 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) +{ + FilteredSysnum *filtered_sysnums = NULL; + Extension *extension; + int status; + + assert(tracee != NULL && tracee->ctx != NULL); + + /* Add the sysnums required by PRoot to the list of filtered + * sysnums. TODO: only if path translation is required. */ + status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, proot_sysnums); + if (status < 0) + return status; + + /* Merge the sysnums required by the extensions to the list + * of filtered sysnums. */ + if (tracee->extensions != NULL) { + LIST_FOREACH(extension, tracee->extensions, link) { + if (extension->filtered_sysnums == NULL) + continue; + + status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, + extension->filtered_sysnums); + if (status < 0) + return status; + } + } + + status = set_seccomp_filters(filtered_sysnums); + if (status < 0) + return status; + + return 0; +} + +#else + +#include "tracee/tracee.h" +#include "attribute.h" + +int enable_syscall_filtering(const Tracee *tracee UNUSED) +{ + return 0; +} + +#endif /* defined(HAVE_SECCOMP_FILTER) */
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-arm.h b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-arm.h new file mode 100644 index 0000000..d779327 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-arm.h
@@ -0,0 +1,338 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_arm[] = { + [ 0 ] = PR_restart_syscall, + [ 1 ] = PR_exit, + [ 2 ] = PR_fork, + [ 3 ] = PR_read, + [ 4 ] = PR_write, + [ 5 ] = PR_open, + [ 6 ] = PR_close, + [ 8 ] = PR_creat, + [ 9 ] = PR_link, + [ 10 ] = PR_unlink, + [ 11 ] = PR_execve, + [ 12 ] = PR_chdir, + [ 14 ] = PR_mknod, + [ 15 ] = PR_chmod, + [ 16 ] = PR_lchown, + [ 19 ] = PR_lseek, + [ 20 ] = PR_getpid, + [ 21 ] = PR_mount, + [ 23 ] = PR_setuid, + [ 24 ] = PR_getuid, + [ 26 ] = PR_ptrace, + [ 29 ] = PR_pause, + [ 33 ] = PR_access, + [ 34 ] = PR_nice, + [ 36 ] = PR_sync, + [ 37 ] = PR_kill, + [ 38 ] = PR_rename, + [ 39 ] = PR_mkdir, + [ 40 ] = PR_rmdir, + [ 41 ] = PR_dup, + [ 42 ] = PR_pipe, + [ 43 ] = PR_times, + [ 45 ] = PR_brk, + [ 46 ] = PR_setgid, + [ 47 ] = PR_getgid, + [ 49 ] = PR_geteuid, + [ 50 ] = PR_getegid, + [ 51 ] = PR_acct, + [ 52 ] = PR_umount2, + [ 54 ] = PR_ioctl, + [ 55 ] = PR_fcntl, + [ 57 ] = PR_setpgid, + [ 60 ] = PR_umask, + [ 61 ] = PR_chroot, + [ 62 ] = PR_ustat, + [ 63 ] = PR_dup2, + [ 64 ] = PR_getppid, + [ 65 ] = PR_getpgrp, + [ 66 ] = PR_setsid, + [ 67 ] = PR_sigaction, + [ 70 ] = PR_setreuid, + [ 71 ] = PR_setregid, + [ 72 ] = PR_sigsuspend, + [ 73 ] = PR_sigpending, + [ 74 ] = PR_sethostname, + [ 75 ] = PR_setrlimit, + [ 77 ] = PR_getrusage, + [ 78 ] = PR_gettimeofday, + [ 79 ] = PR_settimeofday, + [ 80 ] = PR_getgroups, + [ 81 ] = PR_setgroups, + [ 83 ] = PR_symlink, + [ 85 ] = PR_readlink, + [ 86 ] = PR_uselib, + [ 87 ] = PR_swapon, + [ 88 ] = PR_reboot, + [ 91 ] = PR_munmap, + [ 92 ] = PR_truncate, + [ 93 ] = PR_ftruncate, + [ 94 ] = PR_fchmod, + [ 95 ] = PR_fchown, + [ 96 ] = PR_getpriority, + [ 97 ] = PR_setpriority, + [ 99 ] = PR_statfs, + [ 100 ] = PR_fstatfs, + [ 103 ] = PR_syslog, + [ 104 ] = PR_setitimer, + [ 105 ] = PR_getitimer, + [ 106 ] = PR_stat, + [ 107 ] = PR_lstat, + [ 108 ] = PR_fstat, + [ 111 ] = PR_vhangup, + [ 114 ] = PR_wait4, + [ 115 ] = PR_swapoff, + [ 116 ] = PR_sysinfo, + [ 118 ] = PR_fsync, + [ 119 ] = PR_sigreturn, + [ 120 ] = PR_clone, + [ 121 ] = PR_setdomainname, + [ 122 ] = PR_uname, + [ 124 ] = PR_adjtimex, + [ 125 ] = PR_mprotect, + [ 126 ] = PR_sigprocmask, + [ 128 ] = PR_init_module, + [ 129 ] = PR_delete_module, + [ 131 ] = PR_quotactl, + [ 132 ] = PR_getpgid, + [ 133 ] = PR_fchdir, + [ 134 ] = PR_bdflush, + [ 135 ] = PR_sysfs, + [ 136 ] = PR_personality, + [ 138 ] = PR_setfsuid, + [ 139 ] = PR_setfsgid, + [ 140 ] = PR__llseek, + [ 141 ] = PR_getdents, + [ 142 ] = PR__newselect, + [ 143 ] = PR_flock, + [ 144 ] = PR_msync, + [ 145 ] = PR_readv, + [ 146 ] = PR_writev, + [ 147 ] = PR_getsid, + [ 148 ] = PR_fdatasync, + [ 149 ] = PR__sysctl, + [ 150 ] = PR_mlock, + [ 151 ] = PR_munlock, + [ 152 ] = PR_mlockall, + [ 153 ] = PR_munlockall, + [ 154 ] = PR_sched_setparam, + [ 155 ] = PR_sched_getparam, + [ 156 ] = PR_sched_setscheduler, + [ 157 ] = PR_sched_getscheduler, + [ 158 ] = PR_sched_yield, + [ 159 ] = PR_sched_get_priority_max, + [ 160 ] = PR_sched_get_priority_min, + [ 161 ] = PR_sched_rr_get_interval, + [ 162 ] = PR_nanosleep, + [ 163 ] = PR_mremap, + [ 164 ] = PR_setresuid, + [ 165 ] = PR_getresuid, + [ 168 ] = PR_poll, + [ 169 ] = PR_nfsservctl, + [ 170 ] = PR_setresgid, + [ 171 ] = PR_getresgid, + [ 172 ] = PR_prctl, + [ 173 ] = PR_rt_sigreturn, + [ 174 ] = PR_rt_sigaction, + [ 175 ] = PR_rt_sigprocmask, + [ 176 ] = PR_rt_sigpending, + [ 177 ] = PR_rt_sigtimedwait, + [ 178 ] = PR_rt_sigqueueinfo, + [ 179 ] = PR_rt_sigsuspend, + [ 180 ] = PR_pread64, + [ 181 ] = PR_pwrite64, + [ 182 ] = PR_chown, + [ 183 ] = PR_getcwd, + [ 184 ] = PR_capget, + [ 185 ] = PR_capset, + [ 186 ] = PR_sigaltstack, + [ 187 ] = PR_sendfile, + [ 190 ] = PR_vfork, + [ 191 ] = PR_ugetrlimit, + [ 192 ] = PR_mmap2, + [ 193 ] = PR_truncate64, + [ 194 ] = PR_ftruncate64, + [ 195 ] = PR_stat64, + [ 196 ] = PR_lstat64, + [ 197 ] = PR_fstat64, + [ 198 ] = PR_lchown32, + [ 199 ] = PR_getuid32, + [ 200 ] = PR_getgid32, + [ 201 ] = PR_geteuid32, + [ 202 ] = PR_getegid32, + [ 203 ] = PR_setreuid32, + [ 204 ] = PR_setregid32, + [ 205 ] = PR_getgroups32, + [ 206 ] = PR_setgroups32, + [ 207 ] = PR_fchown32, + [ 208 ] = PR_setresuid32, + [ 209 ] = PR_getresuid32, + [ 210 ] = PR_setresgid32, + [ 211 ] = PR_getresgid32, + [ 212 ] = PR_chown32, + [ 213 ] = PR_setuid32, + [ 214 ] = PR_setgid32, + [ 215 ] = PR_setfsuid32, + [ 216 ] = PR_setfsgid32, + [ 217 ] = PR_getdents64, + [ 218 ] = PR_pivot_root, + [ 219 ] = PR_mincore, + [ 220 ] = PR_madvise, + [ 221 ] = PR_fcntl64, + [ 222 ] = PR_void, + [ 224 ] = PR_gettid, + [ 225 ] = PR_readahead, + [ 226 ] = PR_setxattr, + [ 227 ] = PR_lsetxattr, + [ 228 ] = PR_fsetxattr, + [ 229 ] = PR_getxattr, + [ 230 ] = PR_lgetxattr, + [ 231 ] = PR_fgetxattr, + [ 232 ] = PR_listxattr, + [ 233 ] = PR_llistxattr, + [ 234 ] = PR_flistxattr, + [ 235 ] = PR_removexattr, + [ 236 ] = PR_lremovexattr, + [ 237 ] = PR_fremovexattr, + [ 238 ] = PR_tkill, + [ 239 ] = PR_sendfile64, + [ 240 ] = PR_futex, + [ 241 ] = PR_sched_setaffinity, + [ 242 ] = PR_sched_getaffinity, + [ 243 ] = PR_io_setup, + [ 244 ] = PR_io_destroy, + [ 245 ] = PR_io_getevents, + [ 246 ] = PR_io_submit, + [ 247 ] = PR_io_cancel, + [ 248 ] = PR_exit_group, + [ 249 ] = PR_lookup_dcookie, + [ 250 ] = PR_epoll_create, + [ 251 ] = PR_epoll_ctl, + [ 252 ] = PR_epoll_wait, + [ 253 ] = PR_remap_file_pages, + [ 256 ] = PR_set_tid_address, + [ 257 ] = PR_timer_create, + [ 258 ] = PR_timer_settime, + [ 259 ] = PR_timer_gettime, + [ 260 ] = PR_timer_getoverrun, + [ 261 ] = PR_timer_delete, + [ 262 ] = PR_clock_settime, + [ 263 ] = PR_clock_gettime, + [ 264 ] = PR_clock_getres, + [ 265 ] = PR_clock_nanosleep, + [ 266 ] = PR_statfs64, + [ 267 ] = PR_fstatfs64, + [ 268 ] = PR_tgkill, + [ 269 ] = PR_utimes, + [ 270 ] = PR_arm_fadvise64_64, + [ 271 ] = PR_pciconfig_iobase, + [ 272 ] = PR_pciconfig_read, + [ 273 ] = PR_pciconfig_write, + [ 274 ] = PR_mq_open, + [ 275 ] = PR_mq_unlink, + [ 276 ] = PR_mq_timedsend, + [ 277 ] = PR_mq_timedreceive, + [ 278 ] = PR_mq_notify, + [ 279 ] = PR_mq_getsetattr, + [ 280 ] = PR_waitid, + [ 281 ] = PR_socket, + [ 282 ] = PR_bind, + [ 283 ] = PR_connect, + [ 284 ] = PR_listen, + [ 285 ] = PR_accept, + [ 286 ] = PR_getsockname, + [ 287 ] = PR_getpeername, + [ 288 ] = PR_socketpair, + [ 289 ] = PR_send, + [ 290 ] = PR_sendto, + [ 291 ] = PR_recv, + [ 292 ] = PR_recvfrom, + [ 293 ] = PR_shutdown, + [ 294 ] = PR_setsockopt, + [ 295 ] = PR_getsockopt, + [ 296 ] = PR_sendmsg, + [ 297 ] = PR_recvmsg, + [ 298 ] = PR_semop, + [ 299 ] = PR_semget, + [ 300 ] = PR_semctl, + [ 301 ] = PR_msgsnd, + [ 302 ] = PR_msgrcv, + [ 303 ] = PR_msgget, + [ 304 ] = PR_msgctl, + [ 305 ] = PR_shmat, + [ 306 ] = PR_shmdt, + [ 307 ] = PR_shmget, + [ 308 ] = PR_shmctl, + [ 309 ] = PR_add_key, + [ 310 ] = PR_request_key, + [ 311 ] = PR_keyctl, + [ 312 ] = PR_semtimedop, + [ 313 ] = PR_vserver, + [ 314 ] = PR_ioprio_set, + [ 315 ] = PR_ioprio_get, + [ 316 ] = PR_inotify_init, + [ 317 ] = PR_inotify_add_watch, + [ 318 ] = PR_inotify_rm_watch, + [ 319 ] = PR_mbind, + [ 320 ] = PR_get_mempolicy, + [ 321 ] = PR_set_mempolicy, + [ 322 ] = PR_openat, + [ 323 ] = PR_mkdirat, + [ 324 ] = PR_mknodat, + [ 325 ] = PR_fchownat, + [ 326 ] = PR_futimesat, + [ 327 ] = PR_fstatat64, + [ 328 ] = PR_unlinkat, + [ 329 ] = PR_renameat, + [ 330 ] = PR_linkat, + [ 331 ] = PR_symlinkat, + [ 332 ] = PR_readlinkat, + [ 333 ] = PR_fchmodat, + [ 334 ] = PR_faccessat, + [ 335 ] = PR_pselect6, + [ 336 ] = PR_ppoll, + [ 337 ] = PR_unshare, + [ 338 ] = PR_set_robust_list, + [ 339 ] = PR_get_robust_list, + [ 340 ] = PR_splice, + [ 341 ] = PR_arm_sync_file_range, + [ 342 ] = PR_tee, + [ 343 ] = PR_vmsplice, + [ 344 ] = PR_move_pages, + [ 345 ] = PR_getcpu, + [ 346 ] = PR_epoll_pwait, + [ 347 ] = PR_kexec_load, + [ 348 ] = PR_utimensat, + [ 349 ] = PR_signalfd, + [ 350 ] = PR_timerfd_create, + [ 351 ] = PR_eventfd, + [ 352 ] = PR_fallocate, + [ 353 ] = PR_timerfd_settime, + [ 354 ] = PR_timerfd_gettime, + [ 355 ] = PR_signalfd4, + [ 356 ] = PR_eventfd2, + [ 357 ] = PR_epoll_create1, + [ 358 ] = PR_dup3, + [ 359 ] = PR_pipe2, + [ 360 ] = PR_inotify_init1, + [ 361 ] = PR_preadv, + [ 362 ] = PR_pwritev, + [ 363 ] = PR_rt_tgsigqueueinfo, + [ 364 ] = PR_perf_event_open, + [ 365 ] = PR_recvmmsg, + [ 366 ] = PR_accept4, + [ 367 ] = PR_fanotify_init, + [ 368 ] = PR_fanotify_mark, + [ 369 ] = PR_prlimit64, + [ 370 ] = PR_name_to_handle_at, + [ 371 ] = PR_open_by_handle_at, + [ 372 ] = PR_clock_adjtime, + [ 373 ] = PR_syncfs, + [ 374 ] = PR_sendmmsg, + [ 375 ] = PR_setns, + [ 376 ] = PR_process_vm_readv, + [ 377 ] = PR_process_vm_writev, +};
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-arm64.h b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-arm64.h new file mode 100644 index 0000000..9476c53 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-arm64.h
@@ -0,0 +1,263 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_arm64[] = { + [ 0 ] = PR_io_setup, + [ 1 ] = PR_io_destroy, + [ 2 ] = PR_io_submit, + [ 3 ] = PR_io_cancel, + [ 4 ] = PR_io_getevents, + [ 5 ] = PR_setxattr, + [ 6 ] = PR_lsetxattr, + [ 7 ] = PR_fsetxattr, + [ 8 ] = PR_getxattr, + [ 9 ] = PR_lgetxattr, + [ 10 ] = PR_fgetxattr, + [ 11 ] = PR_listxattr, + [ 12 ] = PR_llistxattr, + [ 13 ] = PR_flistxattr, + [ 14 ] = PR_removexattr, + [ 15 ] = PR_lremovexattr, + [ 16 ] = PR_fremovexattr, + [ 17 ] = PR_getcwd, + [ 18 ] = PR_lookup_dcookie, + [ 19 ] = PR_eventfd2, + [ 20 ] = PR_epoll_create1, + [ 21 ] = PR_epoll_ctl, + [ 22 ] = PR_epoll_pwait, + [ 23 ] = PR_dup, + [ 24 ] = PR_dup3, + [ 25 ] = PR_fcntl, + [ 26 ] = PR_inotify_init1, + [ 27 ] = PR_inotify_add_watch, + [ 28 ] = PR_inotify_rm_watch, + [ 29 ] = PR_ioctl, + [ 30 ] = PR_ioprio_set, + [ 31 ] = PR_ioprio_get, + [ 32 ] = PR_flock, + [ 33 ] = PR_mknodat, + [ 34 ] = PR_mkdirat, + [ 35 ] = PR_unlinkat, + [ 36 ] = PR_symlinkat, + [ 37 ] = PR_linkat, + [ 38 ] = PR_renameat, + [ 39 ] = PR_umount2, + [ 40 ] = PR_mount, + [ 41 ] = PR_pivot_root, + [ 42 ] = PR_nfsservctl, + [ 43 ] = PR_statfs, + [ 44 ] = PR_fstatfs, + [ 45 ] = PR_truncate, + [ 46 ] = PR_ftruncate, + [ 47 ] = PR_fallocate, + [ 48 ] = PR_faccessat, + [ 49 ] = PR_chdir, + [ 50 ] = PR_fchdir, + [ 51 ] = PR_chroot, + [ 52 ] = PR_fchmod, + [ 53 ] = PR_fchmodat, + [ 54 ] = PR_fchownat, + [ 55 ] = PR_fchown, + [ 56 ] = PR_openat, + [ 57 ] = PR_close, + [ 58 ] = PR_vhangup, + [ 59 ] = PR_pipe2, + [ 60 ] = PR_quotactl, + [ 61 ] = PR_getdents64, + [ 62 ] = PR_lseek, + [ 63 ] = PR_read, + [ 64 ] = PR_write, + [ 65 ] = PR_readv, + [ 66 ] = PR_writev, + [ 67 ] = PR_pread64, + [ 68 ] = PR_pwrite64, + [ 69 ] = PR_preadv, + [ 70 ] = PR_pwritev, + [ 71 ] = PR_sendfile, + [ 72 ] = PR_pselect6, + [ 73 ] = PR_ppoll, + [ 74 ] = PR_signalfd4, + [ 75 ] = PR_vmsplice, + [ 76 ] = PR_splice, + [ 77 ] = PR_tee, + [ 78 ] = PR_readlinkat, + [ 79 ] = PR_fstatat64, + [ 80 ] = PR_fstat, + [ 81 ] = PR_sync, + [ 82 ] = PR_fsync, + [ 83 ] = PR_fdatasync, + [ 84 ] = PR_sync_file_range, + [ 85 ] = PR_timerfd_create, + [ 86 ] = PR_timerfd_settime, + [ 87 ] = PR_timerfd_gettime, + [ 88 ] = PR_utimensat, + [ 89 ] = PR_acct, + [ 90 ] = PR_capget, + [ 91 ] = PR_capset, + [ 92 ] = PR_personality, + [ 93 ] = PR_exit, + [ 94 ] = PR_exit_group, + [ 95 ] = PR_waitid, + [ 96 ] = PR_set_tid_address, + [ 97 ] = PR_unshare, + [ 98 ] = PR_futex, + [ 99 ] = PR_set_robust_list, + [ 100 ] = PR_get_robust_list, + [ 101 ] = PR_nanosleep, + [ 102 ] = PR_getitimer, + [ 103 ] = PR_setitimer, + [ 104 ] = PR_kexec_load, + [ 105 ] = PR_init_module, + [ 106 ] = PR_delete_module, + [ 107 ] = PR_timer_create, + [ 108 ] = PR_timer_gettime, + [ 109 ] = PR_timer_getoverrun, + [ 110 ] = PR_timer_settime, + [ 111 ] = PR_timer_delete, + [ 112 ] = PR_clock_settime, + [ 113 ] = PR_clock_gettime, + [ 114 ] = PR_clock_getres, + [ 115 ] = PR_clock_nanosleep, + [ 116 ] = PR_syslog, + [ 117 ] = PR_ptrace, + [ 118 ] = PR_sched_setparam, + [ 119 ] = PR_sched_setscheduler, + [ 120 ] = PR_sched_getscheduler, + [ 121 ] = PR_sched_getparam, + [ 122 ] = PR_sched_setaffinity, + [ 123 ] = PR_sched_getaffinity, + [ 124 ] = PR_sched_yield, + [ 125 ] = PR_sched_get_priority_max, + [ 126 ] = PR_sched_get_priority_min, + [ 127 ] = PR_sched_rr_get_interval, + [ 128 ] = PR_restart_syscall, + [ 129 ] = PR_kill, + [ 130 ] = PR_tkill, + [ 131 ] = PR_tgkill, + [ 132 ] = PR_sigaltstack, + [ 133 ] = PR_rt_sigsuspend, + [ 134 ] = PR_rt_sigaction, + [ 135 ] = PR_rt_sigprocmask, + [ 136 ] = PR_rt_sigpending, + [ 137 ] = PR_rt_sigtimedwait, + [ 138 ] = PR_rt_sigqueueinfo, + [ 139 ] = PR_rt_sigreturn, + [ 140 ] = PR_setpriority, + [ 141 ] = PR_getpriority, + [ 142 ] = PR_reboot, + [ 143 ] = PR_setregid, + [ 144 ] = PR_setgid, + [ 145 ] = PR_setreuid, + [ 146 ] = PR_setuid, + [ 147 ] = PR_setresuid, + [ 148 ] = PR_getresuid, + [ 149 ] = PR_setresgid, + [ 150 ] = PR_getresgid, + [ 151 ] = PR_setfsuid, + [ 152 ] = PR_setfsgid, + [ 153 ] = PR_times, + [ 154 ] = PR_setpgid, + [ 155 ] = PR_getpgid, + [ 156 ] = PR_getsid, + [ 157 ] = PR_setsid, + [ 158 ] = PR_getgroups, + [ 159 ] = PR_setgroups, + [ 160 ] = PR_uname, + [ 161 ] = PR_sethostname, + [ 162 ] = PR_setdomainname, + [ 163 ] = PR_getrlimit, + [ 164 ] = PR_setrlimit, + [ 165 ] = PR_getrusage, + [ 166 ] = PR_umask, + [ 167 ] = PR_prctl, + [ 168 ] = PR_getcpu, + [ 169 ] = PR_gettimeofday, + [ 170 ] = PR_settimeofday, + [ 171 ] = PR_adjtimex, + [ 172 ] = PR_getpid, + [ 173 ] = PR_getppid, + [ 174 ] = PR_getuid, + [ 175 ] = PR_geteuid, + [ 176 ] = PR_getgid, + [ 177 ] = PR_getegid, + [ 178 ] = PR_gettid, + [ 179 ] = PR_sysinfo, + [ 180 ] = PR_mq_open, + [ 181 ] = PR_mq_unlink, + [ 182 ] = PR_mq_timedsend, + [ 183 ] = PR_mq_timedreceive, + [ 184 ] = PR_mq_notify, + [ 185 ] = PR_mq_getsetattr, + [ 186 ] = PR_msgget, + [ 187 ] = PR_msgctl, + [ 188 ] = PR_msgrcv, + [ 189 ] = PR_msgsnd, + [ 190 ] = PR_semget, + [ 191 ] = PR_semctl, + [ 192 ] = PR_semtimedop, + [ 193 ] = PR_semop, + [ 194 ] = PR_shmget, + [ 195 ] = PR_shmctl, + [ 196 ] = PR_shmat, + [ 197 ] = PR_shmdt, + [ 198 ] = PR_socket, + [ 199 ] = PR_socketpair, + [ 200 ] = PR_bind, + [ 201 ] = PR_listen, + [ 202 ] = PR_accept, + [ 203 ] = PR_connect, + [ 204 ] = PR_getsockname, + [ 205 ] = PR_getpeername, + [ 206 ] = PR_sendto, + [ 207 ] = PR_recvfrom, + [ 208 ] = PR_setsockopt, + [ 209 ] = PR_getsockopt, + [ 210 ] = PR_shutdown, + [ 211 ] = PR_sendmsg, + [ 212 ] = PR_recvmsg, + [ 213 ] = PR_readahead, + [ 214 ] = PR_brk, + [ 215 ] = PR_munmap, + [ 216 ] = PR_mremap, + [ 217 ] = PR_add_key, + [ 218 ] = PR_request_key, + [ 219 ] = PR_keyctl, + [ 220 ] = PR_clone, + [ 221 ] = PR_execve, + [ 222 ] = PR_mmap, + [ 223 ] = PR_fadvise64, + [ 224 ] = PR_swapon, + [ 225 ] = PR_swapoff, + [ 226 ] = PR_mprotect, + [ 227 ] = PR_msync, + [ 228 ] = PR_mlock, + [ 229 ] = PR_munlock, + [ 230 ] = PR_mlockall, + [ 231 ] = PR_munlockall, + [ 232 ] = PR_mincore, + [ 233 ] = PR_madvise, + [ 234 ] = PR_remap_file_pages, + [ 235 ] = PR_mbind, + [ 236 ] = PR_get_mempolicy, + [ 237 ] = PR_set_mempolicy, + [ 238 ] = PR_migrate_pages, + [ 239 ] = PR_move_pages, + [ 240 ] = PR_rt_tgsigqueueinfo, + [ 241 ] = PR_perf_event_open, + [ 242 ] = PR_accept4, + [ 243 ] = PR_recvmmsg, + [ 244 ] = PR_arch_specific_syscall, + [ 260 ] = PR_wait4, + [ 261 ] = PR_prlimit64, + [ 262 ] = PR_fanotify_init, + [ 263 ] = PR_fanotify_mark, + [ 264 ] = PR_name_to_handle_at, + [ 265 ] = PR_open_by_handle_at, + [ 266 ] = PR_clock_adjtime, + [ 267 ] = PR_syncfs, + [ 268 ] = PR_setns, + [ 269 ] = PR_sendmmsg, + [ 270 ] = PR_process_vm_readv, + [ 271 ] = PR_process_vm_writev, + [ 272 ] = PR_kcmp, + [ 273 ] = PR_syscalls, +};
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-i386.h b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-i386.h new file mode 100644 index 0000000..9f2d88b --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-i386.h
@@ -0,0 +1,350 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_i386[] = { + [ 0 ] = PR_restart_syscall, + [ 1 ] = PR_exit, + [ 2 ] = PR_fork, + [ 3 ] = PR_read, + [ 4 ] = PR_write, + [ 5 ] = PR_open, + [ 6 ] = PR_close, + [ 7 ] = PR_waitpid, + [ 8 ] = PR_creat, + [ 9 ] = PR_link, + [ 10 ] = PR_unlink, + [ 11 ] = PR_execve, + [ 12 ] = PR_chdir, + [ 13 ] = PR_time, + [ 14 ] = PR_mknod, + [ 15 ] = PR_chmod, + [ 16 ] = PR_lchown, + [ 17 ] = PR_break, + [ 18 ] = PR_oldstat, + [ 19 ] = PR_lseek, + [ 20 ] = PR_getpid, + [ 21 ] = PR_mount, + [ 22 ] = PR_umount, + [ 23 ] = PR_setuid, + [ 24 ] = PR_getuid, + [ 25 ] = PR_stime, + [ 26 ] = PR_ptrace, + [ 27 ] = PR_alarm, + [ 28 ] = PR_oldfstat, + [ 29 ] = PR_pause, + [ 30 ] = PR_utime, + [ 31 ] = PR_stty, + [ 32 ] = PR_gtty, + [ 33 ] = PR_access, + [ 34 ] = PR_nice, + [ 35 ] = PR_ftime, + [ 36 ] = PR_sync, + [ 37 ] = PR_kill, + [ 38 ] = PR_rename, + [ 39 ] = PR_mkdir, + [ 40 ] = PR_rmdir, + [ 41 ] = PR_dup, + [ 42 ] = PR_pipe, + [ 43 ] = PR_times, + [ 44 ] = PR_prof, + [ 45 ] = PR_brk, + [ 46 ] = PR_setgid, + [ 47 ] = PR_getgid, + [ 48 ] = PR_signal, + [ 49 ] = PR_geteuid, + [ 50 ] = PR_getegid, + [ 51 ] = PR_acct, + [ 52 ] = PR_umount2, + [ 53 ] = PR_lock, + [ 54 ] = PR_ioctl, + [ 55 ] = PR_fcntl, + [ 56 ] = PR_mpx, + [ 57 ] = PR_setpgid, + [ 58 ] = PR_ulimit, + [ 59 ] = PR_oldolduname, + [ 60 ] = PR_umask, + [ 61 ] = PR_chroot, + [ 62 ] = PR_ustat, + [ 63 ] = PR_dup2, + [ 64 ] = PR_getppid, + [ 65 ] = PR_getpgrp, + [ 66 ] = PR_setsid, + [ 67 ] = PR_sigaction, + [ 68 ] = PR_sgetmask, + [ 69 ] = PR_ssetmask, + [ 70 ] = PR_setreuid, + [ 71 ] = PR_setregid, + [ 72 ] = PR_sigsuspend, + [ 73 ] = PR_sigpending, + [ 74 ] = PR_sethostname, + [ 75 ] = PR_setrlimit, + [ 76 ] = PR_getrlimit, + [ 77 ] = PR_getrusage, + [ 78 ] = PR_gettimeofday, + [ 79 ] = PR_settimeofday, + [ 80 ] = PR_getgroups, + [ 81 ] = PR_setgroups, + [ 82 ] = PR_select, + [ 83 ] = PR_symlink, + [ 84 ] = PR_oldlstat, + [ 85 ] = PR_readlink, + [ 86 ] = PR_uselib, + [ 87 ] = PR_swapon, + [ 88 ] = PR_reboot, + [ 89 ] = PR_readdir, + [ 90 ] = PR_mmap, + [ 91 ] = PR_munmap, + [ 92 ] = PR_truncate, + [ 93 ] = PR_ftruncate, + [ 94 ] = PR_fchmod, + [ 95 ] = PR_fchown, + [ 96 ] = PR_getpriority, + [ 97 ] = PR_setpriority, + [ 98 ] = PR_profil, + [ 99 ] = PR_statfs, + [ 100 ] = PR_fstatfs, + [ 101 ] = PR_ioperm, + [ 102 ] = PR_socketcall, + [ 103 ] = PR_syslog, + [ 104 ] = PR_setitimer, + [ 105 ] = PR_getitimer, + [ 106 ] = PR_stat, + [ 107 ] = PR_lstat, + [ 108 ] = PR_fstat, + [ 109 ] = PR_olduname, + [ 110 ] = PR_iopl, + [ 111 ] = PR_vhangup, + [ 112 ] = PR_idle, + [ 113 ] = PR_vm86old, + [ 114 ] = PR_wait4, + [ 115 ] = PR_swapoff, + [ 116 ] = PR_sysinfo, + [ 117 ] = PR_ipc, + [ 118 ] = PR_fsync, + [ 119 ] = PR_sigreturn, + [ 120 ] = PR_clone, + [ 121 ] = PR_setdomainname, + [ 122 ] = PR_uname, + [ 123 ] = PR_modify_ldt, + [ 124 ] = PR_adjtimex, + [ 125 ] = PR_mprotect, + [ 126 ] = PR_sigprocmask, + [ 127 ] = PR_create_module, + [ 128 ] = PR_init_module, + [ 129 ] = PR_delete_module, + [ 130 ] = PR_get_kernel_syms, + [ 131 ] = PR_quotactl, + [ 132 ] = PR_getpgid, + [ 133 ] = PR_fchdir, + [ 134 ] = PR_bdflush, + [ 135 ] = PR_sysfs, + [ 136 ] = PR_personality, + [ 137 ] = PR_afs_syscall, + [ 138 ] = PR_setfsuid, + [ 139 ] = PR_setfsgid, + [ 140 ] = PR__llseek, + [ 141 ] = PR_getdents, + [ 142 ] = PR__newselect, + [ 143 ] = PR_flock, + [ 144 ] = PR_msync, + [ 145 ] = PR_readv, + [ 146 ] = PR_writev, + [ 147 ] = PR_getsid, + [ 148 ] = PR_fdatasync, + [ 149 ] = PR__sysctl, + [ 150 ] = PR_mlock, + [ 151 ] = PR_munlock, + [ 152 ] = PR_mlockall, + [ 153 ] = PR_munlockall, + [ 154 ] = PR_sched_setparam, + [ 155 ] = PR_sched_getparam, + [ 156 ] = PR_sched_setscheduler, + [ 157 ] = PR_sched_getscheduler, + [ 158 ] = PR_sched_yield, + [ 159 ] = PR_sched_get_priority_max, + [ 160 ] = PR_sched_get_priority_min, + [ 161 ] = PR_sched_rr_get_interval, + [ 162 ] = PR_nanosleep, + [ 163 ] = PR_mremap, + [ 164 ] = PR_setresuid, + [ 165 ] = PR_getresuid, + [ 166 ] = PR_vm86, + [ 167 ] = PR_query_module, + [ 168 ] = PR_poll, + [ 169 ] = PR_nfsservctl, + [ 170 ] = PR_setresgid, + [ 171 ] = PR_getresgid, + [ 172 ] = PR_prctl, + [ 173 ] = PR_rt_sigreturn, + [ 174 ] = PR_rt_sigaction, + [ 175 ] = PR_rt_sigprocmask, + [ 176 ] = PR_rt_sigpending, + [ 177 ] = PR_rt_sigtimedwait, + [ 178 ] = PR_rt_sigqueueinfo, + [ 179 ] = PR_rt_sigsuspend, + [ 180 ] = PR_pread64, + [ 181 ] = PR_pwrite64, + [ 182 ] = PR_chown, + [ 183 ] = PR_getcwd, + [ 184 ] = PR_capget, + [ 185 ] = PR_capset, + [ 186 ] = PR_sigaltstack, + [ 187 ] = PR_sendfile, + [ 188 ] = PR_getpmsg, + [ 189 ] = PR_putpmsg, + [ 190 ] = PR_vfork, + [ 191 ] = PR_ugetrlimit, + [ 192 ] = PR_mmap2, + [ 193 ] = PR_truncate64, + [ 194 ] = PR_ftruncate64, + [ 195 ] = PR_stat64, + [ 196 ] = PR_lstat64, + [ 197 ] = PR_fstat64, + [ 198 ] = PR_lchown32, + [ 199 ] = PR_getuid32, + [ 200 ] = PR_getgid32, + [ 201 ] = PR_geteuid32, + [ 202 ] = PR_getegid32, + [ 203 ] = PR_setreuid32, + [ 204 ] = PR_setregid32, + [ 205 ] = PR_getgroups32, + [ 206 ] = PR_setgroups32, + [ 207 ] = PR_fchown32, + [ 208 ] = PR_setresuid32, + [ 209 ] = PR_getresuid32, + [ 210 ] = PR_setresgid32, + [ 211 ] = PR_getresgid32, + [ 212 ] = PR_chown32, + [ 213 ] = PR_setuid32, + [ 214 ] = PR_setgid32, + [ 215 ] = PR_setfsuid32, + [ 216 ] = PR_setfsgid32, + [ 217 ] = PR_pivot_root, + [ 218 ] = PR_mincore, + [ 219 ] = PR_madvise, + [ 220 ] = PR_getdents64, + [ 221 ] = PR_fcntl64, + [ 224 ] = PR_gettid, + [ 225 ] = PR_readahead, + [ 226 ] = PR_setxattr, + [ 227 ] = PR_lsetxattr, + [ 228 ] = PR_fsetxattr, + [ 229 ] = PR_getxattr, + [ 230 ] = PR_lgetxattr, + [ 231 ] = PR_fgetxattr, + [ 232 ] = PR_listxattr, + [ 233 ] = PR_llistxattr, + [ 234 ] = PR_flistxattr, + [ 235 ] = PR_removexattr, + [ 236 ] = PR_lremovexattr, + [ 237 ] = PR_fremovexattr, + [ 238 ] = PR_tkill, + [ 239 ] = PR_sendfile64, + [ 240 ] = PR_futex, + [ 241 ] = PR_sched_setaffinity, + [ 242 ] = PR_sched_getaffinity, + [ 243 ] = PR_set_thread_area, + [ 244 ] = PR_get_thread_area, + [ 245 ] = PR_io_setup, + [ 246 ] = PR_io_destroy, + [ 247 ] = PR_io_getevents, + [ 248 ] = PR_io_submit, + [ 249 ] = PR_io_cancel, + [ 250 ] = PR_fadvise64, + [ 252 ] = PR_exit_group, + [ 253 ] = PR_lookup_dcookie, + [ 254 ] = PR_epoll_create, + [ 255 ] = PR_epoll_ctl, + [ 256 ] = PR_epoll_wait, + [ 257 ] = PR_remap_file_pages, + [ 258 ] = PR_set_tid_address, + [ 259 ] = PR_timer_create, + [ 260 ] = PR_timer_settime, + [ 261 ] = PR_timer_gettime, + [ 262 ] = PR_timer_getoverrun, + [ 263 ] = PR_timer_delete, + [ 264 ] = PR_clock_settime, + [ 265 ] = PR_clock_gettime, + [ 266 ] = PR_clock_getres, + [ 267 ] = PR_clock_nanosleep, + [ 268 ] = PR_statfs64, + [ 269 ] = PR_fstatfs64, + [ 270 ] = PR_tgkill, + [ 271 ] = PR_utimes, + [ 272 ] = PR_fadvise64_64, + [ 273 ] = PR_vserver, + [ 274 ] = PR_mbind, + [ 275 ] = PR_get_mempolicy, + [ 276 ] = PR_set_mempolicy, + [ 277 ] = PR_mq_open, + [ 278 ] = PR_mq_unlink, + [ 279 ] = PR_mq_timedsend, + [ 280 ] = PR_mq_timedreceive, + [ 281 ] = PR_mq_notify, + [ 282 ] = PR_mq_getsetattr, + [ 283 ] = PR_kexec_load, + [ 284 ] = PR_waitid, + [ 286 ] = PR_add_key, + [ 287 ] = PR_request_key, + [ 288 ] = PR_keyctl, + [ 289 ] = PR_ioprio_set, + [ 290 ] = PR_ioprio_get, + [ 291 ] = PR_inotify_init, + [ 292 ] = PR_inotify_add_watch, + [ 293 ] = PR_inotify_rm_watch, + [ 294 ] = PR_migrate_pages, + [ 295 ] = PR_openat, + [ 296 ] = PR_mkdirat, + [ 297 ] = PR_mknodat, + [ 298 ] = PR_fchownat, + [ 299 ] = PR_futimesat, + [ 300 ] = PR_fstatat64, + [ 301 ] = PR_unlinkat, + [ 302 ] = PR_renameat, + [ 303 ] = PR_linkat, + [ 304 ] = PR_symlinkat, + [ 305 ] = PR_readlinkat, + [ 306 ] = PR_fchmodat, + [ 307 ] = PR_faccessat, + [ 308 ] = PR_pselect6, + [ 309 ] = PR_ppoll, + [ 310 ] = PR_unshare, + [ 311 ] = PR_set_robust_list, + [ 312 ] = PR_get_robust_list, + [ 313 ] = PR_splice, + [ 314 ] = PR_sync_file_range, + [ 315 ] = PR_tee, + [ 316 ] = PR_vmsplice, + [ 317 ] = PR_move_pages, + [ 318 ] = PR_getcpu, + [ 319 ] = PR_epoll_pwait, + [ 320 ] = PR_utimensat, + [ 321 ] = PR_signalfd, + [ 322 ] = PR_timerfd_create, + [ 323 ] = PR_eventfd, + [ 324 ] = PR_fallocate, + [ 325 ] = PR_timerfd_settime, + [ 326 ] = PR_timerfd_gettime, + [ 327 ] = PR_signalfd4, + [ 328 ] = PR_eventfd2, + [ 329 ] = PR_epoll_create1, + [ 330 ] = PR_dup3, + [ 331 ] = PR_pipe2, + [ 332 ] = PR_inotify_init1, + [ 333 ] = PR_preadv, + [ 334 ] = PR_pwritev, + [ 335 ] = PR_rt_tgsigqueueinfo, + [ 336 ] = PR_perf_event_open, + [ 337 ] = PR_recvmmsg, + [ 338 ] = PR_fanotify_init, + [ 339 ] = PR_fanotify_mark, + [ 340 ] = PR_prlimit64, + [ 341 ] = PR_name_to_handle_at, + [ 342 ] = PR_open_by_handle_at, + [ 343 ] = PR_clock_adjtime, + [ 344 ] = PR_syncfs, + [ 345 ] = PR_sendmmsg, + [ 346 ] = PR_setns, + [ 347 ] = PR_process_vm_readv, + [ 348 ] = PR_process_vm_writev, + [ 349 ] = PR_kcmp, +};
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-sh4.h b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-sh4.h new file mode 100644 index 0000000..8546839 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-sh4.h
@@ -0,0 +1,342 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_sh4[] = { + [ 0 ] = PR_restart_syscall, + [ 1 ] = PR_exit, + [ 2 ] = PR_fork, + [ 3 ] = PR_read, + [ 4 ] = PR_write, + [ 5 ] = PR_open, + [ 6 ] = PR_close, + [ 7 ] = PR_waitpid, + [ 8 ] = PR_creat, + [ 9 ] = PR_link, + [ 10 ] = PR_unlink, + [ 11 ] = PR_execve, + [ 12 ] = PR_chdir, + [ 13 ] = PR_time, + [ 14 ] = PR_mknod, + [ 15 ] = PR_chmod, + [ 16 ] = PR_lchown, + [ 18 ] = PR_oldstat, + [ 19 ] = PR_lseek, + [ 20 ] = PR_getpid, + [ 21 ] = PR_mount, + [ 22 ] = PR_umount, + [ 23 ] = PR_setuid, + [ 24 ] = PR_getuid, + [ 25 ] = PR_stime, + [ 26 ] = PR_ptrace, + [ 27 ] = PR_alarm, + [ 28 ] = PR_oldfstat, + [ 29 ] = PR_pause, + [ 30 ] = PR_utime, + [ 33 ] = PR_access, + [ 34 ] = PR_nice, + [ 36 ] = PR_sync, + [ 37 ] = PR_kill, + [ 38 ] = PR_rename, + [ 39 ] = PR_mkdir, + [ 40 ] = PR_rmdir, + [ 41 ] = PR_dup, + [ 42 ] = PR_pipe, + [ 43 ] = PR_times, + [ 45 ] = PR_brk, + [ 46 ] = PR_setgid, + [ 47 ] = PR_getgid, + [ 48 ] = PR_signal, + [ 49 ] = PR_geteuid, + [ 50 ] = PR_getegid, + [ 51 ] = PR_acct, + [ 52 ] = PR_umount2, + [ 54 ] = PR_ioctl, + [ 55 ] = PR_fcntl, + [ 57 ] = PR_setpgid, + [ 60 ] = PR_umask, + [ 61 ] = PR_chroot, + [ 62 ] = PR_ustat, + [ 63 ] = PR_dup2, + [ 64 ] = PR_getppid, + [ 65 ] = PR_getpgrp, + [ 66 ] = PR_setsid, + [ 67 ] = PR_sigaction, + [ 68 ] = PR_sgetmask, + [ 69 ] = PR_ssetmask, + [ 70 ] = PR_setreuid, + [ 71 ] = PR_setregid, + [ 72 ] = PR_sigsuspend, + [ 73 ] = PR_sigpending, + [ 74 ] = PR_sethostname, + [ 75 ] = PR_setrlimit, + [ 76 ] = PR_getrlimit, + [ 77 ] = PR_getrusage, + [ 78 ] = PR_gettimeofday, + [ 79 ] = PR_settimeofday, + [ 80 ] = PR_getgroups, + [ 81 ] = PR_setgroups, + [ 83 ] = PR_symlink, + [ 84 ] = PR_oldlstat, + [ 85 ] = PR_readlink, + [ 86 ] = PR_uselib, + [ 87 ] = PR_swapon, + [ 88 ] = PR_reboot, + [ 89 ] = PR_readdir, + [ 90 ] = PR_mmap, + [ 91 ] = PR_munmap, + [ 92 ] = PR_truncate, + [ 93 ] = PR_ftruncate, + [ 94 ] = PR_fchmod, + [ 95 ] = PR_fchown, + [ 96 ] = PR_getpriority, + [ 97 ] = PR_setpriority, + [ 99 ] = PR_statfs, + [ 100 ] = PR_fstatfs, + [ 102 ] = PR_socketcall, + [ 103 ] = PR_syslog, + [ 104 ] = PR_setitimer, + [ 105 ] = PR_getitimer, + [ 106 ] = PR_stat, + [ 107 ] = PR_lstat, + [ 108 ] = PR_fstat, + [ 109 ] = PR_olduname, + [ 111 ] = PR_vhangup, + [ 114 ] = PR_wait4, + [ 115 ] = PR_swapoff, + [ 116 ] = PR_sysinfo, + [ 117 ] = PR_ipc, + [ 118 ] = PR_fsync, + [ 119 ] = PR_sigreturn, + [ 120 ] = PR_clone, + [ 121 ] = PR_setdomainname, + [ 122 ] = PR_uname, + [ 123 ] = PR_cacheflush, + [ 124 ] = PR_adjtimex, + [ 125 ] = PR_mprotect, + [ 126 ] = PR_sigprocmask, + [ 128 ] = PR_init_module, + [ 129 ] = PR_delete_module, + [ 131 ] = PR_quotactl, + [ 132 ] = PR_getpgid, + [ 133 ] = PR_fchdir, + [ 134 ] = PR_bdflush, + [ 135 ] = PR_sysfs, + [ 136 ] = PR_personality, + [ 138 ] = PR_setfsuid, + [ 139 ] = PR_setfsgid, + [ 140 ] = PR__llseek, + [ 141 ] = PR_getdents, + [ 142 ] = PR__newselect, + [ 143 ] = PR_flock, + [ 144 ] = PR_msync, + [ 145 ] = PR_readv, + [ 146 ] = PR_writev, + [ 147 ] = PR_getsid, + [ 148 ] = PR_fdatasync, + [ 149 ] = PR__sysctl, + [ 150 ] = PR_mlock, + [ 151 ] = PR_munlock, + [ 152 ] = PR_mlockall, + [ 153 ] = PR_munlockall, + [ 154 ] = PR_sched_setparam, + [ 155 ] = PR_sched_getparam, + [ 156 ] = PR_sched_setscheduler, + [ 157 ] = PR_sched_getscheduler, + [ 158 ] = PR_sched_yield, + [ 159 ] = PR_sched_get_priority_max, + [ 160 ] = PR_sched_get_priority_min, + [ 161 ] = PR_sched_rr_get_interval, + [ 162 ] = PR_nanosleep, + [ 163 ] = PR_mremap, + [ 164 ] = PR_setresuid, + [ 165 ] = PR_getresuid, + [ 168 ] = PR_poll, + [ 169 ] = PR_nfsservctl, + [ 170 ] = PR_setresgid, + [ 171 ] = PR_getresgid, + [ 172 ] = PR_prctl, + [ 173 ] = PR_rt_sigreturn, + [ 174 ] = PR_rt_sigaction, + [ 175 ] = PR_rt_sigprocmask, + [ 176 ] = PR_rt_sigpending, + [ 177 ] = PR_rt_sigtimedwait, + [ 178 ] = PR_rt_sigqueueinfo, + [ 179 ] = PR_rt_sigsuspend, + [ 180 ] = PR_pread64, + [ 181 ] = PR_pwrite64, + [ 182 ] = PR_chown, + [ 183 ] = PR_getcwd, + [ 184 ] = PR_capget, + [ 185 ] = PR_capset, + [ 186 ] = PR_sigaltstack, + [ 187 ] = PR_sendfile, + [ 190 ] = PR_vfork, + [ 191 ] = PR_ugetrlimit, + [ 192 ] = PR_mmap2, + [ 193 ] = PR_truncate64, + [ 194 ] = PR_ftruncate64, + [ 195 ] = PR_stat64, + [ 196 ] = PR_lstat64, + [ 197 ] = PR_fstat64, + [ 198 ] = PR_lchown32, + [ 199 ] = PR_getuid32, + [ 200 ] = PR_getgid32, + [ 201 ] = PR_geteuid32, + [ 202 ] = PR_getegid32, + [ 203 ] = PR_setreuid32, + [ 204 ] = PR_setregid32, + [ 205 ] = PR_getgroups32, + [ 206 ] = PR_setgroups32, + [ 207 ] = PR_fchown32, + [ 208 ] = PR_setresuid32, + [ 209 ] = PR_getresuid32, + [ 210 ] = PR_setresgid32, + [ 211 ] = PR_getresgid32, + [ 212 ] = PR_chown32, + [ 213 ] = PR_setuid32, + [ 214 ] = PR_setgid32, + [ 215 ] = PR_setfsuid32, + [ 216 ] = PR_setfsgid32, + [ 217 ] = PR_pivot_root, + [ 218 ] = PR_mincore, + [ 219 ] = PR_madvise, + [ 220 ] = PR_getdents64, + [ 221 ] = PR_fcntl64, + [ 224 ] = PR_gettid, + [ 225 ] = PR_readahead, + [ 226 ] = PR_setxattr, + [ 227 ] = PR_lsetxattr, + [ 228 ] = PR_fsetxattr, + [ 229 ] = PR_getxattr, + [ 230 ] = PR_lgetxattr, + [ 231 ] = PR_fgetxattr, + [ 232 ] = PR_listxattr, + [ 233 ] = PR_llistxattr, + [ 234 ] = PR_flistxattr, + [ 235 ] = PR_removexattr, + [ 236 ] = PR_lremovexattr, + [ 237 ] = PR_fremovexattr, + [ 238 ] = PR_tkill, + [ 239 ] = PR_sendfile64, + [ 240 ] = PR_futex, + [ 241 ] = PR_sched_setaffinity, + [ 242 ] = PR_sched_getaffinity, + [ 245 ] = PR_io_setup, + [ 246 ] = PR_io_destroy, + [ 247 ] = PR_io_getevents, + [ 248 ] = PR_io_submit, + [ 249 ] = PR_io_cancel, + [ 250 ] = PR_fadvise64, + [ 252 ] = PR_exit_group, + [ 253 ] = PR_lookup_dcookie, + [ 254 ] = PR_epoll_create, + [ 255 ] = PR_epoll_ctl, + [ 256 ] = PR_epoll_wait, + [ 257 ] = PR_remap_file_pages, + [ 258 ] = PR_set_tid_address, + [ 259 ] = PR_timer_create, + [ 260 ] = PR_timer_settime, + [ 261 ] = PR_timer_gettime, + [ 262 ] = PR_timer_getoverrun, + [ 263 ] = PR_timer_delete, + [ 264 ] = PR_clock_settime, + [ 265 ] = PR_clock_gettime, + [ 266 ] = PR_clock_getres, + [ 267 ] = PR_clock_nanosleep, + [ 268 ] = PR_statfs64, + [ 269 ] = PR_fstatfs64, + [ 270 ] = PR_tgkill, + [ 271 ] = PR_utimes, + [ 272 ] = PR_fadvise64_64, + [ 274 ] = PR_mbind, + [ 275 ] = PR_get_mempolicy, + [ 276 ] = PR_set_mempolicy, + [ 277 ] = PR_mq_open, + [ 278 ] = PR_mq_unlink, + [ 279 ] = PR_mq_timedsend, + [ 280 ] = PR_mq_timedreceive, + [ 281 ] = PR_mq_notify, + [ 282 ] = PR_mq_getsetattr, + [ 283 ] = PR_kexec_load, + [ 284 ] = PR_waitid, + [ 285 ] = PR_add_key, + [ 286 ] = PR_request_key, + [ 287 ] = PR_keyctl, + [ 288 ] = PR_ioprio_set, + [ 289 ] = PR_ioprio_get, + [ 290 ] = PR_inotify_init, + [ 291 ] = PR_inotify_add_watch, + [ 292 ] = PR_inotify_rm_watch, + [ 294 ] = PR_migrate_pages, + [ 295 ] = PR_openat, + [ 296 ] = PR_mkdirat, + [ 297 ] = PR_mknodat, + [ 298 ] = PR_fchownat, + [ 299 ] = PR_futimesat, + [ 300 ] = PR_fstatat64, + [ 301 ] = PR_unlinkat, + [ 302 ] = PR_renameat, + [ 303 ] = PR_linkat, + [ 304 ] = PR_symlinkat, + [ 305 ] = PR_readlinkat, + [ 306 ] = PR_fchmodat, + [ 307 ] = PR_faccessat, + [ 308 ] = PR_pselect6, + [ 309 ] = PR_ppoll, + [ 310 ] = PR_unshare, + [ 311 ] = PR_set_robust_list, + [ 312 ] = PR_get_robust_list, + [ 313 ] = PR_splice, + [ 314 ] = PR_sync_file_range, + [ 315 ] = PR_tee, + [ 316 ] = PR_vmsplice, + [ 317 ] = PR_move_pages, + [ 318 ] = PR_getcpu, + [ 319 ] = PR_epoll_pwait, + [ 320 ] = PR_utimensat, + [ 321 ] = PR_signalfd, + [ 322 ] = PR_timerfd_create, + [ 323 ] = PR_eventfd, + [ 324 ] = PR_fallocate, + [ 325 ] = PR_timerfd_settime, + [ 326 ] = PR_timerfd_gettime, + [ 327 ] = PR_signalfd4, + [ 328 ] = PR_eventfd2, + [ 329 ] = PR_epoll_create1, + [ 330 ] = PR_dup3, + [ 331 ] = PR_pipe2, + [ 332 ] = PR_inotify_init1, + [ 333 ] = PR_preadv, + [ 334 ] = PR_pwritev, + [ 335 ] = PR_rt_tgsigqueueinfo, + [ 336 ] = PR_perf_event_open, + [ 337 ] = PR_fanotify_init, + [ 338 ] = PR_fanotify_mark, + [ 339 ] = PR_prlimit64, + [ 340 ] = PR_socket, + [ 341 ] = PR_bind, + [ 342 ] = PR_connect, + [ 343 ] = PR_listen, + [ 344 ] = PR_accept, + [ 345 ] = PR_getsockname, + [ 346 ] = PR_getpeername, + [ 347 ] = PR_socketpair, + [ 348 ] = PR_send, + [ 349 ] = PR_sendto, + [ 350 ] = PR_recv, + [ 351 ] = PR_recvfrom, + [ 352 ] = PR_shutdown, + [ 353 ] = PR_setsockopt, + [ 354 ] = PR_getsockopt, + [ 355 ] = PR_sendmsg, + [ 356 ] = PR_recvmsg, + [ 357 ] = PR_recvmmsg, + [ 358 ] = PR_accept4, + [ 359 ] = PR_name_to_handle_at, + [ 360 ] = PR_open_by_handle_at, + [ 361 ] = PR_clock_adjtime, + [ 362 ] = PR_syncfs, + [ 363 ] = PR_sendmmsg, + [ 364 ] = PR_setns, + [ 365 ] = PR_process_vm_readv, + [ 366 ] = PR_process_vm_writev, +};
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-x32.h b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-x32.h new file mode 100644 index 0000000..ff68414 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-x32.h
@@ -0,0 +1,306 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_x32[] = { + [ 0 ] = PR_read, + [ 1 ] = PR_write, + [ 2 ] = PR_open, + [ 3 ] = PR_close, + [ 4 ] = PR_stat, + [ 5 ] = PR_fstat, + [ 6 ] = PR_lstat, + [ 7 ] = PR_poll, + [ 8 ] = PR_lseek, + [ 9 ] = PR_mmap, + [ 10 ] = PR_mprotect, + [ 11 ] = PR_munmap, + [ 12 ] = PR_brk, + [ 14 ] = PR_rt_sigprocmask, + [ 17 ] = PR_pread64, + [ 18 ] = PR_pwrite64, + [ 21 ] = PR_access, + [ 22 ] = PR_pipe, + [ 23 ] = PR_select, + [ 24 ] = PR_sched_yield, + [ 25 ] = PR_mremap, + [ 26 ] = PR_msync, + [ 27 ] = PR_mincore, + [ 28 ] = PR_madvise, + [ 29 ] = PR_shmget, + [ 30 ] = PR_shmat, + [ 31 ] = PR_shmctl, + [ 32 ] = PR_dup, + [ 33 ] = PR_dup2, + [ 34 ] = PR_pause, + [ 35 ] = PR_nanosleep, + [ 36 ] = PR_getitimer, + [ 37 ] = PR_alarm, + [ 38 ] = PR_setitimer, + [ 39 ] = PR_getpid, + [ 40 ] = PR_sendfile, + [ 41 ] = PR_socket, + [ 42 ] = PR_connect, + [ 43 ] = PR_accept, + [ 44 ] = PR_sendto, + [ 48 ] = PR_shutdown, + [ 49 ] = PR_bind, + [ 50 ] = PR_listen, + [ 51 ] = PR_getsockname, + [ 52 ] = PR_getpeername, + [ 53 ] = PR_socketpair, + [ 56 ] = PR_clone, + [ 57 ] = PR_fork, + [ 58 ] = PR_vfork, + [ 60 ] = PR_exit, + [ 61 ] = PR_wait4, + [ 62 ] = PR_kill, + [ 63 ] = PR_uname, + [ 64 ] = PR_semget, + [ 65 ] = PR_semop, + [ 66 ] = PR_semctl, + [ 67 ] = PR_shmdt, + [ 68 ] = PR_msgget, + [ 69 ] = PR_msgsnd, + [ 70 ] = PR_msgrcv, + [ 71 ] = PR_msgctl, + [ 72 ] = PR_fcntl, + [ 73 ] = PR_flock, + [ 74 ] = PR_fsync, + [ 75 ] = PR_fdatasync, + [ 76 ] = PR_truncate, + [ 77 ] = PR_ftruncate, + [ 78 ] = PR_getdents, + [ 79 ] = PR_getcwd, + [ 80 ] = PR_chdir, + [ 81 ] = PR_fchdir, + [ 82 ] = PR_rename, + [ 83 ] = PR_mkdir, + [ 84 ] = PR_rmdir, + [ 85 ] = PR_creat, + [ 86 ] = PR_link, + [ 87 ] = PR_unlink, + [ 88 ] = PR_symlink, + [ 89 ] = PR_readlink, + [ 90 ] = PR_chmod, + [ 91 ] = PR_fchmod, + [ 92 ] = PR_chown, + [ 93 ] = PR_fchown, + [ 94 ] = PR_lchown, + [ 95 ] = PR_umask, + [ 96 ] = PR_gettimeofday, + [ 97 ] = PR_getrlimit, + [ 98 ] = PR_getrusage, + [ 99 ] = PR_sysinfo, + [ 100 ] = PR_times, + [ 102 ] = PR_getuid, + [ 103 ] = PR_syslog, + [ 104 ] = PR_getgid, + [ 105 ] = PR_setuid, + [ 106 ] = PR_setgid, + [ 107 ] = PR_geteuid, + [ 108 ] = PR_getegid, + [ 109 ] = PR_setpgid, + [ 110 ] = PR_getppid, + [ 111 ] = PR_getpgrp, + [ 112 ] = PR_setsid, + [ 113 ] = PR_setreuid, + [ 114 ] = PR_setregid, + [ 115 ] = PR_getgroups, + [ 116 ] = PR_setgroups, + [ 117 ] = PR_setresuid, + [ 118 ] = PR_getresuid, + [ 119 ] = PR_setresgid, + [ 120 ] = PR_getresgid, + [ 121 ] = PR_getpgid, + [ 122 ] = PR_setfsuid, + [ 123 ] = PR_setfsgid, + [ 124 ] = PR_getsid, + [ 125 ] = PR_capget, + [ 126 ] = PR_capset, + [ 130 ] = PR_rt_sigsuspend, + [ 132 ] = PR_utime, + [ 133 ] = PR_mknod, + [ 135 ] = PR_personality, + [ 136 ] = PR_ustat, + [ 137 ] = PR_statfs, + [ 138 ] = PR_fstatfs, + [ 139 ] = PR_sysfs, + [ 140 ] = PR_getpriority, + [ 141 ] = PR_setpriority, + [ 142 ] = PR_sched_setparam, + [ 143 ] = PR_sched_getparam, + [ 144 ] = PR_sched_setscheduler, + [ 145 ] = PR_sched_getscheduler, + [ 146 ] = PR_sched_get_priority_max, + [ 147 ] = PR_sched_get_priority_min, + [ 148 ] = PR_sched_rr_get_interval, + [ 149 ] = PR_mlock, + [ 150 ] = PR_munlock, + [ 151 ] = PR_mlockall, + [ 152 ] = PR_munlockall, + [ 153 ] = PR_vhangup, + [ 154 ] = PR_modify_ldt, + [ 155 ] = PR_pivot_root, + [ 157 ] = PR_prctl, + [ 158 ] = PR_arch_prctl, + [ 159 ] = PR_adjtimex, + [ 160 ] = PR_setrlimit, + [ 161 ] = PR_chroot, + [ 162 ] = PR_sync, + [ 163 ] = PR_acct, + [ 164 ] = PR_settimeofday, + [ 165 ] = PR_mount, + [ 166 ] = PR_umount2, + [ 167 ] = PR_swapon, + [ 168 ] = PR_swapoff, + [ 169 ] = PR_reboot, + [ 170 ] = PR_sethostname, + [ 171 ] = PR_setdomainname, + [ 172 ] = PR_iopl, + [ 173 ] = PR_ioperm, + [ 175 ] = PR_init_module, + [ 176 ] = PR_delete_module, + [ 179 ] = PR_quotactl, + [ 181 ] = PR_getpmsg, + [ 182 ] = PR_putpmsg, + [ 183 ] = PR_afs_syscall, + [ 184 ] = PR_tuxcall, + [ 185 ] = PR_security, + [ 186 ] = PR_gettid, + [ 187 ] = PR_readahead, + [ 188 ] = PR_setxattr, + [ 189 ] = PR_lsetxattr, + [ 190 ] = PR_fsetxattr, + [ 191 ] = PR_getxattr, + [ 192 ] = PR_lgetxattr, + [ 193 ] = PR_fgetxattr, + [ 194 ] = PR_listxattr, + [ 195 ] = PR_llistxattr, + [ 196 ] = PR_flistxattr, + [ 197 ] = PR_removexattr, + [ 198 ] = PR_lremovexattr, + [ 199 ] = PR_fremovexattr, + [ 200 ] = PR_tkill, + [ 201 ] = PR_time, + [ 202 ] = PR_futex, + [ 203 ] = PR_sched_setaffinity, + [ 204 ] = PR_sched_getaffinity, + [ 206 ] = PR_io_setup, + [ 207 ] = PR_io_destroy, + [ 208 ] = PR_io_getevents, + [ 209 ] = PR_io_submit, + [ 210 ] = PR_io_cancel, + [ 212 ] = PR_lookup_dcookie, + [ 213 ] = PR_epoll_create, + [ 216 ] = PR_remap_file_pages, + [ 217 ] = PR_getdents64, + [ 218 ] = PR_set_tid_address, + [ 219 ] = PR_restart_syscall, + [ 220 ] = PR_semtimedop, + [ 221 ] = PR_fadvise64, + [ 223 ] = PR_timer_settime, + [ 224 ] = PR_timer_gettime, + [ 225 ] = PR_timer_getoverrun, + [ 226 ] = PR_timer_delete, + [ 227 ] = PR_clock_settime, + [ 228 ] = PR_clock_gettime, + [ 229 ] = PR_clock_getres, + [ 230 ] = PR_clock_nanosleep, + [ 231 ] = PR_exit_group, + [ 232 ] = PR_epoll_wait, + [ 233 ] = PR_epoll_ctl, + [ 234 ] = PR_tgkill, + [ 235 ] = PR_utimes, + [ 237 ] = PR_mbind, + [ 238 ] = PR_set_mempolicy, + [ 239 ] = PR_get_mempolicy, + [ 240 ] = PR_mq_open, + [ 241 ] = PR_mq_unlink, + [ 242 ] = PR_mq_timedsend, + [ 243 ] = PR_mq_timedreceive, + [ 245 ] = PR_mq_getsetattr, + [ 248 ] = PR_add_key, + [ 249 ] = PR_request_key, + [ 250 ] = PR_keyctl, + [ 251 ] = PR_ioprio_set, + [ 252 ] = PR_ioprio_get, + [ 253 ] = PR_inotify_init, + [ 254 ] = PR_inotify_add_watch, + [ 255 ] = PR_inotify_rm_watch, + [ 256 ] = PR_migrate_pages, + [ 257 ] = PR_openat, + [ 258 ] = PR_mkdirat, + [ 259 ] = PR_mknodat, + [ 260 ] = PR_fchownat, + [ 261 ] = PR_futimesat, + [ 262 ] = PR_newfstatat, + [ 263 ] = PR_unlinkat, + [ 264 ] = PR_renameat, + [ 265 ] = PR_linkat, + [ 266 ] = PR_symlinkat, + [ 267 ] = PR_readlinkat, + [ 268 ] = PR_fchmodat, + [ 269 ] = PR_faccessat, + [ 270 ] = PR_pselect6, + [ 271 ] = PR_ppoll, + [ 272 ] = PR_unshare, + [ 275 ] = PR_splice, + [ 276 ] = PR_tee, + [ 277 ] = PR_sync_file_range, + [ 280 ] = PR_utimensat, + [ 281 ] = PR_epoll_pwait, + [ 282 ] = PR_signalfd, + [ 283 ] = PR_timerfd_create, + [ 284 ] = PR_eventfd, + [ 285 ] = PR_fallocate, + [ 286 ] = PR_timerfd_settime, + [ 287 ] = PR_timerfd_gettime, + [ 288 ] = PR_accept4, + [ 289 ] = PR_signalfd4, + [ 290 ] = PR_eventfd2, + [ 291 ] = PR_epoll_create1, + [ 292 ] = PR_dup3, + [ 293 ] = PR_pipe2, + [ 294 ] = PR_inotify_init1, + [ 298 ] = PR_perf_event_open, + [ 300 ] = PR_fanotify_init, + [ 301 ] = PR_fanotify_mark, + [ 302 ] = PR_prlimit64, + [ 303 ] = PR_name_to_handle_at, + [ 304 ] = PR_open_by_handle_at, + [ 305 ] = PR_clock_adjtime, + [ 306 ] = PR_syncfs, + [ 308 ] = PR_setns, + [ 309 ] = PR_getcpu, + [ 312 ] = PR_kcmp, + [ 512 ] = PR_rt_sigaction, + [ 513 ] = PR_rt_sigreturn, + [ 514 ] = PR_ioctl, + [ 515 ] = PR_readv, + [ 516 ] = PR_writev, + [ 517 ] = PR_recvfrom, + [ 518 ] = PR_sendmsg, + [ 519 ] = PR_recvmsg, + [ 520 ] = PR_execve, + [ 521 ] = PR_ptrace, + [ 522 ] = PR_rt_sigpending, + [ 523 ] = PR_rt_sigtimedwait, + [ 524 ] = PR_rt_sigqueueinfo, + [ 525 ] = PR_sigaltstack, + [ 526 ] = PR_timer_create, + [ 527 ] = PR_mq_notify, + [ 528 ] = PR_kexec_load, + [ 529 ] = PR_waitid, + [ 530 ] = PR_set_robust_list, + [ 531 ] = PR_get_robust_list, + [ 532 ] = PR_vmsplice, + [ 533 ] = PR_move_pages, + [ 534 ] = PR_preadv, + [ 535 ] = PR_pwritev, + [ 536 ] = PR_rt_tgsigqueueinfo, + [ 537 ] = PR_recvmmsg, + [ 538 ] = PR_sendmmsg, + [ 539 ] = PR_process_vm_readv, + [ 540 ] = PR_process_vm_writev, + [ 541 ] = PR_setsockopt, + [ 542 ] = PR_getsockopt, +};
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-x86_64.h b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-x86_64.h new file mode 100644 index 0000000..fe3f822 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums-x86_64.h
@@ -0,0 +1,317 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_x86_64[] = { + [ 0 ] = PR_read, + [ 1 ] = PR_write, + [ 2 ] = PR_open, + [ 3 ] = PR_close, + [ 4 ] = PR_stat, + [ 5 ] = PR_fstat, + [ 6 ] = PR_lstat, + [ 7 ] = PR_poll, + [ 8 ] = PR_lseek, + [ 9 ] = PR_mmap, + [ 10 ] = PR_mprotect, + [ 11 ] = PR_munmap, + [ 12 ] = PR_brk, + [ 13 ] = PR_rt_sigaction, + [ 14 ] = PR_rt_sigprocmask, + [ 15 ] = PR_rt_sigreturn, + [ 16 ] = PR_ioctl, + [ 17 ] = PR_pread64, + [ 18 ] = PR_pwrite64, + [ 19 ] = PR_readv, + [ 20 ] = PR_writev, + [ 21 ] = PR_access, + [ 22 ] = PR_pipe, + [ 23 ] = PR_select, + [ 24 ] = PR_sched_yield, + [ 25 ] = PR_mremap, + [ 26 ] = PR_msync, + [ 27 ] = PR_mincore, + [ 28 ] = PR_madvise, + [ 29 ] = PR_shmget, + [ 30 ] = PR_shmat, + [ 31 ] = PR_shmctl, + [ 32 ] = PR_dup, + [ 33 ] = PR_dup2, + [ 34 ] = PR_pause, + [ 35 ] = PR_nanosleep, + [ 36 ] = PR_getitimer, + [ 37 ] = PR_alarm, + [ 38 ] = PR_setitimer, + [ 39 ] = PR_getpid, + [ 40 ] = PR_sendfile, + [ 41 ] = PR_socket, + [ 42 ] = PR_connect, + [ 43 ] = PR_accept, + [ 44 ] = PR_sendto, + [ 45 ] = PR_recvfrom, + [ 46 ] = PR_sendmsg, + [ 47 ] = PR_recvmsg, + [ 48 ] = PR_shutdown, + [ 49 ] = PR_bind, + [ 50 ] = PR_listen, + [ 51 ] = PR_getsockname, + [ 52 ] = PR_getpeername, + [ 53 ] = PR_socketpair, + [ 54 ] = PR_setsockopt, + [ 55 ] = PR_getsockopt, + [ 56 ] = PR_clone, + [ 57 ] = PR_fork, + [ 58 ] = PR_vfork, + [ 59 ] = PR_execve, + [ 60 ] = PR_exit, + [ 61 ] = PR_wait4, + [ 62 ] = PR_kill, + [ 63 ] = PR_uname, + [ 64 ] = PR_semget, + [ 65 ] = PR_semop, + [ 66 ] = PR_semctl, + [ 67 ] = PR_shmdt, + [ 68 ] = PR_msgget, + [ 69 ] = PR_msgsnd, + [ 70 ] = PR_msgrcv, + [ 71 ] = PR_msgctl, + [ 72 ] = PR_fcntl, + [ 73 ] = PR_flock, + [ 74 ] = PR_fsync, + [ 75 ] = PR_fdatasync, + [ 76 ] = PR_truncate, + [ 77 ] = PR_ftruncate, + [ 78 ] = PR_getdents, + [ 79 ] = PR_getcwd, + [ 80 ] = PR_chdir, + [ 81 ] = PR_fchdir, + [ 82 ] = PR_rename, + [ 83 ] = PR_mkdir, + [ 84 ] = PR_rmdir, + [ 85 ] = PR_creat, + [ 86 ] = PR_link, + [ 87 ] = PR_unlink, + [ 88 ] = PR_symlink, + [ 89 ] = PR_readlink, + [ 90 ] = PR_chmod, + [ 91 ] = PR_fchmod, + [ 92 ] = PR_chown, + [ 93 ] = PR_fchown, + [ 94 ] = PR_lchown, + [ 95 ] = PR_umask, + [ 96 ] = PR_gettimeofday, + [ 97 ] = PR_getrlimit, + [ 98 ] = PR_getrusage, + [ 99 ] = PR_sysinfo, + [ 100 ] = PR_times, + [ 101 ] = PR_ptrace, + [ 102 ] = PR_getuid, + [ 103 ] = PR_syslog, + [ 104 ] = PR_getgid, + [ 105 ] = PR_setuid, + [ 106 ] = PR_setgid, + [ 107 ] = PR_geteuid, + [ 108 ] = PR_getegid, + [ 109 ] = PR_setpgid, + [ 110 ] = PR_getppid, + [ 111 ] = PR_getpgrp, + [ 112 ] = PR_setsid, + [ 113 ] = PR_setreuid, + [ 114 ] = PR_setregid, + [ 115 ] = PR_getgroups, + [ 116 ] = PR_setgroups, + [ 117 ] = PR_setresuid, + [ 118 ] = PR_getresuid, + [ 119 ] = PR_setresgid, + [ 120 ] = PR_getresgid, + [ 121 ] = PR_getpgid, + [ 122 ] = PR_setfsuid, + [ 123 ] = PR_setfsgid, + [ 124 ] = PR_getsid, + [ 125 ] = PR_capget, + [ 126 ] = PR_capset, + [ 127 ] = PR_rt_sigpending, + [ 128 ] = PR_rt_sigtimedwait, + [ 129 ] = PR_rt_sigqueueinfo, + [ 130 ] = PR_rt_sigsuspend, + [ 131 ] = PR_sigaltstack, + [ 132 ] = PR_utime, + [ 133 ] = PR_mknod, + [ 134 ] = PR_uselib, + [ 135 ] = PR_personality, + [ 136 ] = PR_ustat, + [ 137 ] = PR_statfs, + [ 138 ] = PR_fstatfs, + [ 139 ] = PR_sysfs, + [ 140 ] = PR_getpriority, + [ 141 ] = PR_setpriority, + [ 142 ] = PR_sched_setparam, + [ 143 ] = PR_sched_getparam, + [ 144 ] = PR_sched_setscheduler, + [ 145 ] = PR_sched_getscheduler, + [ 146 ] = PR_sched_get_priority_max, + [ 147 ] = PR_sched_get_priority_min, + [ 148 ] = PR_sched_rr_get_interval, + [ 149 ] = PR_mlock, + [ 150 ] = PR_munlock, + [ 151 ] = PR_mlockall, + [ 152 ] = PR_munlockall, + [ 153 ] = PR_vhangup, + [ 154 ] = PR_modify_ldt, + [ 155 ] = PR_pivot_root, + [ 156 ] = PR__sysctl, + [ 157 ] = PR_prctl, + [ 158 ] = PR_arch_prctl, + [ 159 ] = PR_adjtimex, + [ 160 ] = PR_setrlimit, + [ 161 ] = PR_chroot, + [ 162 ] = PR_sync, + [ 163 ] = PR_acct, + [ 164 ] = PR_settimeofday, + [ 165 ] = PR_mount, + [ 166 ] = PR_umount2, + [ 167 ] = PR_swapon, + [ 168 ] = PR_swapoff, + [ 169 ] = PR_reboot, + [ 170 ] = PR_sethostname, + [ 171 ] = PR_setdomainname, + [ 172 ] = PR_iopl, + [ 173 ] = PR_ioperm, + [ 174 ] = PR_create_module, + [ 175 ] = PR_init_module, + [ 176 ] = PR_delete_module, + [ 177 ] = PR_get_kernel_syms, + [ 178 ] = PR_query_module, + [ 179 ] = PR_quotactl, + [ 180 ] = PR_nfsservctl, + [ 181 ] = PR_getpmsg, + [ 182 ] = PR_putpmsg, + [ 183 ] = PR_afs_syscall, + [ 184 ] = PR_tuxcall, + [ 185 ] = PR_security, + [ 186 ] = PR_gettid, + [ 187 ] = PR_readahead, + [ 188 ] = PR_setxattr, + [ 189 ] = PR_lsetxattr, + [ 190 ] = PR_fsetxattr, + [ 191 ] = PR_getxattr, + [ 192 ] = PR_lgetxattr, + [ 193 ] = PR_fgetxattr, + [ 194 ] = PR_listxattr, + [ 195 ] = PR_llistxattr, + [ 196 ] = PR_flistxattr, + [ 197 ] = PR_removexattr, + [ 198 ] = PR_lremovexattr, + [ 199 ] = PR_fremovexattr, + [ 200 ] = PR_tkill, + [ 201 ] = PR_time, + [ 202 ] = PR_futex, + [ 203 ] = PR_sched_setaffinity, + [ 204 ] = PR_sched_getaffinity, + [ 205 ] = PR_set_thread_area, + [ 206 ] = PR_io_setup, + [ 207 ] = PR_io_destroy, + [ 208 ] = PR_io_getevents, + [ 209 ] = PR_io_submit, + [ 210 ] = PR_io_cancel, + [ 211 ] = PR_get_thread_area, + [ 212 ] = PR_lookup_dcookie, + [ 213 ] = PR_epoll_create, + [ 214 ] = PR_epoll_ctl_old, + [ 215 ] = PR_epoll_wait_old, + [ 216 ] = PR_remap_file_pages, + [ 217 ] = PR_getdents64, + [ 218 ] = PR_set_tid_address, + [ 219 ] = PR_restart_syscall, + [ 220 ] = PR_semtimedop, + [ 221 ] = PR_fadvise64, + [ 222 ] = PR_timer_create, + [ 223 ] = PR_timer_settime, + [ 224 ] = PR_timer_gettime, + [ 225 ] = PR_timer_getoverrun, + [ 226 ] = PR_timer_delete, + [ 227 ] = PR_clock_settime, + [ 228 ] = PR_clock_gettime, + [ 229 ] = PR_clock_getres, + [ 230 ] = PR_clock_nanosleep, + [ 231 ] = PR_exit_group, + [ 232 ] = PR_epoll_wait, + [ 233 ] = PR_epoll_ctl, + [ 234 ] = PR_tgkill, + [ 235 ] = PR_utimes, + [ 236 ] = PR_vserver, + [ 237 ] = PR_mbind, + [ 238 ] = PR_set_mempolicy, + [ 239 ] = PR_get_mempolicy, + [ 240 ] = PR_mq_open, + [ 241 ] = PR_mq_unlink, + [ 242 ] = PR_mq_timedsend, + [ 243 ] = PR_mq_timedreceive, + [ 244 ] = PR_mq_notify, + [ 245 ] = PR_mq_getsetattr, + [ 246 ] = PR_kexec_load, + [ 247 ] = PR_waitid, + [ 248 ] = PR_add_key, + [ 249 ] = PR_request_key, + [ 250 ] = PR_keyctl, + [ 251 ] = PR_ioprio_set, + [ 252 ] = PR_ioprio_get, + [ 253 ] = PR_inotify_init, + [ 254 ] = PR_inotify_add_watch, + [ 255 ] = PR_inotify_rm_watch, + [ 256 ] = PR_migrate_pages, + [ 257 ] = PR_openat, + [ 258 ] = PR_mkdirat, + [ 259 ] = PR_mknodat, + [ 260 ] = PR_fchownat, + [ 261 ] = PR_futimesat, + [ 262 ] = PR_newfstatat, + [ 263 ] = PR_unlinkat, + [ 264 ] = PR_renameat, + [ 265 ] = PR_linkat, + [ 266 ] = PR_symlinkat, + [ 267 ] = PR_readlinkat, + [ 268 ] = PR_fchmodat, + [ 269 ] = PR_faccessat, + [ 270 ] = PR_pselect6, + [ 271 ] = PR_ppoll, + [ 272 ] = PR_unshare, + [ 273 ] = PR_set_robust_list, + [ 274 ] = PR_get_robust_list, + [ 275 ] = PR_splice, + [ 276 ] = PR_tee, + [ 277 ] = PR_sync_file_range, + [ 278 ] = PR_vmsplice, + [ 279 ] = PR_move_pages, + [ 280 ] = PR_utimensat, + [ 281 ] = PR_epoll_pwait, + [ 282 ] = PR_signalfd, + [ 283 ] = PR_timerfd_create, + [ 284 ] = PR_eventfd, + [ 285 ] = PR_fallocate, + [ 286 ] = PR_timerfd_settime, + [ 287 ] = PR_timerfd_gettime, + [ 288 ] = PR_accept4, + [ 289 ] = PR_signalfd4, + [ 290 ] = PR_eventfd2, + [ 291 ] = PR_epoll_create1, + [ 292 ] = PR_dup3, + [ 293 ] = PR_pipe2, + [ 294 ] = PR_inotify_init1, + [ 295 ] = PR_preadv, + [ 296 ] = PR_pwritev, + [ 297 ] = PR_rt_tgsigqueueinfo, + [ 298 ] = PR_perf_event_open, + [ 299 ] = PR_recvmmsg, + [ 300 ] = PR_fanotify_init, + [ 301 ] = PR_fanotify_mark, + [ 302 ] = PR_prlimit64, + [ 303 ] = PR_name_to_handle_at, + [ 304 ] = PR_open_by_handle_at, + [ 305 ] = PR_clock_adjtime, + [ 306 ] = PR_syncfs, + [ 307 ] = PR_sendmmsg, + [ 308 ] = PR_setns, + [ 309 ] = PR_getcpu, + [ 310 ] = PR_process_vm_readv, + [ 311 ] = PR_process_vm_writev, + [ 312 ] = PR_kcmp, +};
diff --git a/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums.list b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums.list new file mode 100644 index 0000000..0457f07 --- /dev/null +++ b/5.1.0/.pc/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch/src/syscall/sysnums.list
@@ -0,0 +1,427 @@ +SYSNUM(ARM_BASE) +SYSNUM(ARM_breakpoint) +SYSNUM(ARM_cacheflush) +SYSNUM(ARM_set_tls) +SYSNUM(ARM_usr26) +SYSNUM(ARM_usr32) +SYSNUM(X32_SYSCALL_BIT) +SYSNUM(_llseek) +SYSNUM(_newselect) +SYSNUM(_sysctl) +SYSNUM(accept) +SYSNUM(accept4) +SYSNUM(access) +SYSNUM(acct) +SYSNUM(add_key) +SYSNUM(adjtimex) +SYSNUM(afs_syscall) +SYSNUM(alarm) +SYSNUM(arch_prctl) +SYSNUM(arch_specific_syscall) +SYSNUM(arm_fadvise64_64) +SYSNUM(arm_sync_file_range) +SYSNUM(bdflush) +SYSNUM(bind) +SYSNUM(break) +SYSNUM(brk) +SYSNUM(cacheflush) +SYSNUM(capget) +SYSNUM(capset) +SYSNUM(chdir) +SYSNUM(chmod) +SYSNUM(chown) +SYSNUM(chown32) +SYSNUM(chroot) +SYSNUM(clock_adjtime) +SYSNUM(clock_getres) +SYSNUM(clock_gettime) +SYSNUM(clock_nanosleep) +SYSNUM(clock_settime) +SYSNUM(clone) +SYSNUM(close) +SYSNUM(connect) +SYSNUM(creat) +SYSNUM(create_module) +SYSNUM(delete_module) +SYSNUM(dup) +SYSNUM(dup2) +SYSNUM(dup3) +SYSNUM(epoll_create) +SYSNUM(epoll_create1) +SYSNUM(epoll_ctl) +SYSNUM(epoll_ctl_old) +SYSNUM(epoll_pwait) +SYSNUM(epoll_wait) +SYSNUM(epoll_wait_old) +SYSNUM(eventfd) +SYSNUM(eventfd2) +SYSNUM(execve) +SYSNUM(exit) +SYSNUM(exit_group) +SYSNUM(faccessat) +SYSNUM(fadvise64) +SYSNUM(fadvise64_64) +SYSNUM(fallocate) +SYSNUM(fanotify_init) +SYSNUM(fanotify_mark) +SYSNUM(fchdir) +SYSNUM(fchmod) +SYSNUM(fchmodat) +SYSNUM(fchown) +SYSNUM(fchown32) +SYSNUM(fchownat) +SYSNUM(fcntl) +SYSNUM(fcntl64) +SYSNUM(fdatasync) +SYSNUM(fgetxattr) +SYSNUM(flistxattr) +SYSNUM(flock) +SYSNUM(fork) +SYSNUM(fremovexattr) +SYSNUM(fsetxattr) +SYSNUM(fstat) +SYSNUM(fstat64) +SYSNUM(fstatat64) +SYSNUM(fstatfs) +SYSNUM(fstatfs64) +SYSNUM(fsync) +SYSNUM(ftime) +SYSNUM(ftruncate) +SYSNUM(ftruncate64) +SYSNUM(futex) +SYSNUM(futimesat) +SYSNUM(get_kernel_syms) +SYSNUM(get_mempolicy) +SYSNUM(get_robust_list) +SYSNUM(get_thread_area) +SYSNUM(getcpu) +SYSNUM(getcwd) +SYSNUM(getdents) +SYSNUM(getdents64) +SYSNUM(getegid) +SYSNUM(getegid32) +SYSNUM(geteuid) +SYSNUM(geteuid32) +SYSNUM(getgid) +SYSNUM(getgid32) +SYSNUM(getgroups) +SYSNUM(getgroups32) +SYSNUM(getitimer) +SYSNUM(getpeername) +SYSNUM(getpgid) +SYSNUM(getpgrp) +SYSNUM(getpid) +SYSNUM(getpmsg) +SYSNUM(getppid) +SYSNUM(getpriority) +SYSNUM(getresgid) +SYSNUM(getresgid32) +SYSNUM(getresuid) +SYSNUM(getresuid32) +SYSNUM(getrlimit) +SYSNUM(getrusage) +SYSNUM(getsid) +SYSNUM(getsockname) +SYSNUM(getsockopt) +SYSNUM(gettid) +SYSNUM(gettimeofday) +SYSNUM(getuid) +SYSNUM(getuid32) +SYSNUM(getxattr) +SYSNUM(gtty) +SYSNUM(idle) +SYSNUM(init_module) +SYSNUM(inotify_add_watch) +SYSNUM(inotify_init) +SYSNUM(inotify_init1) +SYSNUM(inotify_rm_watch) +SYSNUM(io_cancel) +SYSNUM(io_destroy) +SYSNUM(io_getevents) +SYSNUM(io_setup) +SYSNUM(io_submit) +SYSNUM(ioctl) +SYSNUM(ioperm) +SYSNUM(iopl) +SYSNUM(ioprio_get) +SYSNUM(ioprio_set) +SYSNUM(ipc) +SYSNUM(kcmp) +SYSNUM(kexec_load) +SYSNUM(keyctl) +SYSNUM(kill) +SYSNUM(lchown) +SYSNUM(lchown32) +SYSNUM(lgetxattr) +SYSNUM(link) +SYSNUM(linkat) +SYSNUM(listen) +SYSNUM(listxattr) +SYSNUM(llistxattr) +SYSNUM(lock) +SYSNUM(lookup_dcookie) +SYSNUM(lremovexattr) +SYSNUM(lseek) +SYSNUM(lsetxattr) +SYSNUM(lstat) +SYSNUM(lstat64) +SYSNUM(madvise) +SYSNUM(mbind) +SYSNUM(migrate_pages) +SYSNUM(mincore) +SYSNUM(mkdir) +SYSNUM(mkdirat) +SYSNUM(mknod) +SYSNUM(mknodat) +SYSNUM(mlock) +SYSNUM(mlockall) +SYSNUM(mmap) +SYSNUM(mmap2) +SYSNUM(modify_ldt) +SYSNUM(mount) +SYSNUM(move_pages) +SYSNUM(mprotect) +SYSNUM(mpx) +SYSNUM(mq_getsetattr) +SYSNUM(mq_notify) +SYSNUM(mq_open) +SYSNUM(mq_timedreceive) +SYSNUM(mq_timedsend) +SYSNUM(mq_unlink) +SYSNUM(mremap) +SYSNUM(msgctl) +SYSNUM(msgget) +SYSNUM(msgrcv) +SYSNUM(msgsnd) +SYSNUM(msync) +SYSNUM(munlock) +SYSNUM(munlockall) +SYSNUM(munmap) +SYSNUM(name_to_handle_at) +SYSNUM(nanosleep) +SYSNUM(newfstatat) +SYSNUM(nfsservctl) +SYSNUM(nice) +SYSNUM(oldfstat) +SYSNUM(oldlstat) +SYSNUM(oldolduname) +SYSNUM(oldstat) +SYSNUM(olduname) +SYSNUM(open) +SYSNUM(open_by_handle_at) +SYSNUM(openat) +SYSNUM(pause) +SYSNUM(pciconfig_iobase) +SYSNUM(pciconfig_read) +SYSNUM(pciconfig_write) +SYSNUM(perf_event_open) +SYSNUM(personality) +SYSNUM(pipe) +SYSNUM(pipe2) +SYSNUM(pivot_root) +SYSNUM(poll) +SYSNUM(ppoll) +SYSNUM(prctl) +SYSNUM(pread64) +SYSNUM(preadv) +SYSNUM(prlimit64) +SYSNUM(process_vm_readv) +SYSNUM(process_vm_writev) +SYSNUM(prof) +SYSNUM(profil) +SYSNUM(pselect6) +SYSNUM(ptrace) +SYSNUM(putpmsg) +SYSNUM(pwrite64) +SYSNUM(pwritev) +SYSNUM(query_module) +SYSNUM(quotactl) +SYSNUM(read) +SYSNUM(readahead) +SYSNUM(readdir) +SYSNUM(readlink) +SYSNUM(readlinkat) +SYSNUM(readv) +SYSNUM(reboot) +SYSNUM(recv) +SYSNUM(recvfrom) +SYSNUM(recvmmsg) +SYSNUM(recvmsg) +SYSNUM(remap_file_pages) +SYSNUM(removexattr) +SYSNUM(rename) +SYSNUM(renameat) +SYSNUM(request_key) +SYSNUM(restart_syscall) +SYSNUM(rmdir) +SYSNUM(rt_sigaction) +SYSNUM(rt_sigpending) +SYSNUM(rt_sigprocmask) +SYSNUM(rt_sigqueueinfo) +SYSNUM(rt_sigreturn) +SYSNUM(rt_sigsuspend) +SYSNUM(rt_sigtimedwait) +SYSNUM(rt_tgsigqueueinfo) +SYSNUM(sched_get_priority_max) +SYSNUM(sched_get_priority_min) +SYSNUM(sched_getaffinity) +SYSNUM(sched_getparam) +SYSNUM(sched_getscheduler) +SYSNUM(sched_rr_get_interval) +SYSNUM(sched_setaffinity) +SYSNUM(sched_setparam) +SYSNUM(sched_setscheduler) +SYSNUM(sched_yield) +SYSNUM(security) +SYSNUM(select) +SYSNUM(semctl) +SYSNUM(semget) +SYSNUM(semop) +SYSNUM(semtimedop) +SYSNUM(send) +SYSNUM(sendfile) +SYSNUM(sendfile64) +SYSNUM(sendmmsg) +SYSNUM(sendmsg) +SYSNUM(sendto) +SYSNUM(set_mempolicy) +SYSNUM(set_robust_list) +SYSNUM(set_thread_area) +SYSNUM(set_tid_address) +SYSNUM(setdomainname) +SYSNUM(setfsgid) +SYSNUM(setfsgid32) +SYSNUM(setfsuid) +SYSNUM(setfsuid32) +SYSNUM(setgid) +SYSNUM(setgid32) +SYSNUM(setgroups) +SYSNUM(setgroups32) +SYSNUM(sethostname) +SYSNUM(setitimer) +SYSNUM(setns) +SYSNUM(setpgid) +SYSNUM(setpriority) +SYSNUM(setregid) +SYSNUM(setregid32) +SYSNUM(setresgid) +SYSNUM(setresgid32) +SYSNUM(setresuid) +SYSNUM(setresuid32) +SYSNUM(setreuid) +SYSNUM(setreuid32) +SYSNUM(setrlimit) +SYSNUM(setsid) +SYSNUM(setsockopt) +SYSNUM(settimeofday) +SYSNUM(setuid) +SYSNUM(setuid32) +SYSNUM(setxattr) +SYSNUM(sgetmask) +SYSNUM(shmat) +SYSNUM(shmctl) +SYSNUM(shmdt) +SYSNUM(shmget) +SYSNUM(shutdown) +SYSNUM(sigaction) +SYSNUM(sigaltstack) +SYSNUM(signal) +SYSNUM(signalfd) +SYSNUM(signalfd4) +SYSNUM(sigpending) +SYSNUM(sigprocmask) +SYSNUM(sigreturn) +SYSNUM(sigsuspend) +SYSNUM(socket) +SYSNUM(socketcall) +SYSNUM(socketpair) +SYSNUM(splice) +SYSNUM(ssetmask) +SYSNUM(stat) +SYSNUM(stat64) +SYSNUM(statfs) +SYSNUM(statfs64) +SYSNUM(stime) +SYSNUM(stty) +SYSNUM(swapoff) +SYSNUM(swapon) +SYSNUM(symlink) +SYSNUM(symlinkat) +SYSNUM(sync) +SYSNUM(sync_file_range) +SYSNUM(sync_file_range2) +SYSNUM(syncfs) +SYSNUM(syscalls) +SYSNUM(sysfs) +SYSNUM(sysinfo) +SYSNUM(syslog) +SYSNUM(tee) +SYSNUM(tgkill) +SYSNUM(time) +SYSNUM(timer_create) +SYSNUM(timer_delete) +SYSNUM(timer_getoverrun) +SYSNUM(timer_gettime) +SYSNUM(timer_settime) +SYSNUM(timerfd_create) +SYSNUM(timerfd_gettime) +SYSNUM(timerfd_settime) +SYSNUM(times) +SYSNUM(tkill) +SYSNUM(truncate) +SYSNUM(truncate64) +SYSNUM(tuxcall) +SYSNUM(ugetrlimit) +SYSNUM(ulimit) +SYSNUM(umask) +SYSNUM(umount) +SYSNUM(umount2) +SYSNUM(uname) +SYSNUM(unlink) +SYSNUM(unlinkat) +SYSNUM(unshare) +SYSNUM(uselib) +SYSNUM(ustat) +SYSNUM(utime) +SYSNUM(utimensat) +SYSNUM(utimes) +SYSNUM(vfork) +SYSNUM(vhangup) +SYSNUM(vm86) +SYSNUM(vm86old) +SYSNUM(vmsplice) +SYSNUM(vserver) +SYSNUM(wait4) +SYSNUM(waitid) +SYSNUM(waitpid) +SYSNUM(write) +SYSNUM(writev) +SYSNUM(x32_execve) +SYSNUM(x32_get_robust_list) +SYSNUM(x32_ioctl) +SYSNUM(x32_kexec_load) +SYSNUM(x32_move_pages) +SYSNUM(x32_mq_notify) +SYSNUM(x32_preadv) +SYSNUM(x32_process_vm_readv) +SYSNUM(x32_process_vm_writev) +SYSNUM(x32_ptrace) +SYSNUM(x32_pwritev) +SYSNUM(x32_readv) +SYSNUM(x32_recvfrom) +SYSNUM(x32_recvmmsg) +SYSNUM(x32_recvmsg) +SYSNUM(x32_rt_sigaction) +SYSNUM(x32_rt_sigpending) +SYSNUM(x32_rt_sigqueueinfo) +SYSNUM(x32_rt_sigreturn) +SYSNUM(x32_rt_sigtimedwait) +SYSNUM(x32_rt_tgsigqueueinfo) +SYSNUM(x32_sendmmsg) +SYSNUM(x32_sendmsg) +SYSNUM(x32_set_robust_list) +SYSNUM(x32_sigaltstack) +SYSNUM(x32_timer_create) +SYSNUM(x32_vmsplice) +SYSNUM(x32_waitid) +SYSNUM(x32_writev)
diff --git a/5.1.0/.pc/Fix-man-syntax.diff/doc/proot/man.1 b/5.1.0/.pc/Fix-man-syntax.diff/doc/proot/man.1 new file mode 100644 index 0000000..e83b9f2 --- /dev/null +++ b/5.1.0/.pc/Fix-man-syntax.diff/doc/proot/man.1
@@ -0,0 +1,727 @@ +.\" Man page generated from reStructuredText. +. +.TH PROOT 1 "2014-12-12" "5.1.0" "" +.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 use PRoot as a generic Linux process instrumentation engine thanks +to its extension mechanism, see \fI\%CARE\fP for an example. 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\fP,\fB \ \-\-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. +.sp +It is recommended to use the \fB\-R\fP or \fB\-S\fP options instead. +.TP +.BI \-b \ path\fP,\fB \ \-\-bind\fB= path\fP,\fB \ \-m \ path\fP,\fB \ \-\-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\fP,\fB \ \-\-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. +.TP +.BI \-w \ path\fP,\fB \ \-\-pwd\fB= path\fP,\fB \ \-\-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\fP,\fB \ \-\-verbose\fB= value +Set the level of debug information to \fIvalue\fP\&. +.sp +The higher the integer \fIvalue\fP is, the more detailed debug +information is printed to the standard error stream. A negative +\fIvalue\fP makes PRoot quiet except on fatal errors. +.TP +.B \-V\fP,\fB \-\-version\fP,\fB \-\-about +Print version, copyright, license and contact, then exit. +.TP +.B \-h\fP,\fB \-\-help\fP,\fB \-\-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\fP,\fB \ \-\-kernel\-release\fB= string +Make current kernel appear as kernel release \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 features that are available in the kernel release specified by +\fIstring\fP but that are missing in the current kernel. +.TP +.B \-0\fP,\fB \-\-root\-id +Make current user appear as "root" and fake its privileges. +.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\&. +.TP +.BI \-i \ string\fP,\fB \ \-\-change\-id\fB= string +Make current user and group appear as \fIstring\fP "uid:gid". +.sp +This option makes the current user and group appear as \fIuid\fP and +\fIgid\fP\&. Likewise, files actually owned by the current user and +group appear as if they were owned by \fIuid\fP and \fIgid\fP instead. +Note that the \fB\-0\fP option is the same as \fB\-i 0:0\fP\&. +.UNINDENT +.SS Alias options +.sp +The following options are aliases for handy sets of options. +.INDENT 0.0 +.TP +.BI \-R \ path +Alias: \fB\-r *path*\fP + a couple of recommended \fB\-b\fP\&. +.sp +Programs isolated in \fIpath\fP, a guest rootfs, might still need to +access information about the host system, as it is illustrated in +the \fBExamples\fP section of the manual. These host information +are typically: user/group definition, network setup, run\-time +information, users\(aq files, ... On all Linux distributions, they +all lie in a couple of host files and directories that are +automatically bound by this option: +.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 +/run/ +.IP \(bu 2 +/var/run/dbus/system_bus_socket +.IP \(bu 2 +$HOME +.IP \(bu 2 +\fIpath\fP +.UNINDENT +.TP +.BI \-S \ path +Alias: \fB\-0 \-r *path*\fP + a couple of recommended \fB\-b\fP\&. +.sp +This option is useful to safely create and install packages into +the guest rootfs. It is similar to the \fB\-R\fP option expect it +enables the \fB\-0\fP option and binds only the following minimal set +of paths to avoid unexpected changes on host files: +.INDENT 7.0 +.IP \(bu 2 +/etc/host.conf +.IP \(bu 2 +/etc/hosts +.IP \(bu 2 +/etc/nsswitch.conf +.IP \(bu 2 +/etc/resolv.conf +.IP \(bu 2 +/dev/ +.IP \(bu 2 +/sys/ +.IP \(bu 2 +/proc/ +.IP \(bu 2 +/tmp/ +.IP \(bu 2 +/run/shm +.IP \(bu 2 +$HOME +.IP \(bu 2 +\fIpath\fP +.UNINDENT +.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/<pid>/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 enables one to relocate files and directories. This is +typically useful to trick 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/ +? \- +? 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\-R\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/ + +$ ps \-o tty,command +TT COMMAND +pts/6 \-bash +pts/6 proot \-R /mnt/slackware\-8.0/ +pts/6 \- +pts/6 ps \-o tty,command +.ft P +.fi +.UNINDENT +.UNINDENT +.SS \fBchroot\fP + \fBmount \-\-bind\fP + \fBsu\fP equivalent +.sp +Some programs will not work correctly if they are not run by the +"root" user, this is typically the case with package managers. PRoot +can fake the root identity and its privileges when the \fB\-0\fP (zero) +option is specified: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +proot \-r /mnt/slackware\-8.0/ \-0 + +# id +uid=0(root) gid=0(root) [...] + +# mkdir /tmp/foo +# chmod a\-rwx /tmp/foo +# echo \(aqI bypass file\-system permissions.\(aq > /tmp/foo/bar +# cat /tmp/foo/bar +I bypass file\-system permissions. +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +This option is typically required to create or install packages into +the guest rootfs. Note it is \fInot\fP recommended to use the \fB\-R\fP +option when installing packages since they may try to update bound +system files, like \fB/etc/group\fP\&. Instead, it is recommended to use +the \fB\-S\fP option. This latter enables the \fB\-0\fP option and binds +only paths that are known to not be updated by packages: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +proot \-S /mnt/slackware\-8.0/ + +# installpkg perl.tgz +Installing package perl... +.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: +.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\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 one 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\&. It is also +available as highly compatible static binaries: +.INDENT 0.0 +.IP \(bu 2 +for x86_64: \fI\%http://static.proot.me/proot\-x86_64\fP +.IP \(bu 2 +for x86: \fI\%http://static.proot.me/proot\-x86\fP +.IP \(bu 2 +for ARM: \fI\%http://static.proot.me/proot\-arm\fP +.IP \(bu 2 +other architectures: on demand. +.UNINDENT +.SS Rootfs +.sp +Here follows a couple of URLs where some rootfs archives can be freely +downloaded. Note that \fBmknod\fP errors reported by \fBtar\fP when +extracting these archives can be safely ignored since special files +are typically bound (see \fB\-R\fP option for details). +.INDENT 0.0 +.IP \(bu 2 +\fI\%http://download.openvz.org/template/precreated/\fP +.IP \(bu 2 +\fI\%https://images.linuxcontainers.org/images/\fP +.IP \(bu 2 +\fI\%http://distfiles.gentoo.org/releases/\fP +.IP \(bu 2 +\fI\%http://cdimage.ubuntu.com/ubuntu\-core/releases/\fP +.IP \(bu 2 +\fI\%http://archlinuxarm.org/developers/downloads\fP +.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, patches, ... +Copyright (C) 2014 STMicroelectronics, licensed under GPL v2 or later. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C + _____ _____ ___ +| __ \e __ \e_____ _____| |_ +| __/ / _ \e/ _ \e _| +|__| |__|__\e_____/\e_____/\e____| +.ft P +.fi +.UNINDENT +.UNINDENT +.\" Generated by docutils manpage writer. +.
diff --git a/5.1.0/.pc/Install-proot-into-DESTDIR-usr-bin.patch/src/GNUmakefile b/5.1.0/.pc/Install-proot-into-DESTDIR-usr-bin.patch/src/GNUmakefile new file mode 100644 index 0000000..af318da --- /dev/null +++ b/5.1.0/.pc/Install-proot-into-DESTDIR-usr-bin.patch/src/GNUmakefile
@@ -0,0 +1,242 @@ +# If you want to build outside of the source tree, use the -f option: +# make -f ${SOMEWHERE}/proot/src/GNUmakefile + +# the VPATH variable must point to the actual makefile directory +VPATH := $(dir $(lastword $(MAKEFILE_LIST))) +SRC = $(dir $(firstword $(MAKEFILE_LIST))) + +GIT = git +RM = rm +INSTALL = install +CC = $(CROSS_COMPILE)gcc +LD = $(CC) +STRIP = $(CROSS_COMPILE)strip +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump + +CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I. -I$(VPATH) +CFLAGS += -Wall -Wextra -O2 +LDFLAGS += -ltalloc + +CARE_LDFLAGS = -larchive + +OBJECTS += \ + cli/cli.o \ + cli/proot.o \ + cli/note.o \ + execve/enter.o \ + execve/exit.o \ + execve/shebang.o \ + execve/elf.o \ + execve/ldso.o \ + execve/auxv.o \ + execve/aoxp.o \ + path/binding.o \ + path/glue.o \ + path/canon.o \ + path/path.o \ + path/proc.o \ + path/temp.o \ + syscall/seccomp.o \ + syscall/syscall.o \ + syscall/chain.o \ + syscall/enter.o \ + syscall/exit.o \ + syscall/sysnum.o \ + syscall/socket.o \ + syscall/heap.o \ + tracee/tracee.o \ + tracee/mem.o \ + tracee/reg.o \ + tracee/event.o \ + ptrace/ptrace.o \ + ptrace/user.o \ + ptrace/wait.o \ + extension/extension.o \ + extension/kompat/kompat.o \ + extension/fake_id0/fake_id0.o \ + loader/loader-wrapped.o + +define define_from_arch.h +$2$1 := $(shell $(CC) $1 -E -dM -DNO_LIBC_HEADER $(SRC)/arch.h | grep -w $2 | cut -f 3 -d ' ') +endef + +$(eval $(call define_from_arch.h,,HAS_LOADER_32BIT)) + +ifdef HAS_LOADER_32BIT + OBJECTS += loader/loader-m32-wrapped.o +endif + +CARE_OBJECTS = \ + cli/care.o \ + cli/care-manual.o \ + extension/care/care.o \ + extension/care/final.o \ + extension/care/extract.o \ + extension/care/archive.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 = process_vm seccomp_filter +CHECK_PROGRAMS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature)) +CHECK_OBJECTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).o) +CHECK_RESULTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).res) + +.SILENT .IGNORE .INTERMEDIATE: $(CHECK_OBJECTS) $(CHECK_PROGRAMS) + +.check_%.o: .check_%.c + -$(COMPILE:echo=false) $(silently) + +.check_%: .check_%.o + -$(LINK:echo=false) $(silently) + +.check_%.res: .check_% + $(Q)if [ -e $< ]; then echo "#define HAVE_$(shell echo $* | tr a-z A-Z)" > $@; else echo "" > $@; fi + +build.h: $(CHECK_RESULTS) + $($(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)cat $^ >> $@ + $(Q)echo "#endif /* BUILD_H */" >> $@ + +BUILD_ID_NONE := $(shell if ld --build-id=none --version >/dev/null 2>&1; then echo ',--build-id=none'; fi) + +###################################################################### +# Build rules + +COMPILE = $($(quiet)CC) $(CPPFLAGS) $(CFLAGS) -MD -c $(SRC)$< -o $@ +LINK = $($(quiet)LD) -o $@ $^ $(LDFLAGS) + +OBJIFY = $($(quiet)GEN) \ + $(OBJCOPY) \ + --input binary \ + --output `env LANG=C $(OBJDUMP) -f cli/cli.o | \ + grep 'file format' | awk '{print $$4}'` \ + --binary-architecture `env LANG=C $(OBJDUMP) -f cli/cli.o | \ + grep architecture | cut -f 1 -d , | awk '{print $$2}'` \ + $< $@ + +proot: $(OBJECTS) + $(LINK) + +care: $(OBJECTS) $(CARE_OBJECTS) + $(LINK) $(CARE_LDFLAGS) + +# Special case to compute which files depend on the auto-generated +# file "build.h". +USE_BUILD_H := $(patsubst $(SRC)%.c,%.o,$(shell egrep -sl 'include[[:space:]]+"build.h"' $(patsubst %.o,$(SRC)%.c,$(OBJECTS) $(CARE_OBJECTS)))) +$(USE_BUILD_H): build.h + +%.o: %.c + @mkdir -p $(dir $@) + $(COMPILE) + +.INTERMEDIATE: manual +manual: $(VPATH)/../doc/care/manual.txt + $(Q)cp $< $@ + +cli/care-manual.o: manual cli/cli.o + $(OBJIFY) + +cli/%-licenses.o: licenses cli/cli.o + $(OBJIFY) + +###################################################################### +# Build rules for the loader + +define build_loader +LOADER$1_OBJECTS = loader/loader$1.o loader/assembly$1.o + +$(eval $(call define_from_arch.h,$1,LOADER_ARCH_CFLAGS)) +$(eval $(call define_from_arch.h,$1,LOADER_ADDRESS)) + +LOADER_CFLAGS$1 += -fPIC -ffreestanding $(LOADER_ARCH_CFLAGS$1) +LOADER_LDFLAGS$1 += -static -nostdlib -Wl$(BUILD_ID_NONE),-Ttext=$(LOADER_ADDRESS$1) + +loader/loader$1.o: loader/loader.c + @mkdir -p $$(dir $$@) + $$(COMPILE) $1 $$(LOADER_CFLAGS$1) + +loader/assembly$1.o: loader/assembly.S + @mkdir -p $$(dir $$@) + $$(COMPILE) $1 $$(LOADER_CFLAGS$1) + +loader/loader$1: $$(LOADER$1_OBJECTS) + $$($$(quiet)LD) $1 -o $$@ $$^ $$(LOADER_LDFLAGS$1) + +.INTERMEDIATE: loader$1.exe +loader$1.exe: loader/loader$1 + $$(Q)cp $$< $$@ + $$(Q)$(STRIP) $$@ + +loader/loader$1-wrapped.o: loader$1.exe cli/cli.o + $$(OBJIFY) +endef + +$(eval $(build_loader)) + +ifdef HAS_LOADER_32BIT +$(eval $(call build_loader,-m32)) +endif + +###################################################################### +# Dependencies + +.DELETE_ON_ERROR: +$(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS): $(firstword $(MAKEFILE_LIST)) + +DEPS = $(OBJECTS:.o=.d) $(CARE_OBJECTS:.o=.d) $(LOADER_OBJECTS:.o=.d) $(LOADER-m32_OBJECTS:.o=.d) $(CHECK_OBJECTS:.o=.d) +-include $(DEPS) + +###################################################################### +# PHONY targets + +PREFIX = /usr/local +DESTDIR = $(PREFIX)/bin + +.PHONY: clean distclean install install-care uninstall +clean distclean: + -$(RM) -f $(CHECK_OBJECTS) $(CHECK_PROGRAMS) $(CHECK_RESULTS) $(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS) proot care loader/loader loader/loader-m32 cli/care-manual.o $(DEPS) build.h licenses + +install: proot + $($(quiet)INSTALL) -D $< $(DESTDIR)/$< + +install-care: care + $($(quiet)INSTALL) -D $< $(DESTDIR)/$< + +uninstall: + -$(RM) -f $(DESTDIR)/proot + +uninstall-care: + -$(RM) -f $(DESTDIR)/care
diff --git a/5.1.0/.pc/applied-patches b/5.1.0/.pc/applied-patches new file mode 100644 index 0000000..3e37338 --- /dev/null +++ b/5.1.0/.pc/applied-patches
@@ -0,0 +1,6 @@ +Install-proot-into-DESTDIR-usr-bin.patch +Fix-man-syntax.diff +arm64.patch +fix-use-of-size +disable-seccomp-based-tracing-performanc +0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch
diff --git a/5.1.0/.pc/arm64.patch/src/arch.h b/5.1.0/.pc/arm64.patch/src/arch.h new file mode 100644 index 0000000..abe36d6 --- /dev/null +++ b/5.1.0/.pc/arm64.patch/src/arch.h
@@ -0,0 +1,172 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 ARCH_H +#define ARCH_H + +#ifndef NO_LIBC_HEADER +#include <sys/ptrace.h> /* linux.git:c0a3a20b */ +#include <linux/audit.h> /* AUDIT_ARCH_*, */ +#endif + +typedef unsigned long word_t; +typedef unsigned char byte_t; + +#define SYSCALL_AVOIDER ((word_t) -2) +#define SYSTRAP_NUM SYSARG_NUM + +#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 SYSNUMS_HEADER1 "syscall/sysnums-x86_64.h" + #define SYSNUMS_HEADER2 "syscall/sysnums-i386.h" + #define SYSNUMS_HEADER3 "syscall/sysnums-x32.h" + + #define SYSNUMS_ABI1 sysnums_x86_64 + #define SYSNUMS_ABI2 sysnums_i386 + #define SYSNUMS_ABI3 sysnums_x32 + + #undef SYSTRAP_NUM + #define SYSTRAP_NUM SYSARG_RESULT + #define SYSTRAP_SIZE 2 + + #define SECCOMP_ARCHS { \ + { .value = AUDIT_ARCH_X86_64, .nb_abis = 2, .abis = { ABI_DEFAULT, ABI_3 } }, \ + { .value = AUDIT_ARCH_I386, .nb_abis = 1, .abis = { ABI_2 } }, \ + } + + #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 + + #define LOADER_ADDRESS 0x600000000000 + #define HAS_LOADER_32BIT true + + #define EXEC_PIC_ADDRESS 0x500000000000 + #define INTERP_PIC_ADDRESS 0x6f0000000000 + #define EXEC_PIC_ADDRESS_32 0x0f000000 + #define INTERP_PIC_ADDRESS_32 0xaf000000 + +#elif defined(ARCH_ARM_EABI) + + #define SYSNUMS_HEADER1 "syscall/sysnums-arm.h" + #define SYSNUMS_ABI1 sysnums_arm + + #define SYSTRAP_SIZE 4 + + #define SECCOMP_ARCHS { { .value = AUDIT_ARCH_ARM, .nb_abis = 1, .abis = { ABI_DEFAULT } } } + + #define user_regs_struct user_regs + #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 LOADER_ADDRESS 0x10000000 + + #define EXEC_PIC_ADDRESS 0x0f000000 + #define INTERP_PIC_ADDRESS 0x1f000000 + + /* The syscall number has to be valid on ARM, so use tuxcall(2) as + * the "void" syscall since it has no side effects. */ + #undef SYSCALL_AVOIDER + #define SYSCALL_AVOIDER ((word_t) 222) + +#elif defined(ARCH_ARM64) + + #define SYSNUMS_HEADER1 "syscall/sysnums-arm64.h" + #define SYSNUMS_ABI1 sysnums_arm64 + + #define SYSTRAP_SIZE 4 + + #define SECCOMP_ARCHS { } + + #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 SYSNUMS_HEADER1 "syscall/sysnums-i386.h" + #define SYSNUMS_ABI1 sysnums_i386 + + #undef SYSTRAP_NUM + #define SYSTRAP_NUM SYSARG_RESULT + #define SYSTRAP_SIZE 2 + + #define SECCOMP_ARCHS { { .value = AUDIT_ARCH_I386, .nb_abis = 1, .abis = { ABI_DEFAULT } } } + + #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 LOADER_ADDRESS 0xa0000000 + #define LOADER_ARCH_CFLAGS -mregparm=3 + + #define EXEC_PIC_ADDRESS 0x0f000000 + #define INTERP_PIC_ADDRESS 0xaf000000 + +#elif defined(ARCH_SH4) + + #define SYSNUMS_HEADER1 "syscall/sysnums-sh4.h" + #define SYSNUMS_ABI1 sysnums_sh4 + + #define SYSTRAP_SIZE 2 + + #define SECCOMP_ARCHS { } + + #define user_regs_struct pt_regs + #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 */
diff --git a/5.1.0/.pc/arm64.patch/src/loader/assembly-arm64.h b/5.1.0/.pc/arm64.patch/src/loader/assembly-arm64.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/5.1.0/.pc/arm64.patch/src/loader/assembly-arm64.h
diff --git a/5.1.0/.pc/arm64.patch/src/loader/loader.c b/5.1.0/.pc/arm64.patch/src/loader/loader.c new file mode 100644 index 0000000..c639122 --- /dev/null +++ b/5.1.0/.pc/arm64.patch/src/loader/loader.c
@@ -0,0 +1,257 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdbool.h> /* bool, true, false, */ + +#define NO_LIBC_HEADER +#include "loader/script.h" +#include "compat.h" +#include "arch.h" + +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) + +#if GCC_VERSION < 40500 +#define __builtin_unreachable() +#endif + +#if defined(ARCH_X86_64) +# include "loader/assembly-x86_64.h" +#elif defined(ARCH_ARM_EABI) +# include "loader/assembly-arm.h" +#elif defined(ARCH_X86) +# include "loader/assembly-x86.h" +#else +# error "Unsupported architecture" +#endif + +#if !defined(MMAP_OFFSET_SHIFT) +# define MMAP_OFFSET_SHIFT 0 +#endif + +#define FATAL() do { \ + SYSCALL(EXIT, 1, 182); \ + __builtin_unreachable(); \ + } while (0) + +#define unlikely(expr) __builtin_expect(!!(expr), 0) + +/** + * Clear the memory from @start (inclusive) to @end (exclusive). + */ +static inline void clear(word_t start, word_t end) +{ + byte_t *start_misaligned; + byte_t *end_misaligned; + + word_t *start_aligned; + word_t *end_aligned; + + /* Compute the number of mis-aligned bytes. */ + word_t start_bytes = start % sizeof(word_t); + word_t end_bytes = end % sizeof(word_t); + + /* Compute aligned addresses. */ + start_aligned = (word_t *) (start_bytes ? start + sizeof(word_t) - start_bytes : start); + end_aligned = (word_t *) (end - end_bytes); + + /* Clear leading mis-aligned bytes. */ + start_misaligned = (byte_t *) start; + while (start_misaligned < (byte_t *) start_aligned) + *start_misaligned++ = 0; + + /* Clear aligned bytes. */ + while (start_aligned < end_aligned) + *start_aligned++ = 0; + + /* Clear trailing mis-aligned bytes. */ + end_misaligned = (byte_t *) end_aligned; + while (end_misaligned < (byte_t *) end) + *end_misaligned++ = 0; +} + +/** + * Return the address of the last path component of @string_. Note + * that @string_ is not modified. + */ +static inline word_t basename(word_t string_) +{ + byte_t *string = (byte_t *) string_; + byte_t *cursor; + + for (cursor = string; *cursor != 0; cursor++) + ; + + for (; *cursor != (byte_t) '/' && cursor > string; cursor--) + ; + + if (cursor != string) + cursor++; + + return (word_t) cursor; +} + +/** + * Interpret the load script pointed to by @cursor. + */ +void _start(void *cursor) +{ + bool traced = false; + bool reset_at_base = true; + word_t at_base = 0; + + word_t fd = -1; + word_t status; + + while(1) { + LoadStatement *stmt = cursor; + + switch (stmt->action) { + case LOAD_ACTION_OPEN_NEXT: + status = SYSCALL(CLOSE, 1, fd); + if (unlikely((int) status < 0)) + FATAL(); + /* Fall through. */ + + case LOAD_ACTION_OPEN: + fd = SYSCALL(OPEN, 3, stmt->open.string_address, O_RDONLY, 0); + if (unlikely((int) fd < 0)) + FATAL(); + + reset_at_base = true; + + cursor += LOAD_STATEMENT_SIZE(*stmt, open); + break; + + case LOAD_ACTION_MMAP_FILE: + status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length, + stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED, fd, + stmt->mmap.offset >> MMAP_OFFSET_SHIFT); + if (unlikely(status != stmt->mmap.addr)) + FATAL(); + + if (stmt->mmap.clear_length != 0) + clear(stmt->mmap.addr + stmt->mmap.length - stmt->mmap.clear_length, + stmt->mmap.addr + stmt->mmap.length); + + if (reset_at_base) { + at_base = stmt->mmap.addr; + reset_at_base = false; + } + + cursor += LOAD_STATEMENT_SIZE(*stmt, mmap); + break; + + case LOAD_ACTION_MMAP_ANON: + status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length, + stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); + if (unlikely(status != stmt->mmap.addr)) + FATAL(); + + cursor += LOAD_STATEMENT_SIZE(*stmt, mmap); + break; + + case LOAD_ACTION_START_TRACED: + traced = true; + /* Fall through. */ + + case LOAD_ACTION_START: { + word_t *cursor2 = (word_t *) stmt->start.stack_pointer; + const word_t argc = cursor2[0]; + const word_t at_execfn = cursor2[1]; + word_t name; + + status = SYSCALL(CLOSE, 1, fd); + if (unlikely((int) status < 0)) + FATAL(); + + /* Right after execve, the stack content is as follow: + * + * +------+--------+--------+--------+ + * | argc | argv[] | envp[] | auxv[] | + * +------+--------+--------+--------+ + */ + + /* Skip argv[]. */ + cursor2 += argc + 1; + + /* Skip envp[]. */ + do cursor2++; while (cursor2[0] != 0); + cursor2++; + + /* Adjust auxv[]. */ + do { + switch (cursor2[0]) { + case AT_PHDR: + cursor2[1] = stmt->start.at_phdr; + break; + + case AT_PHENT: + cursor2[1] = stmt->start.at_phent; + break; + + case AT_PHNUM: + cursor2[1] = stmt->start.at_phnum; + break; + + case AT_ENTRY: + cursor2[1] = stmt->start.at_entry; + break; + + case AT_BASE: + cursor2[1] = at_base; + break; + + case AT_EXECFN: + /* stmt->start.at_execfn can't be used for now since it is + * currently stored in a location that will be scratched + * by the process (below the final stack pointer). */ + cursor2[1] = at_execfn; + break; + + default: + break; + } + cursor2 += 2; + } while (cursor2[0] != AT_NULL); + + /* Note that only 2 arguments are actually necessary... */ + name = basename(stmt->start.at_execfn); + SYSCALL(PRCTL, 3, PR_SET_NAME, name, 0); + + if (unlikely(traced)) + SYSCALL(EXECVE, 6, 1, + stmt->start.stack_pointer, + stmt->start.entry_point, 2, 3, 4); + else + BRANCH(stmt->start.stack_pointer, stmt->start.entry_point); + FATAL(); + } + + default: + FATAL(); + } + } + + FATAL(); +}
diff --git a/5.1.0/.pc/disable-seccomp-based-tracing-performanc/src/GNUmakefile b/5.1.0/.pc/disable-seccomp-based-tracing-performanc/src/GNUmakefile new file mode 100644 index 0000000..e230a18 --- /dev/null +++ b/5.1.0/.pc/disable-seccomp-based-tracing-performanc/src/GNUmakefile
@@ -0,0 +1,242 @@ +# If you want to build outside of the source tree, use the -f option: +# make -f ${SOMEWHERE}/proot/src/GNUmakefile + +# the VPATH variable must point to the actual makefile directory +VPATH := $(dir $(lastword $(MAKEFILE_LIST))) +SRC = $(dir $(firstword $(MAKEFILE_LIST))) + +GIT = git +RM = rm +INSTALL = install +CC = $(CROSS_COMPILE)gcc +LD = $(CC) +STRIP = $(CROSS_COMPILE)strip +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump + +CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I. -I$(VPATH) +CFLAGS += -Wall -Wextra -O2 +LDFLAGS += -ltalloc + +CARE_LDFLAGS = -larchive + +OBJECTS += \ + cli/cli.o \ + cli/proot.o \ + cli/note.o \ + execve/enter.o \ + execve/exit.o \ + execve/shebang.o \ + execve/elf.o \ + execve/ldso.o \ + execve/auxv.o \ + execve/aoxp.o \ + path/binding.o \ + path/glue.o \ + path/canon.o \ + path/path.o \ + path/proc.o \ + path/temp.o \ + syscall/seccomp.o \ + syscall/syscall.o \ + syscall/chain.o \ + syscall/enter.o \ + syscall/exit.o \ + syscall/sysnum.o \ + syscall/socket.o \ + syscall/heap.o \ + tracee/tracee.o \ + tracee/mem.o \ + tracee/reg.o \ + tracee/event.o \ + ptrace/ptrace.o \ + ptrace/user.o \ + ptrace/wait.o \ + extension/extension.o \ + extension/kompat/kompat.o \ + extension/fake_id0/fake_id0.o \ + loader/loader-wrapped.o + +define define_from_arch.h +$2$1 := $(shell $(CC) $1 -E -dM -DNO_LIBC_HEADER $(SRC)/arch.h | grep -w $2 | cut -f 3 -d ' ') +endef + +$(eval $(call define_from_arch.h,,HAS_LOADER_32BIT)) + +ifdef HAS_LOADER_32BIT + OBJECTS += loader/loader-m32-wrapped.o +endif + +CARE_OBJECTS = \ + cli/care.o \ + cli/care-manual.o \ + extension/care/care.o \ + extension/care/final.o \ + extension/care/extract.o \ + extension/care/archive.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 = process_vm seccomp_filter +CHECK_PROGRAMS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature)) +CHECK_OBJECTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).o) +CHECK_RESULTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).res) + +.SILENT .IGNORE .INTERMEDIATE: $(CHECK_OBJECTS) $(CHECK_PROGRAMS) + +.check_%.o: .check_%.c + -$(COMPILE:echo=false) $(silently) + +.check_%: .check_%.o + -$(LINK:echo=false) $(silently) + +.check_%.res: .check_% + $(Q)if [ -e $< ]; then echo "#define HAVE_$(shell echo $* | tr a-z A-Z)" > $@; else echo "" > $@; fi + +build.h: $(CHECK_RESULTS) + $($(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)cat $^ >> $@ + $(Q)echo "#endif /* BUILD_H */" >> $@ + +BUILD_ID_NONE := $(shell if ld --build-id=none --version >/dev/null 2>&1; then echo ',--build-id=none'; fi) + +###################################################################### +# Build rules + +COMPILE = $($(quiet)CC) $(CPPFLAGS) $(CFLAGS) -MD -c $(SRC)$< -o $@ +LINK = $($(quiet)LD) -o $@ $^ $(LDFLAGS) + +OBJIFY = $($(quiet)GEN) \ + $(OBJCOPY) \ + --input binary \ + --output `env LANG=C $(OBJDUMP) -f cli/cli.o | \ + grep 'file format' | awk '{print $$4}'` \ + --binary-architecture `env LANG=C $(OBJDUMP) -f cli/cli.o | \ + grep architecture | cut -f 1 -d , | awk '{print $$2}'` \ + $< $@ + +proot: $(OBJECTS) + $(LINK) + +care: $(OBJECTS) $(CARE_OBJECTS) + $(LINK) $(CARE_LDFLAGS) + +# Special case to compute which files depend on the auto-generated +# file "build.h". +USE_BUILD_H := $(patsubst $(SRC)%.c,%.o,$(shell egrep -sl 'include[[:space:]]+"build.h"' $(patsubst %.o,$(SRC)%.c,$(OBJECTS) $(CARE_OBJECTS)))) +$(USE_BUILD_H): build.h + +%.o: %.c + @mkdir -p $(dir $@) + $(COMPILE) + +.INTERMEDIATE: manual +manual: $(VPATH)/../doc/care/manual.txt + $(Q)cp $< $@ + +cli/care-manual.o: manual cli/cli.o + $(OBJIFY) + +cli/%-licenses.o: licenses cli/cli.o + $(OBJIFY) + +###################################################################### +# Build rules for the loader + +define build_loader +LOADER$1_OBJECTS = loader/loader$1.o loader/assembly$1.o + +$(eval $(call define_from_arch.h,$1,LOADER_ARCH_CFLAGS)) +$(eval $(call define_from_arch.h,$1,LOADER_ADDRESS)) + +LOADER_CFLAGS$1 += -fPIC -ffreestanding $(LOADER_ARCH_CFLAGS$1) +LOADER_LDFLAGS$1 += -static -nostdlib -Wl$(BUILD_ID_NONE),-Ttext=$(LOADER_ADDRESS$1) + +loader/loader$1.o: loader/loader.c + @mkdir -p $$(dir $$@) + $$(COMPILE) $1 $$(LOADER_CFLAGS$1) + +loader/assembly$1.o: loader/assembly.S + @mkdir -p $$(dir $$@) + $$(COMPILE) $1 $$(LOADER_CFLAGS$1) + +loader/loader$1: $$(LOADER$1_OBJECTS) + $$($$(quiet)LD) $1 -o $$@ $$^ $$(LOADER_LDFLAGS$1) + +.INTERMEDIATE: loader$1.exe +loader$1.exe: loader/loader$1 + $$(Q)cp $$< $$@ + $$(Q)$(STRIP) $$@ + +loader/loader$1-wrapped.o: loader$1.exe cli/cli.o + $$(OBJIFY) +endef + +$(eval $(build_loader)) + +ifdef HAS_LOADER_32BIT +$(eval $(call build_loader,-m32)) +endif + +###################################################################### +# Dependencies + +.DELETE_ON_ERROR: +$(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS): $(firstword $(MAKEFILE_LIST)) + +DEPS = $(OBJECTS:.o=.d) $(CARE_OBJECTS:.o=.d) $(LOADER_OBJECTS:.o=.d) $(LOADER-m32_OBJECTS:.o=.d) $(CHECK_OBJECTS:.o=.d) +-include $(DEPS) + +###################################################################### +# PHONY targets + +PREFIX = /usr/local +DESTDIR = $(PREFIX)/bin + +.PHONY: clean distclean install install-care uninstall +clean distclean: + -$(RM) -f $(CHECK_OBJECTS) $(CHECK_PROGRAMS) $(CHECK_RESULTS) $(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS) proot care loader/loader loader/loader-m32 cli/care-manual.o $(DEPS) build.h licenses + +install: proot + $($(quiet)INSTALL) -D $< $(DESTDIR)/usr/bin/$< + +install-care: care + $($(quiet)INSTALL) -D $< $(DESTDIR)/$< + +uninstall: + -$(RM) -f $(DESTDIR)/usr/bin/proot + +uninstall-care: + -$(RM) -f $(DESTDIR)/care
diff --git a/5.1.0/.pc/fix-use-of-size/src/execve/enter.c b/5.1.0/.pc/fix-use-of-size/src/execve/enter.c new file mode 100644 index 0000000..8f22d9c --- /dev/null +++ b/5.1.0/.pc/fix-use-of-size/src/execve/enter.c
@@ -0,0 +1,667 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* lstat(2), lseek(2), */ +#include <sys/stat.h> /* lstat(2), lseek(2), fchmod(2), */ +#include <unistd.h> /* access(2), lstat(2), close(2), read(2), */ +#include <errno.h> /* E*, */ +#include <assert.h> /* assert(3), */ +#include <talloc.h> /* talloc*, */ +#include <sys/mman.h> /* PROT_*, */ +#include <string.h> /* strlen(3), strcpy(3), */ +#include <stdlib.h> /* getenv(3), */ +#include <stdio.h> /* fwrite(3), */ +#include <assert.h> /* assert(3), */ + +#include "execve/execve.h" +#include "execve/shebang.h" +#include "execve/aoxp.h" +#include "execve/ldso.h" +#include "execve/elf.h" +#include "path/path.h" +#include "path/temp.h" +#include "tracee/tracee.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "arch.h" +#include "cli/note.h" + +#define P(a) PROGRAM_FIELD(load_info->elf_header, *program_header, a) + +/** + * Add @program_header (type PT_LOAD) to @load_info->mappings. This + * function returns -errno if an error occured, otherwise it returns + * 0. + */ +static int add_mapping(const Tracee *tracee UNUSED, LoadInfo *load_info, + const ProgramHeader *program_header) +{ + size_t index; + word_t start_address; + word_t end_address; + static word_t page_size = 0; + static word_t page_mask = 0; + + if (page_size == 0) { + page_size = sysconf(_SC_PAGE_SIZE); + if ((int) page_size <= 0) + page_size = 0x1000; + page_mask = ~(page_size - 1); + } + + if (load_info->mappings == NULL) + index = 0; + else + index = talloc_array_length(load_info->mappings); + + load_info->mappings = talloc_realloc(load_info, load_info->mappings, Mapping, index + 1); + if (load_info->mappings == NULL) + return -ENOMEM; + + start_address = P(vaddr) & page_mask; + end_address = (P(vaddr) + P(filesz) + page_size) & page_mask; + + load_info->mappings[index].fd = -1; /* Unknown yet. */ + load_info->mappings[index].offset = P(offset) & page_mask; + load_info->mappings[index].addr = start_address; + load_info->mappings[index].length = end_address - start_address; + load_info->mappings[index].flags = MAP_PRIVATE | MAP_FIXED; + load_info->mappings[index].prot = ( (P(flags) & PF_R ? PROT_READ : 0) + | (P(flags) & PF_W ? PROT_WRITE : 0) + | (P(flags) & PF_X ? PROT_EXEC : 0)); + + /* "If the segment's memory size p_memsz is larger than the + * file size p_filesz, the "extra" bytes are defined to hold + * the value 0 and to follow the segment's initialized area." + * -- man 7 elf. */ + if (P(memsz) > P(filesz)) { + /* How many extra bytes in the current page? */ + load_info->mappings[index].clear_length = end_address - P(vaddr) - P(filesz); + + /* Create new pages for the remaining extra bytes. */ + start_address = end_address; + end_address = (P(vaddr) + P(memsz) + page_size) & page_mask; + if (end_address > start_address) { + index++; + load_info->mappings = talloc_realloc(load_info, load_info->mappings, + Mapping, index + 1); + if (load_info->mappings == NULL) + return -ENOMEM; + + load_info->mappings[index].fd = -1; /* Anonymous. */ + load_info->mappings[index].offset = 0; + load_info->mappings[index].addr = start_address; + load_info->mappings[index].length = end_address - start_address; + load_info->mappings[index].clear_length = 0; + load_info->mappings[index].flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + load_info->mappings[index].prot = load_info->mappings[index - 1].prot; + } + } + else + load_info->mappings[index].clear_length = 0; + + return 0; +} + +/** + * Translate @user_path into @host_path and check if this latter exists, is + * executable and is a regular file. This function returns -errno if + * an error occured, 0 otherwise. + */ +int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path) +{ + struct stat statl; + int status; + + if (user_path[0] == '\0') + return -ENOEXEC; + + status = translate_path(tracee, host_path, AT_FDCWD, user_path, true); + if (status < 0) + return status; + + status = access(host_path, F_OK); + if (status < 0) + return -ENOENT; + + status = access(host_path, X_OK); + if (status < 0) + return -EACCES; + + status = lstat(host_path, &statl); + if (status < 0) + return -EPERM; + + return 0; +} + +/** + * Add @program_header (type PT_INTERP) to @load_info->interp. This + * function returns -errno if an error occured, otherwise it returns + * 0. + */ +static int add_interp(Tracee *tracee, int fd, LoadInfo *load_info, + const ProgramHeader *program_header) +{ + char host_path[PATH_MAX]; + char *user_path; + int status; + + /* Only one PT_INTERP segment is allowed. */ + if (load_info->interp != NULL) + return -EINVAL; + + load_info->interp = talloc_zero(load_info, LoadInfo); + if (load_info->interp == NULL) + return -ENOMEM; + + user_path = talloc_size(tracee->ctx, P(filesz) + 1); + if (user_path == NULL) + return -ENOMEM; + + /* Remember pread(2) doesn't change the + * current position in the file. */ + status = pread(fd, user_path, P(filesz), P(offset)); + if ((size_t) status != P(filesz)) /* Unexpected size. */ + status = -EACCES; + if (status < 0) + return status; + + user_path[P(filesz)] = '\0'; + + /* When a QEMU command was specified: + * + * - if it's a foreign binary we are reading the ELF + * interpreter of QEMU instead. + * + * - if it's a host binary, we are reading its ELF + * interpreter. + * + * In both case, it lies in "/host-rootfs" from a guest + * point-of-view. */ + if (tracee->qemu != NULL && user_path[0] == '/') { + user_path = talloc_asprintf(tracee->ctx, "%s%s", HOST_ROOTFS, user_path); + if (user_path == NULL) + return -ENOMEM; + } + + status = translate_and_check_exec(tracee, host_path, user_path); + if (status < 0) + return status; + + load_info->interp->host_path = talloc_strdup(load_info->interp, host_path); + if (load_info->interp->host_path == NULL) + return -ENOMEM; + + load_info->interp->user_path = talloc_strdup(load_info->interp, user_path); + if (load_info->interp->user_path == NULL) + return -ENOMEM; + + return 0; +} + +#undef P + +struct add_load_info_data { + LoadInfo *load_info; + Tracee *tracee; + int fd; +}; + +/** + * This function is a program header iterator. It invokes + * add_mapping() or add_interp(), according to the type of + * @program_header. This function returns -errno if an error + * occurred, otherwise 0. + */ +static int add_load_info(const ElfHeader *elf_header, + const ProgramHeader *program_header, void *data_) +{ + struct add_load_info_data *data = data_; + int status; + + switch (PROGRAM_FIELD(*elf_header, *program_header, type)) { + case PT_LOAD: + status = add_mapping(data->tracee, data->load_info, program_header); + if (status < 0) + return status; + break; + + case PT_INTERP: + status = add_interp(data->tracee, data->fd, data->load_info, program_header); + if (status < 0) + return status; + break; + + default: + break; + } + + return 0; +} + +/** + * Extract the load info from @load->host_path. This function returns + * -errno if an error occured, otherwise it returns 0. + */ +static int extract_load_info(Tracee *tracee, LoadInfo *load_info) +{ + struct add_load_info_data data; + int fd = -1; + int status; + + assert(load_info != NULL); + assert(load_info->host_path != NULL); + + fd = open_elf(load_info->host_path, &load_info->elf_header); + if (fd < 0) + return fd; + + /* Sanity check. */ + switch (ELF_FIELD(load_info->elf_header, type)) { + case ET_EXEC: + case ET_DYN: + break; + + default: + status = -EINVAL; + goto end; + } + + data.load_info = load_info; + data.tracee = tracee; + data.fd = fd; + + status = iterate_program_headers(tracee, fd, &load_info->elf_header, add_load_info, &data); +end: + if (fd >= 0) + close(fd); + + return status; +} + +/** + * Add @load_base to each adresses of @load_info. + */ +static void add_load_base(LoadInfo *load_info, word_t load_base) +{ + size_t nb_mappings; + size_t i; + + nb_mappings = talloc_array_length(load_info->mappings); + for (i = 0; i < nb_mappings; i++) + load_info->mappings[i].addr += load_base; + + if (IS_CLASS64(load_info->elf_header)) + load_info->elf_header.class64.e_entry += load_base; + else + load_info->elf_header.class32.e_entry += load_base; +} + +/** + * Compute the final load address for each position independant + * objects of @tracee. + * + * TODO: support for ASLR. + */ +static void compute_load_addresses(Tracee *tracee) +{ + if (IS_POSITION_INDENPENDANT(tracee->load_info->elf_header) + && tracee->load_info->mappings[0].addr == 0) { +#if defined(HAS_LOADER_32BIT) + if (IS_CLASS32(tracee->load_info->elf_header)) + add_load_base(tracee->load_info, EXEC_PIC_ADDRESS_32); + else +#endif + add_load_base(tracee->load_info, EXEC_PIC_ADDRESS); + } + + /* Nothing more to do? */ + if (tracee->load_info->interp == NULL) + return; + + if (IS_POSITION_INDENPENDANT(tracee->load_info->interp->elf_header) + && tracee->load_info->interp->mappings[0].addr == 0) { +#if defined(HAS_LOADER_32BIT) + if (IS_CLASS32(tracee->load_info->elf_header)) + add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS_32); + else +#endif + add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS); + } +} + +/** + * Expand in argv[] and envp[] the runner for @user_path, if needed. + * This function returns -errno if an error occurred, otherwise 0. On + * success, both @host_path and @user_path point to the program to + * execute (respectively from host and guest point-of-views), and both + * @tracee's argv[] (pointed to by SYSARG_2) @tracee's envp[] (pointed + * to by SYSARG_3) are correctly updated. + */ +static int expand_runner(Tracee* tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]) +{ + ArrayOfXPointers *envp; + char *argv0; + int status; + + /* Execution of host programs when QEMU is in use relies on + * LD_ environment variables. */ + status = fetch_array_of_xpointers(tracee, &envp, SYSARG_3, 0); + if (status < 0) + return status; + + /* Environment variables should be compared with the "name" + * part of the "name=value" string format. */ + envp->compare_xpointee = (compare_xpointee_t) compare_xpointee_env; + + /* No need to adjust argv[] if it's a host binary (a.k.a + * mixed-mode). */ + if (!is_host_elf(tracee, host_path)) { + ArrayOfXPointers *argv; + size_t nb_qemu_args; + size_t i; + + status = fetch_array_of_xpointers(tracee, &argv, SYSARG_2, 0); + if (status < 0) + return status; + + status = read_xpointee_as_string(argv, 0, &argv0); + if (status < 0) + return status; + + /* Assuming PRoot was invoked this way: + * + * proot -q 'qemu-arm -cpu cortex-a9' ... + * + * a call to: + * + * execve("/bin/true", { "true", NULL }, ...) + * + * becomes: + * + * execve("/usr/bin/qemu", + * { "qemu", "-cpu", "cortex-a9", "-0", "true", "/bin/true", NULL }, ...) + */ + + nb_qemu_args = talloc_array_length(tracee->qemu) - 1; + status = resize_array_of_xpointers(argv, 1, nb_qemu_args + 2); + if (status < 0) + return status; + + for (i = 0; i < nb_qemu_args; i++) { + status = write_xpointee(argv, i, tracee->qemu[i]); + if (status < 0) + return status; + } + + status = write_xpointees(argv, i, 3, "-0", argv0, user_path); + if (status < 0) + return status; + + /* Ensure LD_ features should not be applied to QEMU + * iteself. */ + status = ldso_env_passthru(tracee, envp, argv, "-E", "-U", i); + if (status < 0) + return status; + + status = push_array_of_xpointers(argv, SYSARG_2); + if (status < 0) + return status; + + /* Launch the runner in lieu of the initial + * program. */ + assert(strlen(tracee->qemu[0]) + strlen(HOST_ROOTFS) < PATH_MAX); + assert(tracee->qemu[0][0] == '/'); + + strcpy(host_path, tracee->qemu[0]); + + strcpy(user_path, HOST_ROOTFS); + strcat(user_path, host_path); + } + + /* 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, host_path, envp); + if (status < 0) + return status; + + status = push_array_of_xpointers(envp, SYSARG_3); + if (status < 0) + return status; + + return 0; +} + +extern unsigned char _binary_loader_exe_start; +extern unsigned char _binary_loader_exe_size; + +extern unsigned char WEAK _binary_loader_m32_exe_start; +extern unsigned char WEAK _binary_loader_m32_exe_size; + +/** + * Extract the built-in loader. This function returns NULL if an + * error occurred, otherwise it returns the path to the extracted + * loader. Note: @tracee is only used for notification purpose. + */ +static char *extract_loader(const Tracee *tracee, bool wants_32bit_version) +{ + char path[PATH_MAX]; + size_t status2; + void *start; + size_t size; + int status; + int fd; + + char *loader_path = NULL; + FILE *file = NULL; + + file = open_temp_file(NULL, "prooted"); + if (file == NULL) + goto end; + fd = fileno(file); + + if (wants_32bit_version) { + start = (void *) &_binary_loader_m32_exe_start; + size = (size_t) &_binary_loader_m32_exe_size; + } + else { + start = (void *) &_binary_loader_exe_start; + size = (size_t) &_binary_loader_exe_size; + } + + status2 = write(fd, start, size); + if (status2 != size) { + note(tracee, ERROR, SYSTEM, "can't write the loader"); + goto end; + } + + status = fchmod(fd, S_IRUSR|S_IXUSR); + if (status < 0) { + note(tracee, ERROR, SYSTEM, "can't change loader permissions (u+rx)"); + goto end; + } + + status = readlink_proc_pid_fd(getpid(), fd, path); + if (status < 0) { + note(tracee, ERROR, INTERNAL, "can't retrieve loader path (/proc/self/fd/)"); + goto end; + } + + loader_path = talloc_strdup(talloc_autofree_context(), path); + if (loader_path == NULL) { + note(tracee, ERROR, INTERNAL, "can't allocate memory"); + goto end; + } + + if (tracee->verbose >= 2) + note(tracee, INFO, INTERNAL, "loader: %s", loader_path); + +end: + if (file != NULL) { + status = fclose(file); + if (status < 0) + note(tracee, WARNING, SYSTEM, "can't close loader file"); + } + + return loader_path; +} + +/** + * Get the path to the loader for the given @tracee. This function + * returns NULL if an error occurred. + */ +static inline const char *get_loader_path(const Tracee *tracee) +{ + static char *loader_path = NULL; + +#if defined(HAS_LOADER_32BIT) + static char *loader32_path = NULL; + + if (IS_CLASS32(tracee->load_info->elf_header)) { + loader32_path = loader32_path ?: getenv("PROOT_LOADER_32") ?: extract_loader(tracee, true); + return loader32_path; + } + else +#endif + { + loader_path = loader_path ?: getenv("PROOT_LOADER") ?: extract_loader(tracee, false); + return loader_path; + } +} + +/** + * Extract all the information that will be required by + * translate_load_*(). This function returns -errno if an error + * occured, otherwise 0. + */ +int translate_execve_enter(Tracee *tracee) +{ + char user_path[PATH_MAX]; + char host_path[PATH_MAX]; + char new_exe[PATH_MAX]; + char *raw_path; + const char *loader_path; + int status; + + if (IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee)) { + /* Syscalls can now be reported to its ptracer. */ + tracee->as_ptracee.ignore_loader_syscalls = false; + + /* Cancel this spurious execve, it was only used as a + * notification. */ + set_sysnum(tracee, PR_void); + return 0; + } + + status = get_sysarg_path(tracee, user_path, SYSARG_1); + if (status < 0) + return status; + + /* Remember the user path before it is overwritten by + * expand_shebang(). This "raw" path is useful to fix the + * value of AT_EXECFN and /proc/{@tracee->pid}/comm. */ + raw_path = talloc_strdup(tracee->ctx, user_path); + if (raw_path == NULL) + return -ENOMEM; + + status = expand_shebang(tracee, host_path, user_path); + if (status < 0) + /* The Linux kernel actually returns -EACCES when + * trying to execute a directory. */ + return status == -EISDIR ? -EACCES : status; + + /* user_path is modified only if there's an interpreter + * (ie. for a script or with qemu). */ + if (status == 0 && tracee->qemu == NULL) + TALLOC_FREE(raw_path); + + /* Remember the new value for "/proc/self/exe". It points to + * a canonicalized guest path, hence detranslate_path() + * instead of using user_path directly. */ + strcpy(new_exe, host_path); + status = detranslate_path(tracee, new_exe, NULL); + if (status >= 0) { + talloc_unlink(tracee, tracee->new_exe); + tracee->new_exe = talloc_strdup(tracee, new_exe); + } + else + tracee->new_exe = NULL; + + if (tracee->qemu != NULL) { + status = expand_runner(tracee, host_path, user_path); + if (status < 0) + return status; + } + + TALLOC_FREE(tracee->load_info); + + tracee->load_info = talloc_zero(tracee, LoadInfo); + if (tracee->load_info == NULL) + return -ENOMEM; + + tracee->load_info->host_path = talloc_strdup(tracee->load_info, host_path); + if (tracee->load_info->host_path == NULL) + return -ENOMEM; + + tracee->load_info->user_path = talloc_strdup(tracee->load_info, user_path); + if (tracee->load_info->user_path == NULL) + return -ENOMEM; + + tracee->load_info->raw_path = (raw_path != NULL + ? talloc_reparent(tracee->ctx, tracee->load_info, raw_path) + : talloc_reference(tracee->load_info, tracee->load_info->user_path)); + if (tracee->load_info->raw_path == NULL) + return -ENOMEM; + + status = extract_load_info(tracee, tracee->load_info); + if (status < 0) + return status; + + if (tracee->load_info->interp != NULL) { + status = extract_load_info(tracee, tracee->load_info->interp); + if (status < 0) + return status; + + /* An ELF interpreter is supposed to be + * standalone. */ + if (tracee->load_info->interp->interp != NULL) + return -EINVAL; + } + + compute_load_addresses(tracee); + + /* Execute the loader instead of the program. */ + loader_path = get_loader_path(tracee); + if (loader_path == NULL) + return -ENOENT; + + status = set_sysarg_path(tracee, loader_path, SYSARG_1); + if (status < 0) + return status; + + /* Mask to its ptracer syscalls performed by the loader. */ + tracee->as_ptracee.ignore_loader_syscalls = true; + + return 0; +}
diff --git a/5.1.0/.travis.yml b/5.1.0/.travis.yml new file mode 100644 index 0000000..86d9639 --- /dev/null +++ b/5.1.0/.travis.yml
@@ -0,0 +1,29 @@ +language: c + +compiler: gcc + +before_install: + - sudo apt-get update -qq + - sudo apt-get install -qq libtalloc-dev uthash-dev libarchive-dev gdb strace realpath + - sudo pip install cpp-coveralls + +script: if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then make -C src loader.exe loader-m32.exe build.h && env CFLAGS=--coverage LDFLAGS='--coverage' make -C src proot && env PATH=/bin:/usr/bin:/sbin:/usr/sbin:$PWD/src make -C tests; fi + +after_success: + - coveralls --build-root src --exclude tests --gcov-options '\-lp' + +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "G2fG/oWcEOiG+RYLCK3XOO/7ZXn4LoiIyOT4fxxJzb43gu/90lEkbwsALqVAAjh/Fn5T4rYqiECeJ0jEhxvWak1pK/jkFshfXzL34yaY8ZLNtwvnFf7+DDtsj1Hx136TEjYuyc/jasoTf/3fNlIX6Oh1BB6c0UtJfHwTfbreRbI=" + +addons: + coverity_scan: + project: + name: "cedric-vincent/PRoot" + description: "PRoot" + notification_email: cedric.vincent@gmail.com + build_command_prepend: + build_command: make -C src + branch_pattern: coverity_scan
diff --git a/5.1.0/AUTHORS b/5.1.0/AUTHORS new file mode 100644 index 0000000..dceedc3 --- /dev/null +++ b/5.1.0/AUTHORS
@@ -0,0 +1,32 @@ +The copyright holder for PRoot and CARE is STMicroelectronics, these +tools are developed in the Compilation Expertise Center by: + +Cédric VINCENT <cedric.vincent@st.com> + Original author, maintainer. + +Rémi DURAFFORT <remi.duraffort@st.com> + Improved the -0 feature, many experiments, bug reports, fixes, and articles. + +Christophe GUILLON <christophe.guillon@st.com> + Improved CARE, plugin experiments, bug reports, and fixes. + +Yves JANIN <yves.janin@st.com> + Support for the i386 architecture, many experiments and bug reports. + +Antoine MOYNAULT <antoine.moynault@st.com> + Many experiments and bug reports. + +Claire ROBINE <claire.robine@st.com> + Many bug fixes. + +Clément BAZIN <clement@bazin-fr.org> + Plugins interface, dependency tracker plugin, and bug reports. + +Christian BERTIN <christian.bertin@st.com> + Valuable support. + +Denis FERRANTI <denis.ferranti@st.com> + Valuable support. + +Paul GHALEB <paul.ghaleb@st.com> + User manual review.
diff --git a/5.1.0/COPYING b/5.1.0/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/5.1.0/COPYING
@@ -0,0 +1,339 @@ + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + <signature of Ty Coon>, 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.
diff --git a/5.1.0/README.rst b/5.1.0/README.rst new file mode 100644 index 0000000..7075607 --- /dev/null +++ b/5.1.0/README.rst
@@ -0,0 +1,19 @@ +Manuals +======= + +- `PRoot <doc/proot/manual.txt>`_ + +- `CARE <doc/care/manual.txt>`_ + + +Build status +============ + +- .. image:: https://travis-ci.org/cedric-vincent/PRoot.png?branch=master + :target: https://travis-ci.org/cedric-vincent/PRoot + +- .. image:: https://coveralls.io/repos/cedric-vincent/PRoot/badge.png?branch=master + :target: https://coveralls.io/r/cedric-vincent/PRoot?branch=master + +- .. image:: https://scan.coverity.com/projects/602/badge.svg + :target: https://scan.coverity.com/projects/602
diff --git a/5.1.0/debian/changelog b/5.1.0/debian/changelog new file mode 100644 index 0000000..375b5fe --- /dev/null +++ b/5.1.0/debian/changelog
@@ -0,0 +1,71 @@ +proot (5.1.0-1.3) unstable; urgency=medium + + * Non-maintainer upload. + * Add patch from upstream to add support for renameat2 system call (Closes: + #909581) + + -- Johannes 'josch' Schauer <josch@debian.org> Thu, 13 Dec 2018 06:13:02 +0100 + +proot (5.1.0-1.2) unstable; urgency=medium + + * Non-maintainer upload. + * Add patch from upstream to fix simple invocation like "proot ls" + (Closes: #847292) + * Disable the seccomp-based tracing optimization, which just segfaults + with recent kernels (4.8.4+, according to upstream bug #160) + + -- Simon McVittie <smcv@debian.org> Sat, 14 Jan 2017 15:58:21 +0000 + +proot (5.1.0-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Fix FTBFS on arm64, patch by Sebastian Ramacher (Closes: #788113). + + -- Andrey Rahmatullin <wrar@debian.org> Sat, 26 Nov 2016 22:12:08 +0500 + +proot (5.1.0-1) unstable; urgency=low + * Upgrade to latest PRoot version + - PRoot is now embedding is own ELF interpreter + + -- Rémi Duraffort <ivoire@videolan.org> Tue, 27 Jan 2015 14:28:42 +0200 + +proot (4.0.2-1) unstable; urgency=low + * Upgrade to latest PRoot version + - Experimental port of PRoot for AArch64 + * Silent one wrong litian warning + - proot binary: spelling-error-in-binary usr/bin/proot tEH the + + -- Rémi Duraffort <ivoire@videolan.org> Fri, 12 Sep 2014 09:59:21 +0200 + +proot (4.0.1-1) unstable; urgency=low + * Upgrade to latest PRoot version + - Fix issue with CWD (Closes: #756935) + * debian/patches/Fix-man-syntax.diff: correct typographic mistakes in the + manpage + + -- Rémi Duraffort <ivoire@videolan.org> Wed, 06 Aug 2014 13:02:57 +0200 + +proot (4.0.0-1) unstable; urgency=low + * Upgrade to latest PRoot version + + -- Rémi Duraffort <ivoire@videolan.org> Tue, 08 Jul 2014 15:18:03 +0200 + +proot (3.2.2-1) unstable; urgency=low + + * Upgrade to latest PRoot version (Closes: #730363) + * Only build for supported architectures (Closes: #733247) + * Silent one wrong lintian warning + * Upgrade Standards Version to 3.9.5 + * Use DEP-3 format for patches + * Fix copyright holders + * Build with hardening + * Print the full command line while building + * Add a watch file + + -- Rémi Duraffort <ivoire@videolan.org> Wed, 29 Jan 2014 13:15:20 +0200 + +proot (3.0.2-1) unstable; urgency=low + + * Initial release (Closes: #701894) + + -- Rémi Duraffort <ivoire@videolan.org> Wed, 03 Jul 2013 11:13:33 +0200
diff --git a/5.1.0/debian/compat b/5.1.0/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/5.1.0/debian/compat
@@ -0,0 +1 @@ +9
diff --git a/5.1.0/debian/control b/5.1.0/debian/control new file mode 100644 index 0000000..bbd699e --- /dev/null +++ b/5.1.0/debian/control
@@ -0,0 +1,27 @@ +Source: proot +Section: utils +Priority: optional +Maintainer: Rémi Duraffort <ivoire@videolan.org> +Build-Depends: debhelper (>= 9.0.0), libtalloc-dev +Standards-Version: 3.9.5 +Homepage: http://proot.me +Vcs-Git: git://github.com/ivoire/PRoot-debian +Vcs-Browser: https://github.com/ivoire/proot-debian + +Package: proot +Architecture: amd64 arm64 armel armhf i386 sh4 x32 +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: emulate chroot, bind mount and binfmt_misc for non-root users + 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.
diff --git a/5.1.0/debian/copyright b/5.1.0/debian/copyright new file mode 100644 index 0000000..3f1e383 --- /dev/null +++ b/5.1.0/debian/copyright
@@ -0,0 +1,28 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: proot +Source: http://proot.me + +Files: * +Copyright: 2013, STMicroelectronics +License: GPL-2+ + +Files: debian/* +Copyright: 2013, Rémi Duraffort <ivoire@videolan.org> +License: GPL-2+ + +License: GPL-2+ + This package 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 package 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, see <http://www.gnu.org/licenses/> + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
diff --git a/5.1.0/debian/docs b/5.1.0/debian/docs new file mode 100644 index 0000000..496ece0 --- /dev/null +++ b/5.1.0/debian/docs
@@ -0,0 +1 @@ +doc/proot/manual.txt
diff --git a/5.1.0/debian/patches/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch b/5.1.0/debian/patches/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch new file mode 100644 index 0000000..af38669 --- /dev/null +++ b/5.1.0/debian/patches/0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch
@@ -0,0 +1,202 @@ +From fd417981bea00250128274a4f2d196ee1c10c6ea Mon Sep 17 00:00:00 2001 +From: Kimon Hoffmann <Kimon.Hoffmann@lawo.com> +Date: Wed, 10 Oct 2018 13:10:07 +0200 +Subject: [PATCH] Properly filter the renameat2 syscall introduced in Linux + v3.15. + +Handle the renameat2 syscall analogous to renameat, as their only +difference is an additional flags attribute, which isn't relevant to +the path based adjustments performed by proot. + +Removed erroneous syscall entry from arm64 table. +--- + src/extension/kompat/kompat.c | 19 +++++++++++++++++++ + src/syscall/enter.c | 1 + + src/syscall/exit.c | 3 ++- + src/syscall/seccomp.c | 1 + + src/syscall/sysnums-arm.h | 5 +++++ + src/syscall/sysnums-arm64.h | 5 ++++- + src/syscall/sysnums-i386.h | 4 ++++ + src/syscall/sysnums-sh4.h | 5 +++++ + src/syscall/sysnums-x32.h | 4 ++++ + src/syscall/sysnums-x86_64.h | 4 ++++ + src/syscall/sysnums.list | 5 ++++- + 11 files changed, 53 insertions(+), 3 deletions(-) + +--- a/src/extension/kompat/kompat.c ++++ b/src/extension/kompat/kompat.c +@@ -509,6 +509,24 @@ static int handle_sysenter_end(Tracee *t + return 0; + } + ++ case PR_renameat2: { ++ Modif modif = { ++ .expected_release = KERNEL_VERSION(3,15,0), ++ .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: { + bool modified; + Modif modif = { +@@ -968,6 +986,7 @@ static FilteredSysnum filtered_sysnums[] + { PR_pselect6, 0 }, + { PR_readlinkat, 0 }, + { PR_renameat, 0 }, ++ { PR_renameat2, 0 }, + { PR_setdomainname, FILTER_SYSEXIT }, + { PR_sethostname, FILTER_SYSEXIT }, + { PR_signalfd4, FILTER_SYSEXIT }, +--- a/src/syscall/enter.c ++++ b/src/syscall/enter.c +@@ -532,6 +532,7 @@ int translate_syscall_enter(Tracee *trac + break; + + case PR_renameat: ++ case PR_renameat2: + olddirfd = peek_reg(tracee, CURRENT, SYSARG_1); + newdirfd = peek_reg(tracee, CURRENT, SYSARG_3); + +--- a/src/syscall/exit.c ++++ b/src/syscall/exit.c +@@ -231,7 +231,8 @@ void translate_syscall_exit(Tracee *trac + break; + + case PR_rename: +- case PR_renameat: { ++ case PR_renameat: ++ case PR_renameat2: { + char old_path[PATH_MAX]; + char new_path[PATH_MAX]; + ssize_t old_length; +--- a/src/syscall/seccomp.c ++++ b/src/syscall/seccomp.c +@@ -383,6 +383,7 @@ static FilteredSysnum proot_sysnums[] = + { PR_removexattr, 0 }, + { PR_rename, FILTER_SYSEXIT }, + { PR_renameat, FILTER_SYSEXIT }, ++ { PR_renameat2, FILTER_SYSEXIT }, + { PR_rmdir, 0 }, + { PR_setxattr, 0 }, + { PR_socketcall, FILTER_SYSEXIT }, +--- a/src/syscall/sysnums-arm.h ++++ b/src/syscall/sysnums-arm.h +@@ -335,4 +335,9 @@ static const Sysnum sysnums_arm[] = { + [ 375 ] = PR_setns, + [ 376 ] = PR_process_vm_readv, + [ 377 ] = PR_process_vm_writev, ++ [ 378 ] = PR_kcmp, ++ [ 379 ] = PR_finit_module, ++ [ 380 ] = PR_sched_setattr, ++ [ 381 ] = PR_sched_getattr, ++ [ 382 ] = PR_renameat2, + }; +--- a/src/syscall/sysnums-arm64.h ++++ b/src/syscall/sysnums-arm64.h +@@ -259,5 +259,8 @@ static const Sysnum sysnums_arm64[] = { + [ 270 ] = PR_process_vm_readv, + [ 271 ] = PR_process_vm_writev, + [ 272 ] = PR_kcmp, +- [ 273 ] = PR_syscalls, ++ [ 273 ] = PR_finit_module, ++ [ 274 ] = PR_sched_setattr, ++ [ 275 ] = PR_sched_getattr, ++ [ 276 ] = PR_renameat2, + }; +--- a/src/syscall/sysnums-i386.h ++++ b/src/syscall/sysnums-i386.h +@@ -347,4 +347,8 @@ static const Sysnum sysnums_i386[] = { + [ 347 ] = PR_process_vm_readv, + [ 348 ] = PR_process_vm_writev, + [ 349 ] = PR_kcmp, ++ [ 350 ] = PR_finit_module, ++ [ 351 ] = PR_sched_setattr, ++ [ 352 ] = PR_sched_getattr, ++ [ 353 ] = PR_renameat2, + }; +--- a/src/syscall/sysnums-sh4.h ++++ b/src/syscall/sysnums-sh4.h +@@ -339,4 +339,9 @@ static const Sysnum sysnums_sh4[] = { + [ 364 ] = PR_setns, + [ 365 ] = PR_process_vm_readv, + [ 366 ] = PR_process_vm_writev, ++ [ 367 ] = PR_kcmp, ++ [ 368 ] = PR_finit_module, ++ [ 369 ] = PR_sched_setattr, ++ [ 370 ] = PR_sched_getattr, ++ [ 371 ] = PR_renameat2, + }; +--- a/src/syscall/sysnums-x32.h ++++ b/src/syscall/sysnums-x32.h +@@ -272,6 +272,10 @@ static const Sysnum sysnums_x32[] = { + [ 308 ] = PR_setns, + [ 309 ] = PR_getcpu, + [ 312 ] = PR_kcmp, ++ [ 313 ] = PR_finit_module, ++ [ 314 ] = PR_sched_setattr, ++ [ 315 ] = PR_sched_getattr, ++ [ 316 ] = PR_renameat2, + [ 512 ] = PR_rt_sigaction, + [ 513 ] = PR_rt_sigreturn, + [ 514 ] = PR_ioctl, +--- a/src/syscall/sysnums-x86_64.h ++++ b/src/syscall/sysnums-x86_64.h +@@ -314,4 +314,8 @@ static const Sysnum sysnums_x86_64[] = { + [ 310 ] = PR_process_vm_readv, + [ 311 ] = PR_process_vm_writev, + [ 312 ] = PR_kcmp, ++ [ 313 ] = PR_finit_module, ++ [ 314 ] = PR_sched_setattr, ++ [ 315 ] = PR_sched_getattr, ++ [ 316 ] = PR_renameat2, + }; +--- a/src/syscall/sysnums.list ++++ b/src/syscall/sysnums.list +@@ -74,6 +74,7 @@ SYSNUM(fcntl) + SYSNUM(fcntl64) + SYSNUM(fdatasync) + SYSNUM(fgetxattr) ++SYSNUM(finit_module) + SYSNUM(flistxattr) + SYSNUM(flock) + SYSNUM(fork) +@@ -251,6 +252,7 @@ SYSNUM(remap_file_pages) + SYSNUM(removexattr) + SYSNUM(rename) + SYSNUM(renameat) ++SYSNUM(renameat2) + SYSNUM(request_key) + SYSNUM(restart_syscall) + SYSNUM(rmdir) +@@ -265,10 +267,12 @@ SYSNUM(rt_tgsigqueueinfo) + SYSNUM(sched_get_priority_max) + SYSNUM(sched_get_priority_min) + SYSNUM(sched_getaffinity) ++SYSNUM(sched_getattr) + SYSNUM(sched_getparam) + SYSNUM(sched_getscheduler) + SYSNUM(sched_rr_get_interval) + SYSNUM(sched_setaffinity) ++SYSNUM(sched_setattr) + SYSNUM(sched_setparam) + SYSNUM(sched_setscheduler) + SYSNUM(sched_yield) +@@ -351,7 +355,6 @@ SYSNUM(sync) + SYSNUM(sync_file_range) + SYSNUM(sync_file_range2) + SYSNUM(syncfs) +-SYSNUM(syscalls) + SYSNUM(sysfs) + SYSNUM(sysinfo) + SYSNUM(syslog)
diff --git a/5.1.0/debian/patches/Fix-man-syntax.diff b/5.1.0/debian/patches/Fix-man-syntax.diff new file mode 100644 index 0000000..a01273e --- /dev/null +++ b/5.1.0/debian/patches/Fix-man-syntax.diff
@@ -0,0 +1,18 @@ +Author: Rémi Duraffort <ivoire@videolan.org> +Description: Correct typographic mistakes in the manpage +Forwarded: yes +Last-Update: 2015-01-27 + +Index: proot-5.1.0/doc/proot/man.1 +=================================================================== +--- proot-5.1.0.orig/doc/proot/man.1 ++++ proot-5.1.0/doc/proot/man.1 +@@ -2,7 +2,7 @@ + . + .TH PROOT 1 "2014-12-12" "5.1.0" "" + .SH NAME +-PRoot \- chroot, mount --bind, and binfmt_misc without privilege/setup ++PRoot \- chroot, mount \-\-bind, and binfmt_misc without privilege/setup + . + .nr rst2man-indent-level 0 + .
diff --git a/5.1.0/debian/patches/Install-proot-into-DESTDIR-usr-bin.patch b/5.1.0/debian/patches/Install-proot-into-DESTDIR-usr-bin.patch new file mode 100644 index 0000000..99a8d2c --- /dev/null +++ b/5.1.0/debian/patches/Install-proot-into-DESTDIR-usr-bin.patch
@@ -0,0 +1,23 @@ +Author: Rémi Duraffort <ivoire@videolan.org> +Description: Install proot into $(DESTDIR)/usr/bin/ +Forwarded: not-needed +Last-Update: 2015-01-27 + +--- a/src/GNUmakefile ++++ b/src/GNUmakefile +@@ -230,13 +230,13 @@ + -$(RM) -f $(CHECK_OBJECTS) $(CHECK_PROGRAMS) $(CHECK_RESULTS) $(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS) proot care loader/loader loader/loader-m32 cli/care-manual.o $(DEPS) build.h licenses + + install: proot +- $($(quiet)INSTALL) -D $< $(DESTDIR)/$< ++ $($(quiet)INSTALL) -D $< $(DESTDIR)/usr/bin/$< + + install-care: care + $($(quiet)INSTALL) -D $< $(DESTDIR)/$< + + uninstall: +- -$(RM) -f $(DESTDIR)/proot ++ -$(RM) -f $(DESTDIR)/usr/bin/proot + + uninstall-care: + -$(RM) -f $(DESTDIR)/care
diff --git a/5.1.0/debian/patches/arm64.patch b/5.1.0/debian/patches/arm64.patch new file mode 100644 index 0000000..7ca3695 --- /dev/null +++ b/5.1.0/debian/patches/arm64.patch
@@ -0,0 +1,134 @@ +diff -ru proot-5.1.0.orig/src/arch.h proot-5.1.0/src/arch.h +--- proot-5.1.0.orig/src/arch.h ++++ proot-5.1.0/src/arch.h +@@ -125,6 +125,9 @@ + #define OFFSETOF_STAT_UID_32 0 + #define OFFSETOF_STAT_GID_32 0 + ++ #define EXEC_PIC_ADDRESS 0x500000000000 ++ #define INTERP_PIC_ADDRESS 0x6f0000000000 ++ + #elif defined(ARCH_X86) + + #define SYSNUMS_HEADER1 "syscall/sysnums-i386.h" +diff -ru proot-5.1.0.orig/src/loader/assembly-arm64.h proot-5.1.0/src/loader/assembly-arm64.h +--- /dev/null ++++ proot-5.1.0/src/loader/assembly-arm64.h +@@ -0,0 +1,93 @@ ++/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- ++ * ++ * This file is part of PRoot. ++ * ++ * Copyright (C) 2014 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 BRANCH(stack_pointer, destination) do { \ ++ asm volatile ( \ ++ "// Restore initial stack pointer. \n\t" \ ++ "mov sp, %0 \n\t" \ ++ " \n\t" \ ++ "// Clear rtld_fini. \n\t" \ ++ "mov x0, #0 \n\t" \ ++ " \n\t" \ ++ "// Start the program. \n\t" \ ++ "br %1 \n" \ ++ : /* no output */ \ ++ : "r" (stack_pointer), "r" (destination) \ ++ : "memory", "sp", "x0"); \ ++ __builtin_unreachable(); \ ++ } while (0) ++ ++#define PREPARE_ARGS_1(arg1_) \ ++ register word_t arg1 asm("x0") = arg1_; \ ++ ++#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ ++ PREPARE_ARGS_1(arg1_) \ ++ register word_t arg2 asm("x1") = arg2_; \ ++ register word_t arg3 asm("x2") = arg3_; \ ++ ++#define PREPARE_ARGS_4(arg1_, arg2_, arg3_, arg4_) \ ++ PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ ++ register word_t arg4 asm("x3") = arg4_; ++ ++#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \ ++ PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ ++ register word_t arg4 asm("x3") = arg4_; \ ++ register word_t arg5 asm("x4") = arg5_; \ ++ register word_t arg6 asm("x5") = arg6_; ++ ++#define OUTPUT_CONTRAINTS_1 \ ++ "r" (arg1) ++ ++#define OUTPUT_CONTRAINTS_3 \ ++ OUTPUT_CONTRAINTS_1, \ ++ "r" (arg2), "r" (arg3) ++ ++#define OUTPUT_CONTRAINTS_4 \ ++ OUTPUT_CONTRAINTS_3, \ ++ "r" (arg4) ++ ++#define OUTPUT_CONTRAINTS_6 \ ++ OUTPUT_CONTRAINTS_3, \ ++ "r" (arg4), "r" (arg5), "r" (arg6) ++ ++#define SYSCALL(number_, nb_args, args...) \ ++ ({ \ ++ register word_t number asm("w8") = number_; \ ++ register word_t result asm("x0"); \ ++ PREPARE_ARGS_##nb_args(args) \ ++ asm volatile ( \ ++ "svc #0x00000000 \n\t" \ ++ : "=r" (result) \ ++ : "r" (number), \ ++ OUTPUT_CONTRAINTS_##nb_args \ ++ : "memory"); \ ++ result; \ ++ }) ++ ++#define OPENAT 56 ++#define CLOSE 57 ++#define MMAP 222 ++#define MMAP_OFFSET_SHIFT 0 ++#define EXECVE 221 ++#define EXIT 93 ++#define PRCTL 167 ++ +diff -ru proot-5.1.0.orig/src/loader/loader.c proot-5.1.0/src/loader/loader.c +--- proot-5.1.0.orig/src/loader/loader.c ++++ proot-5.1.0/src/loader/loader.c +@@ -39,6 +39,8 @@ + # include "loader/assembly-x86_64.h" + #elif defined(ARCH_ARM_EABI) + # include "loader/assembly-arm.h" ++#elif defined(ARCH_ARM64) ++# include "loader/assembly-arm64.h" + #elif defined(ARCH_X86) + # include "loader/assembly-x86.h" + #else +@@ -134,7 +136,11 @@ + /* Fall through. */ + + case LOAD_ACTION_OPEN: ++#ifdef OPENAT ++ fd = SYSCALL(OPENAT, 4, AT_FDCWD, stmt->open.string_address, O_RDONLY, 0); ++#else + fd = SYSCALL(OPEN, 3, stmt->open.string_address, O_RDONLY, 0); ++#endif + if (unlikely((int) fd < 0)) + FATAL(); +
diff --git a/5.1.0/debian/patches/disable-seccomp-based-tracing-performanc b/5.1.0/debian/patches/disable-seccomp-based-tracing-performanc new file mode 100644 index 0000000..c340de6 --- /dev/null +++ b/5.1.0/debian/patches/disable-seccomp-based-tracing-performanc
@@ -0,0 +1,24 @@ +From: Simon McVittie <smcv@debian.org> +Date: Sat, 14 Jan 2017 15:52:13 +0000 +X-Dgit-Generated: 5.1.0-1.2 dcc5f0999759be03aed5b62a8683e0b965d0219d +Subject: Disable seccomp-based tracing performance improvement + +It is faster (according to upstream documentation), but on current +(4.8.4+) kernels it just segfaults. Software that works slowly seems +better than software that doesn't work at all. + +Bug: https://github.com/proot-me/PRoot/issues/106 + +--- + +--- proot-5.1.0.orig/src/GNUmakefile ++++ proot-5.1.0/src/GNUmakefile +@@ -105,7 +105,7 @@ CHECK_VERSION = VERSION=$$($(GIT) descri + then /bin/echo -e "\#undef VERSION\n\#define VERSION \"$${VERSION}\""; \ + fi; + +-CHECK_FEATURES = process_vm seccomp_filter ++CHECK_FEATURES = process_vm + CHECK_PROGRAMS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature)) + CHECK_OBJECTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).o) + CHECK_RESULTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).res)
diff --git a/5.1.0/debian/patches/fix-use-of-size b/5.1.0/debian/patches/fix-use-of-size new file mode 100644 index 0000000..53d001b --- /dev/null +++ b/5.1.0/debian/patches/fix-use-of-size
@@ -0,0 +1,40 @@ +From: Nicolas Cornu <ncornu@aldebaran.com> +Date: Wed, 29 Jul 2015 14:52:57 +0200 +X-Dgit-Generated: 5.1.0-1.2 37bdc8d26bc0fd1af13142dd34dc11ae2ac2564a +Subject: Fix use of size + +Applied-upstream: 5.1.1, commit:d649854ddb66779950954aac99d960379c631a71 +Bug: https://github.com/proot-me/PRoot/pull/108 +Bug-Debian: https://bugs.debian.org/847292 + +--- + +--- proot-5.1.0.orig/src/execve/enter.c ++++ proot-5.1.0/src/execve/enter.c +@@ -454,10 +454,10 @@ static int expand_runner(Tracee* tracee, + } + + extern unsigned char _binary_loader_exe_start; +-extern unsigned char _binary_loader_exe_size; ++extern unsigned char _binary_loader_exe_end; + + extern unsigned char WEAK _binary_loader_m32_exe_start; +-extern unsigned char WEAK _binary_loader_m32_exe_size; ++extern unsigned char WEAK _binary_loader_m32_exe_end; + + /** + * Extract the built-in loader. This function returns NULL if an +@@ -483,11 +483,11 @@ static char *extract_loader(const Tracee + + if (wants_32bit_version) { + start = (void *) &_binary_loader_m32_exe_start; +- size = (size_t) &_binary_loader_m32_exe_size; ++ size = (size_t)(&_binary_loader_m32_exe_end-&_binary_loader_m32_exe_start); + } + else { + start = (void *) &_binary_loader_exe_start; +- size = (size_t) &_binary_loader_exe_size; ++ size = (size_t) (&_binary_loader_exe_end-&_binary_loader_exe_start); + } + + status2 = write(fd, start, size);
diff --git a/5.1.0/debian/patches/series b/5.1.0/debian/patches/series new file mode 100644 index 0000000..3e37338 --- /dev/null +++ b/5.1.0/debian/patches/series
@@ -0,0 +1,6 @@ +Install-proot-into-DESTDIR-usr-bin.patch +Fix-man-syntax.diff +arm64.patch +fix-use-of-size +disable-seccomp-based-tracing-performanc +0001-Properly-filter-the-renameat2-syscall-introduced-in-.patch
diff --git a/5.1.0/debian/proot.manpages b/5.1.0/debian/proot.manpages new file mode 100644 index 0000000..5d35424 --- /dev/null +++ b/5.1.0/debian/proot.manpages
@@ -0,0 +1 @@ +doc/proot/proot.1
diff --git a/5.1.0/debian/rules b/5.1.0/debian/rules new file mode 100755 index 0000000..9fc0160 --- /dev/null +++ b/5.1.0/debian/rules
@@ -0,0 +1,14 @@ +#!/usr/bin/make -f +# -*- makefile -*- +%: + dh $@ --sourcedirectory=src + +# Force make to print the commands +# By default V=0 +override_dh_auto_build: + dh_auto_build -- V=1 + +override_dh_installman: + mv doc/proot/man.1 doc/proot/proot.1 + dh_installman + mv doc/proot/proot.1 doc/proot/man.1
diff --git a/5.1.0/debian/source/format b/5.1.0/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/5.1.0/debian/source/format
@@ -0,0 +1 @@ +3.0 (quilt)
diff --git a/5.1.0/debian/watch b/5.1.0/debian/watch new file mode 100644 index 0000000..10b7474 --- /dev/null +++ b/5.1.0/debian/watch
@@ -0,0 +1,2 @@ +version=3 +opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/PRoot-$1.tar.gz/ https://github.com/cedric-vincent/PRoot/releases .*/v?(\d\S*)\.tar\.gz
diff --git a/5.1.0/doc/GNUmakefile b/5.1.0/doc/GNUmakefile new file mode 100644 index 0000000..b290017 --- /dev/null +++ b/5.1.0/doc/GNUmakefile
@@ -0,0 +1,29 @@ +OUTPUTS = proot/man.1 proot.h proot/rpm-spec proot/index.html care.h care/index.html +all: $(OUTPUTS) + +%/man.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' $^ > $@ + +%.h: %/stylesheets/cli.xsl %/manual-quoted.xml + xsltproc --output $@ $^ + +%/rpm-spec: %/stylesheets/rpm-spec.xsl %/manual.xml # %/changelog.txt + xsltproc --output $@ $^ + echo "* $(shell date +'%a %b %d %Y') Cédric VINCENT <cedric.vincent@st.com>" >> $@ + cat $*/changelog.txt >> $@ + +%/index.html: stylesheets/website.xsl %/stylesheets/website.xsl %/manual.xml + xsltproc --output $@ $*/stylesheets/website.xsl $*/manual.xml + +clean: + rm -f *.xml $(OUTPUTS) *-quoted.*
diff --git a/5.1.0/doc/articles/extending_qemu-fig1.svg b/5.1.0/doc/articles/extending_qemu-fig1.svg new file mode 100644 index 0000000..33cd2d1 --- /dev/null +++ b/5.1.0/doc/articles/extending_qemu-fig1.svg
@@ -0,0 +1,1001 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="655.53192" + height="158.86346" + id="svg3593" + version="1.1" + inkscape:version="0.48.1 r9760" + sodipodi:docname="article-proot+qemu-fig2.svg"> + <defs + id="defs3595"> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker3615" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3617" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker3619" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3621" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker3623" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3625" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker3627" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3629" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker3631" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3633" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker3635" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3637" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker3639" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3641" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker3643" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3645" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9-6" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3-1" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2-5" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4-5" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3-6" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2-56" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4-9" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9-74" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3-5" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4-54" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3-4" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2-3" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4-0" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3-68" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4-4" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker4078" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4080" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker4082" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4084" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker4086" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4088" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker4090" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4092" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3-61" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2-59" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4-49" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker4212" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4214" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker4216" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4218" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker4220" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4222" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker4224" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4226" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lstart-9-81" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4780-3-2" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2-9" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4-3" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker4324" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4326" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker4328" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4330" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lstart" + orient="auto" + refY="0" + refX="0" + id="marker4332" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4334" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(1.1,0,0,1.1,1.1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="marker4336" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4338" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="5.6" + inkscape:cx="540.32222" + inkscape:cy="44.525493" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1280" + inkscape:window-height="748" + inkscape:window-x="0" + inkscape:window-y="30" + inkscape:window-maximized="1" + fit-margin-top="10" + fit-margin-left="10" + fit-margin-right="10" + fit-margin-bottom="10" + inkscape:snap-global="true"> + <inkscape:grid + type="xygrid" + id="grid3912" + empspacing="5" + visible="true" + enabled="true" + snapvisiblegridlinesonly="true" /> + </sodipodi:namedview> + <metadata + id="metadata3598"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(42.406933,-519.46875)"> + <rect + style="fill:#cccccc;fill-opacity:1;stroke:none" + id="rect4419" + width="90.000015" + height="20" + x="392.8588" + y="549.73834" /> + <rect + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3878" + width="90.000015" + height="40" + x="392.8588" + y="529.73834" + ry="0" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="397.8588" + y="544.73834" + id="text3880" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3882" + x="397.8588" + y="544.73834" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans">process 1</tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="412.8588" + y="564.73834" + id="text3903" + sodipodi:linespacing="125%"><tspan + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans" + sodipodi:role="line" + id="tspan3905" + x="412.8588" + y="564.73834">QEMU</tspan></text> + <rect + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3878-5" + width="60.000015" + height="20" + x="467.8588" + y="584.73834" /> + <text + sodipodi:linespacing="125%" + id="text4748" + y="599.73834" + x="472.8588" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + xml:space="preserve"><tspan + y="599.73834" + x="472.8588" + id="tspan4750" + sodipodi:role="line">PRoot</tspan></text> + <text + sodipodi:linespacing="125%" + id="text4748-3" + y="614.41302" + x="-477.38126" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + xml:space="preserve" + transform="scale(-1,1)"><tspan + y="614.41302" + x="-477.38126" + id="tspan4750-7" + sodipodi:role="line" /></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="372.15564" + y="503.67035" + id="text5416" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5418" + x="372.15564" + y="503.67035" /></text> + <rect + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3878-5-4" + width="100.00002" + height="20" + x="447.8588" + y="619.73834" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="452.8588" + y="634.73834" + id="text5420" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5422" + x="452.8588" + y="634.73834">host kernel</tspan></text> + <g + transform="translate(322.95256,547.05085)" + id="g4554-8" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="489.8588" + y="549.73834" + id="text6457" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan6459" + x="489.8588" + y="549.73834">...</tspan></text> + <rect + style="fill:#cccccc;fill-opacity:1;stroke:none" + id="rect4419-0" + width="89.999985" + height="20" + x="512.85883" + y="549.73834" /> + <rect + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3878-9" + width="89.999985" + height="40" + x="512.85883" + y="529.73834" + ry="0" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="517.85883" + y="544.73834" + id="text3880-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3882-6" + x="517.85883" + y="544.73834" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans">process <tspan + style="font-style:italic;-inkscape-font-specification:Sans Italic" + id="tspan3359">N</tspan></tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="532.85883" + y="564.73834" + id="text3903-0" + sodipodi:linespacing="125%"><tspan + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans" + sodipodi:role="line" + id="tspan3905-6" + x="532.85883" + y="564.73834">QEMU</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart-9);marker-end:url(#Arrow2Lend-2);display:inline" + d="m 497.85881,604.73834 0,15" + id="path8719-4-3" + inkscape:connector-type="polyline" + inkscape:connector-curvature="3" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart-9);marker-end:url(#Arrow2Lend-2);display:inline" + d="m 457.85881,569.73834 40,15" + id="path8719-4-3-7" + inkscape:connector-type="polyline" + inkscape:connector-curvature="3" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart-9);marker-end:url(#Arrow2Lend-2);display:inline" + d="m 497.85881,584.73834 39.61431,-15.05459" + id="path8719-4-3-7-7" + inkscape:connector-type="polyline" + inkscape:connector-curvature="3" + sodipodi:nodetypes="cc" /> + <rect + style="fill:#cccccc;fill-opacity:1;stroke:none" + id="rect4419-09" + width="90.000015" + height="20" + x="242.85881" + y="549.73834" /> + <rect + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3878-1" + width="90.000015" + height="40" + x="242.85881" + y="529.73834" + ry="0" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="247.85881" + y="544.73834" + id="text3880-7" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3882-7" + x="247.85881" + y="544.73834" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans">process 1</tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="262.8588" + y="564.73834" + id="text3903-1" + sodipodi:linespacing="125%"><tspan + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans" + sodipodi:role="line" + id="tspan3905-1" + x="262.8588" + y="564.73834">QEMU</tspan></text> + <rect + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3878-5-4-67" + width="100.00002" + height="20" + x="237.8588" + y="619.73834" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="242.8588" + y="634.73834" + id="text5420-3" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5422-6" + x="242.8588" + y="634.73834">host kernel</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart-9);marker-end:url(#Arrow2Lend-2);display:inline" + d="m 287.59307,570.00409 0,50" + id="path8719-4-3-3" + inkscape:connector-type="polyline" + inkscape:connector-curvature="3" + sodipodi:nodetypes="cc" /> + <rect + style="fill:#cccccc;fill-opacity:1;stroke:none" + id="rect4419-9" + width="220" + height="75" + x="-32.406933" + y="530.00409" /> + <rect + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3878-5-4-8" + width="100.00002" + height="20" + x="27.858812" + y="619.73834" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="32.85881" + y="634.73834" + id="text5420-5" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5422-61" + x="32.85881" + y="634.73834">host kernel</tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="69.858795" + y="549.73834" + id="text6457-1" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan6459-5" + x="69.858795" + y="549.73834">...</tspan></text> + <rect + style="fill:#cccccc;fill-opacity:1;stroke:none" + id="rect4419-0-9" + width="89.999985" + height="20" + x="92.858826" + y="549.73834" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="127.59306" + y="600.00409" + id="text3903-0-0" + sodipodi:linespacing="125%"><tspan + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans" + sodipodi:role="line" + id="tspan3905-6-3" + x="127.59306" + y="600.00409">QEMU</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart-9);marker-end:url(#Arrow2Lend-2);display:inline" + d="m 77.858825,604.73834 0,15" + id="path8719-4-3-0" + inkscape:connector-type="polyline" + inkscape:connector-curvature="3" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart-9);marker-end:url(#Arrow2Lend-2);display:inline" + d="m 37.593066,555.00409 25,25" + id="path8719-4-3-7-4" + inkscape:connector-type="polyline" + inkscape:connector-curvature="3" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.53149605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart-9);marker-end:url(#Arrow2Lend-2);display:inline" + d="m 67.593066,580.00409 50.000004,-25" + id="path8719-4-3-7-7-4" + inkscape:connector-type="polyline" + inkscape:connector-curvature="3" + sodipodi:nodetypes="cc" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.53100002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.59300005, 1.59300005;stroke-dashoffset:0" + id="rect5446" + width="90" + height="20" + x="-27.406933" + y="535.00409" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="-22.406933" + y="550.00409" + id="text3880-8" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3882-8" + x="-22.406933" + y="550.00409" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans">process 1</tspan></text> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.53100002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.59300005, 1.59300005;stroke-dashoffset:0" + id="rect5446-7" + width="115" + height="20" + x="-22.406933" + y="580.00409" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="-17.406933" + y="595.00409" + id="text3880-8-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3882-8-4" + x="-17.406933" + y="595.00409" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans">guest kernel</tspan></text> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.53100002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.59300005, 1.59300005;stroke-dashoffset:0" + id="rect5446-6" + width="90" + height="20" + x="92.593063" + y="535.00409" /> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="97.593063" + y="550.00409" + id="text3880-3-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3882-6-8" + x="97.593063" + y="550.00409" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans">process <tspan + style="font-style:italic;-inkscape-font-specification:Sans Italic" + id="tspan3359-1">N</tspan></tspan></text> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="82.678574" + y="127.67435" + id="text5476" + sodipodi:linespacing="125%" + transform="translate(-37.406934,519.47259)"><tspan + sodipodi:role="line" + id="tspan5478" + x="82.678574" + y="127.67435" /></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="12.593066" + y="665.00409" + id="text5480" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5482" + x="12.593066" + y="665.00409" + style="font-style:italic;-inkscape-font-specification:Sans Italic">(a) system-mode</tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="232.59306" + y="665.00409" + id="text5484" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5486" + x="232.59306" + y="665.00409" + style="font-style:italic;-inkscape-font-specification:Sans Italic">(b) user-mode</tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="407.59308" + y="665.00409" + id="text5488" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5490" + x="407.59308" + y="665.00409">(<tspan + style="font-style:italic;-inkscape-font-specification:Sans Italic" + id="tspan5492">c) user-mode + PRoot</tspan></tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer" + transform="translate(5.0000028,0.00384)" /> +</svg>
diff --git a/5.1.0/doc/articles/extending_qemu-fig2.svg b/5.1.0/doc/articles/extending_qemu-fig2.svg new file mode 100644 index 0000000..d98e5eb --- /dev/null +++ b/5.1.0/doc/articles/extending_qemu-fig2.svg
@@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="635.27875" + height="109.59689" + id="svg3024" + version="1.1" + inkscape:version="0.48.1 r9760" + sodipodi:docname="article-proot+qemu-fig1.svg"> + <defs + id="defs3026"> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0" + refX="0" + id="Arrow2Lend-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path4783-4" + style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.4" + inkscape:cx="277.09991" + inkscape:cy="-24.886332" + inkscape:document-units="px" + showgrid="true" + inkscape:window-width="1280" + inkscape:window-height="748" + inkscape:window-x="0" + inkscape:window-y="30" + inkscape:window-maximized="1" + inkscape:current-layer="svg3024" + fit-margin-top="10" + fit-margin-left="10" + fit-margin-right="10" + fit-margin-bottom="10" + inkscape:snap-global="true"> + <inkscape:grid + type="xygrid" + id="grid3196" + empspacing="5" + visible="true" + enabled="true" + snapvisiblegridlinesonly="true" /> + </sodipodi:namedview> + <metadata + id="metadata3029"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="9.1015625" + y="22.15625" + id="text3903" + sodipodi:linespacing="125%"><tspan + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans" + sodipodi:role="line" + id="tspan3905" + x="9.1015625" + y="22.15625">QEMU:</tspan></text> + <text + sodipodi:linespacing="125%" + id="text4748" + y="52.15625" + x="9.1015625" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + xml:space="preserve"><tspan + y="52.15625" + x="9.1015625" + id="tspan4750" + sodipodi:role="line">PRoot:</tspan></text> + <text + sodipodi:linespacing="125%" + id="text4748-3" + y="52.5625" + x="-151.7334" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + xml:space="preserve" + transform="scale(-1,1)"><tspan + y="52.5625" + x="-151.7334" + id="tspan4750-7" + sodipodi:role="line" /></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="46.507782" + y="-58.180161" + id="text5416" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5418" + x="46.507782" + y="-58.180161" /></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans" + x="9.1015625" + y="82.15625" + id="text5420" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5422" + x="9.1015625" + y="82.15625">host kernel:</tspan></text> + <g + transform="translate(-2.695314,-14.799687)" + id="g4554-8" /> + <path + style="fill:none;stroke:#000000;stroke-width:2.02413559;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-2)" + d="m 69.101562,28.58482 89.999998,0 L 170,99.596893 l 9.10156,-41.012072 90,0 10,40.000002 170,0 10,-40.000003 10,0 10,40.000003 10.83358,-60 132.65567,-0.487288" + id="path3181" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccccccc" /> + <text + sodipodi:linespacing="125%" + id="text4748-6" + y="52.15625" + x="179.10156" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Italic" + xml:space="preserve"><tspan + y="52.15625" + x="179.10156" + id="tspan4750-1" + sodipodi:role="line" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans Italic">translation</tspan></text> + <text + sodipodi:linespacing="125%" + id="text4748-6-9" + y="92.15625" + x="299.10156" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Italic" + xml:space="preserve"><tspan + y="92.15625" + x="299.10156" + id="tspan4750-1-2" + sodipodi:role="line" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans Italic">kernel operation</tspan><tspan + y="112.15625" + x="299.10156" + sodipodi:role="line" + id="tspan3324" /></text> + <text + sodipodi:linespacing="125%" + id="text4748-6-9-0" + y="22.15625" + x="69.101562" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Italic" + xml:space="preserve"><tspan + y="22.15625" + x="69.101562" + id="tspan4750-1-2-2" + sodipodi:role="line" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans Italic">system call</tspan><tspan + y="42.15625" + x="69.101562" + sodipodi:role="line" + id="tspan3324-3" /></text> + <text + sodipodi:linespacing="125%" + id="text4748-6-9-0-9" + y="32.15625" + x="489.10156" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Italic" + xml:space="preserve"><tspan + y="32.15625" + x="489.10156" + id="tspan4750-1-2-2-2" + sodipodi:role="line" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans Italic">result & errno</tspan><tspan + y="52.15625" + x="489.10156" + sodipodi:role="line" + id="tspan3324-3-2" /></text> + <text + sodipodi:linespacing="125%" + id="text4748-6-9-0-9-9" + y="59.596893" + x="610" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Italic" + xml:space="preserve"><tspan + y="59.596893" + x="610" + id="tspan4750-1-2-2-2-2" + sodipodi:role="line" + style="font-size:16px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans Italic">t</tspan><tspan + y="79.596893" + x="610" + sodipodi:role="line" + id="tspan3324-3-2-0" /></text> +</svg>
diff --git a/5.1.0/doc/articles/extending_qemu.txt b/5.1.0/doc/articles/extending_qemu.txt new file mode 100644 index 0000000..e36f12d --- /dev/null +++ b/5.1.0/doc/articles/extending_qemu.txt
@@ -0,0 +1,220 @@ +.. 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
diff --git a/5.1.0/doc/articles/howto_debian_rootfs.txt b/5.1.0/doc/articles/howto_debian_rootfs.txt new file mode 100644 index 0000000..06bfdd7 --- /dev/null +++ b/5.1.0/doc/articles/howto_debian_rootfs.txt
@@ -0,0 +1,209 @@ +.. 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.
diff --git a/5.1.0/doc/articles/howto_migrate_from_scratchbox2.txt b/5.1.0/doc/articles/howto_migrate_from_scratchbox2.txt new file mode 100644 index 0000000..80cbb93 --- /dev/null +++ b/5.1.0/doc/articles/howto_migrate_from_scratchbox2.txt
@@ -0,0 +1,115 @@ +.. include:: stylesheets/article-html.txt + +======================================== +How to migrate from Scratchbox2 to PRoot +======================================== + +:Author: Cédric Vincent +:PRoot Version: 3.1 + +:Abstract: + + PRoot is designed with simplicity and consistency in mind, that's + why most of the Scratchbox2's concepts can be simulated in PRoot + with bindings and a wrapper only. Obviously the comparison + between Scratchbox2 and PRoot is biased as I'm the maintainer of + PRoot. + + +Target +====== + +In Scratchbox2, "target" is a name for a configuration that specifies +the path to the guest rootfs and the path to the cross-compiler. + +PRoot doesn't consider a cross-compiler like a special tool, thus the +user is free to execute it just like any other host programs, either +through the /host-rootfs binding (default when using QEMU): + + proot -q qemu-arm -r rootfs [...] + $ /host-rootfs/opt/cross-tools/arm-linux-gcc [...] + +or through a user-specified binding: + + proot -b /opt/cross-tools/arm-linux-gcc:/usr/bin/gcc -q qemu-arm -r rootfs [...] + $ /usr/bin/gcc + +Also, PRoot has no configuration support on purpose, this lets the +user free to forge the command-line with any programming languages +(shell, Perl, Python, ...). To me, this is more powerful than any +configuration syntax. + + +Execution Policy +================ + +In Scratchbox2, the view of the virtual file-system is automatically +altered according to the executed program. Here is a quote from +"Scratchbox2: Internals and Architecture": + + For example, /usr/lib/perl must be mapped to the target root when + target's perl is running, but to the corresponding place in the + tools directory [ed: where host tools are] when the other perl is + running. + +In PRoot, all processes see exactly the same virtual file-system, +there's *no exception* at all. For example, to use the host Perl the +user can to adjust PERL5LIB explicitly or bind the host instance over +the guest one: + + proot -b /usr/bin/perl $bind_perl_modules [...] + +where $bind_perl_modules is the output of the following command: + + perl -e 'print map { "-b $_ " } grep { m#^/# } @INC' + + +Modes +===== + +Scratchbox2 comes with 3 predefined mapping rule sets ("emulate", +"simple" and "accel") that define how programs are executed (emulated +or natively) and what are the default bindings. + +PRoot has 2 predefined binding sets: "none" ("-r" option) or +"recommended" ("-R" option). Regarding how a program is executed +(emulated or natively), PRoot detects automatically if the binary is +made for the host architecture or not, wherever it lies on the virtual +file-system. + + +Sessions +======== + +In Scratchbox2, a "session" is a "target" instance where the rules +from a given "mode" (a.k.a mapping rule sets) are applied. Also, the +/tmp directory is "private" to each session. + +About the "target" and "mode" support in PRoot, please refer to the +sections above. About "private" directories, the user can simulate +this feature by binding dedicated directories or files over "private" +ones:: + + proot -b ~/tmp-session-01:/tmp [...] + + +Network Namespace +================= + +Scratchbox2 can translate network addresses. This feature doesn't +exist in PRoot but can be implemented easily, as an extension for +instance. + + +Permission Namespace +==================== + +Scratchbox2 (v2.4) has a permission engine that allows one to fake +privileged operations, this engine is even more powerful than +fakeroot's one. + +PRoot has a permission engine ("-0" option) that I want to keep as +small as possible, its only purpose is to cheat package managers that +does some [useless] sanity checks. Note that fakeroot can also be +used under PRoot. +
diff --git a/5.1.0/doc/articles/stylesheets/article-html.txt b/5.1.0/doc/articles/stylesheets/article-html.txt new file mode 100644 index 0000000..efef946 --- /dev/null +++ b/5.1.0/doc/articles/stylesheets/article-html.txt
@@ -0,0 +1,11 @@ +.. raw:: html + + <style> + body { + max-width: 45em; + min-width: 25em; + margin-left: auto; + margin-right: auto; + text-align: justify; + } + </style>
diff --git a/5.1.0/doc/care/changelog.txt b/5.1.0/doc/care/changelog.txt new file mode 100644 index 0000000..1226bba --- /dev/null +++ b/5.1.0/doc/care/changelog.txt
@@ -0,0 +1,104 @@ +CARE v2.2 +========= + ++ Special characters in environment variables are now correctly quoted + in the generated "re-execute.sh" script. For instance, the value of + "COMP_WORDBREAKS" environment variable on Ubuntu used to make CARE + generate broken "re-execute.sh" scripts. + ++ It is now possible to deliver CARE as a dynamically linked binary. + In this case, the self-extracting format is not supported since one + can't assume the re-execution environment contains all the + pre-requisites (libtalloc, libarchive, proot). Thus, CARE and PRoot + should be deployed independently on the re-execution environment. + ++ Syscall are now emulated only if it is really needed. + +This release is based on PRoot v4.0.3, so it contains all the +following generic fixes too: + + https://github.com/cedric-vincent/PRoot/releases/tag/v4.0.0 + https://github.com/cedric-vincent/PRoot/releases/tag/v4.0.1 + https://github.com/cedric-vincent/PRoot/releases/tag/v4.0.2 + https://github.com/cedric-vincent/PRoot/releases/tag/v4.0.3 + + +CARE v2.1 +========= + +This release contains all the fixes from PRoot v3.2.2 [1] and the +following new features: + ++ CARE now supports three new archive formats: + + - a self-extracting format, this has become the default behavior. + Here's an example:: + + somewhere$ care echo OK + [...] + OK + [...] + care info: - run `./care-140115135934.bin` to extract the output archive. + + elsewhere$ ./care-140115135934.bin + info: archive found: offset = 195816, size = 3035122 + info: extracted: care-140115135934/rootfs/usr + info: extracted: care-140115135934/rootfs/usr/local + [...] + info: extracted: care-140115135934/re-execute.sh + info: extracted: care-140115135934/README.txt + info: extracted: care-140115135934/proot + + - the GNU tar format, this is the most commonly used archive + format. It can be combined with all the compression formats + supported by CARE: ".tar.gz" or ".tar.lzo". + + - a "copy" format where the content is copied directly into a + directory instead of being archived in a file. For instance:: + + $ care -o foo/ echo OK + [...] + $ ls foo + README.txt proot re-execute.sh rootfs/ + ++ It is recommended to use the new "-x/--extract" option to extract + archives created by CARE, since most extracting tools -- that are + not based on libarchive -- are too limited to extract them + correctly. + ++ CARE now returns the exit code from the re-executed command. For + instance, with the previous version: + + $ care-v2.0 -o test.cpio sh -c 'exit 1' + [...] + $ echo $? + 1 + [...] + $ test/re-execute.sh + $ echo $? + 0 + $ test/re-execute.sh sh -c 'exit 2' + $ echo $? + 0 + + And with this new version: + + $ care-v2.1 -o test.cpio sh -c 'exit 1' + [...] + $ echo $? + 1 + [...] + $ test/re-execute.sh + $ echo $? + 1 + $ test/re-execute.sh sh -c 'exit 2' + $ echo $? + 2 + ++ Applications that rely on DBUS and/or on KDE cache are now + supported. + +Thanks to Antoine Moynault, Christophe Guillon, and Rémi Duraffort for +their help. + +[1] https://github.com/cedric-vincent/PRoot/blob/v3.2.2/doc/changelog.txt
diff --git a/5.1.0/doc/care/manual.txt b/5.1.0/doc/care/manual.txt new file mode 100644 index 0000000..25e4bed --- /dev/null +++ b/5.1.0/doc/care/manual.txt
@@ -0,0 +1,462 @@ +====== + CARE +====== + +------------------------------------------------- +Comprehensive Archiver for Reproducible Execution +------------------------------------------------- + +:Date: 2014-09-15 +:Version: 2.2 +:Manual section: 1 + + +Synopsis +======== + +**care** [*option*] ... *command* + + +Description +=========== + +CARE monitors the execution of the specified command to create an +*archive* that contains all the material required to *re-execute* it +in the same context. That way, the command will be reproducible +everywhere, even on Linux systems that are supposed to be not +compatible with the original Linux system. CARE is typically useful +to get reliable bug reports, demonstrations, `artifact evaluation`_, +tutorials, portable applications, minimal rootfs, file-system +coverage, ... + +By design, CARE does not record events at all. Instead, it archives +environment variables and accessed file-system components -- before +modification -- during the so-called *initial* execution. Then, to +reproduce this execution, the ``re-execute.sh`` script embedded into +the archive restores the environment variables and relaunches the +command confined into the saved file-system. That way, both *initial* +and *reproduced* executions should produce the same results as they +use the same context, assuming they do not rely on external events -- +like key strokes or network packets -- or that these external events +are replayed manually or automatically, using umockdev_ for instance. +That means it is possible to alter explicitly the reproduced +executions by changing content of the saved file-system, or by +replaying different external events. + +.. _umockdev: https://github.com/martinpitt/umockdev/ + +.. _artifact evaluation: http://www.artifact-eval.org/ + +Privacy +------- + +To ensure that no sensitive file can possibly leak into the archive, +CARE *conceals* recursively the content of ``$HOME`` and ``/tmp``, +that is, they appear empty during the original execution. Although, +for consistency reasons, the content of ``$PWD`` is *revealed* even if +it is nested into the two previous paths. + +As a consequence, a program executed under CARE may behave +unexpectedly because a required path is not accessible anymore. In +this case, such a path has to be revealed explicitly. For details, +see the options ``--concealed-path`` and ``--revealed-path``, and the +file ``concealed-accesses.txt`` as well. + +It is advised to inspect the archived content before sharing it. + + +Options +======= + +The command-line interface is composed of two parts: first CARE's +options, then the command to launch. This section describes the +options supported by CARE, that is, the first part of its command-line +interface. + +-o path, --output=path + Archive in *path*, its suffix specifies the format. + + The suffix of *path* is used to select the archive format, it can + be one of the following: + + ========= ======================================================== + suffix comment + ========= ======================================================== + / don't archive, copy into the specified directory instead + .tar most common archive format + .cpio most portable archive format, it can archive sockets too + ?.gz most common compression format, but slow + ?.lzo fast compression format, but uncommon + ?.bin see ``Self-extracting format`` section + ?.?.bin see ``Self-extracting format`` section + .bin see ``Self-extracting format`` section + .raw recommended archive format, use `care -x` to extract + ========= ======================================================== + + where "?" means the suffix must be combined with another one. For + examples: ".tar.lzo", ".cpio.gz", ".tar.bin", ".cpio.lzo.bin", ... + If this option is not specified, the default output path is + ``care-<DATE>.bin`` or ``care-<DATE>.raw``, depending on whether + CARE was built with self-extracting format support or not. + +-c path, --concealed-path=path + Make *path* content appear empty during the original execution. + + Some paths may contain sensitive data that should never be + archived. This is typically the case for most of the files in: + + * $HOME + * /tmp + + That's why these directories are recursively *concealed* from the + original execution, unless the ``-d`` option is specified. + Concealed paths appear empty during the original execution, as a + consequence their original content can't be accessed nor archived. + +-r path, --revealed-path=path + Make *path* content accessible when nested in a concealed path. + + Concealed paths might make the original execution with CARE behave + differently from an execution without CARE. For example, a lot of + ``No such file or directory`` errors might appear. The solution + is to *reveal* recursively any required paths that would be nested + into a *concealed* path. Note that ``$PWD`` is *revealed*, unless + the ``-d`` option is specified. + +-p path, --volatile-path=path + Don't archive *path* content, reuse actual *path* instead. + + Some paths contain only communication means with programs that + can't be monitored by CARE, like the kernel or a remote server. + Such paths are said *volatile*; they shouldn't be archived, + instead they must be accessed from the *actual* rootfs during the + re-execution. This is typically the case for the following pseudo + file-systems, sockets, and authority files: + + * /dev + * /proc + * /sys + * /run/shm + * /tmp/.X11-unix + * /tmp/.ICE-unix + * $XAUTHORITY + * $ICEAUTHORITY + * /var/run/dbus/system_bus_socket + * /var/tmp/kdecache-$LOGNAME + + This is also typically the case for any other fifos or sockets. + These paths are considered *volatile*, unless the ``-d`` option is + specified. + +-e name, --volatile-env=name + Don't archive *name* env. variable, reuse actual value instead. + + Some environment variables are used to communicate with programs + that can't be monitored by CARE, like remote servers. Such + environment variables are said *volatile*; they shouldn't be + archived, instead they must be accessed from the *actual* + environment during the re-execution. This is typically the case + for the following ones: + + * DISPLAY + * http_proxy + * https_proxy + * ftp_proxy + * all_proxy + * HTTP_PROXY + * HTTPS_PROXY + * FTP_PROXY + * ALL_PROXY + * DBUS_SESSION_BUS_ADDRESS + * SESSION_MANAGER + * XDG_SESSION_COOKIE + + These environment variables are considered *volatile*, unless the + ``-d`` option is specified. + +-m value, --max-archivable-size=value + Set the maximum size of archivable files to *value* megabytes. + + To keep the CPU time and the disk space used by the archiver + reasonable, files whose size exceeds *value* megabytes are + truncated down to 0 bytes. The default is 1GB, unless the ``-d`` + option is specified. A negative *value* means no limit. + +-d, --ignore-default-config + Don't use the default options. + +-x file, --extract=file + Extract content of the archive *file*, then exit. + + It is recommended to use this option to extract archives created + by CARE because most extracting tools -- that are not based on + libarchive -- are too limited to extract them correctly. + +-v value, --verbose=value + Set the level of debug information to *value*. + + The higher the integer *value* is, the more detailed debug + information is printed to the standard error stream. A negative + *value* makes CARE quiet except on fatal errors. + +-V, --version, --about + Print version, copyright, license and contact, then exit. + +-h, --help, --usage + Print the user manual, then exit. + + +Exit Status +=========== + +If an internal error occurs, ``care`` 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 ``care`` itself is to have a look at +the error message. + + +Files +===== + +The output archive contains the following files: + +``re-execute.sh`` + start the re-execution of the initial command as originally + specified. It is also possible to specify an alternate command. + For example, assuming ``gcc`` was archived, it can be re-invoked + differently: + + $ ./re-execute.sh gcc --version + gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2 + + $ echo 'int main(void) { return puts(\"OK\"); }' > rootfs/foo.c + $ ./re-execute.sh gcc -Wall /foo.c + $ foo.c: In function "main": + $ foo.c:1:1: warning: implicit declaration of function "puts" + +``rootfs/`` + directory where all the files used during the original execution + were archived, they will be required for the reproduced execution. + +``proot`` + virtualization tool invoked by re-execute.sh to confine the + reproduced execution into the rootfs. It also emulates the + missing kernel features if needed. + +``concealed-accesses.txt`` + list of accessed paths that were concealed during the original + execution. Its main purpose is to know what are the paths that + should be revealed if the the original execution didn't go as + expected. It is absolutely useless for the reproduced execution. + + +Limitations +=========== + +It's not possible to use GDB, strace, or any programs based on +*ptrace* under CARE yet. This latter is also based on this syscall, +but the Linux kernel allows only one *ptracer* per process. This will +be fixed in a future version of CARE thanks to a ptrace emulator. + + +Example +======= + +In this example, Alice wants to report to Bob that the compilation of +PRoot v2.4 raises an unexpected warning:: + + alice$ make -C PRoot-2.4/src/ + + make: Entering directory `PRoot-2.4/src' + [...] + CC path/proc.o + ./path/proc.c: In function 'readlink_proc': + ./path/proc.c:132:3: warning: ignoring return value of 'strtol' + [...] + +Technically, Alice uses Ubuntu 11.04 for x86, whereas Bob uses +Slackware 13.37 on x86_64. Both distros are supposed to be shipped +with GCC 4.5.2, however Bob is not able to reproduce this issue on his +system:: + + bob$ make -C PRoot-2.4/src/ + + make: Entering directory `PRoot-2.4/src' + [...] + CC path/proc.o + [...] + +Since they don't have much time to investigate this issue by iterating +between each other, they decide to use CARE. First, Alice prepends +``care`` to her command:: + + alice$ care make -C PRoot-2.4/src/ + + care info: concealed path: $HOME + care info: concealed path: /tmp + care info: revealed path: $PWD + care info: ---------------------------------------------------------------------- + make: Entering directory `PRoot-2.4/src' + [...] + CC path/proc.o + ./path/proc.c: In function 'readlink_proc': + ./path/proc.c:132:3: warning: ignoring return value of 'strtol' + [...] + care info: ---------------------------------------------------------------------- + care info: Hints: + care info: - search for "conceal" in `care -h` if the execution didn't go as expected. + care info: - use `./care-130213072430.bin` to extract the output archive. + +Then she sends the ``care-130213072430.bin`` file to Bob. Now, he +should be able to reproduce her issue on his system:: + + bob$ ./care-130213072430.bin + [...] + bob$ ./care-130213072430/re-execute.sh + + make: Entering directory `PRoot-2.4/src' + [...] + CC path/proc.o + ./path/proc.c: In function 'readlink_proc': + ./path/proc.c:132:3: warning: ignoring return value of 'strtol' + [...] + +So far so good! This compiler warning doesn't make sense to Bob since +``strtol`` is used there to check a string format; the return value is +useless, only the ``errno`` value matters. Further investigations are +required, so Bob re-execute Alice's GCC differently to get more +details:: + + bob$ ./care-130213072430/re-execute.sh gcc --version + + gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2 + Copyright (C) 2010 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +The same invocation on his system returns something slightly +different:: + + bob$ gcc --version + + gcc (GCC) 4.5.2 + Copyright (C) 2010 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +This confirms that both GCC versions are the same, however Alice's one +seems to have been modified by Ubuntu. Although, according to the web +page related to this Ubuntu package [#]_, no changes regarding +``strtol`` were made. So Bob decides to search into the files coming +from Alice's system, that is, the ``rootfs`` directory in the +archive:: + + bob$ grep -wIrl strtol ./care-130213072430/rootfs + + care-130213072430/rootfs/usr/include/inttypes.h + care-130213072430/rootfs/usr/include/stdlib.h + [...] + +Here, the file ``usr/include/stdlib.h`` contains a declaration of +``strtol`` with the "warn unused result" attribute. On Ubuntu, this +file belongs to the EGLIBC package, and its related web page [#]_ +shows that this attribute was actually wrongly introduced by the +official EGLIBC developers. Ultimately Bob should notify them in this +regard. + +Thanks to CARE, Bob was able to reproduce the issue reported by Alice +without effort. For investigations purpose, he was able to re-execute +programs differently and to search into the relevant files. + +.. [#] https://launchpad.net/ubuntu/oneiric/+source/gcc-4.5/4.5.2-8ubuntu4 +.. [#] https://launchpad.net/ubuntu/+source/eglibc/2.13-0ubuntu13.2 + + +Self-extracting format +====================== + +The self-extracting format used by CARE starts with an extracting +program, followed by a regular archive, and it ends with a special +footer. This latter contains the signature "I_LOVE_PIZZA" followed by +the size of the embedded archive:: + + +------------------------+ + | extracting program | + +------------------------+ + | | + | embedded archive | + | | + +------------------------+ + | uint8_t signature[13] | + | uint64_t archive_size | # big-endian + +------------------------+ + +The command ``care -x`` can be used against a self-extracting archive, +even if they were not build for the same architecture. For instance, +a self-extracting archive produced for ARM can be extracted with a +``care`` program built for x86_64, and vice versa. It is also +possible to use external tools to extract the embedded archive, for +example:: + + $ care -o foo.tar.gz.bin /usr/bin/echo OK + [...] + OK + [...] + + $ hexdump -C foo.tar.gz.bin | tail -3 + 0015b5b0 00 b0 2e 00 49 5f 4c 4f 56 45 5f 50 49 5a 5a 41 |....I_LOVE_PIZZA| + 0015b5c0 00 00 00 00 00 00 12 b4 13 |.........| + 0015b5c9 + + $ file_size=`stat -c %s foo.tar.gz.bin` + $ archive_size=$((16#12b413)) + $ footer_size=21 + $ skip=$(($file_size - $archive_size - $footer_size)) + + $ dd if=foo.tar.gz.bin of=foo.tar.gz bs=1 skip=$skip count=$archive_size + 1225747+0 records in + 1225747+0 records out + 1225747 bytes (1.2 MB) copied, 2.99546 s, 409 kB/s + + $ file foo.tar.gz + foo.tar.gz: gzip compressed data, from Unix + + $ tar -tzf foo.tar.gz + foo/rootfs/usr/ + [...] + foo/re-execute.sh + foo/README.txt + foo/proot + + +Downloads +========= + +CARE is heavily based on PRoot_, that's why they are both hosted in +the same repository: http://github.proot.me. Since CARE is supposed +to work on any Linux systems, it is recommended to use following +highly compatible static binaries: + +* for x86_64: http://static.reproducible.io/care-x86_64 + +* for x86: http://static.reproducible.io/care-x86 + +* for ARM: http://static.reproducible.io/care-arm + +* other architectures: on demand. + +.. _PRoot: http://proot.me + +Colophon +======== + +Visit http://reproducible.io for help, bug reports, suggestions, patches, ... +Copyright (C) 2014 STMicroelectronics, licensed under GPL v2 or later. + +:: + + _____ ____ _____ ____ + / __/ __ | __ \ __| + / /_/ | / __| + \_____|__|__|__|__\____|
diff --git a/5.1.0/doc/care/roadmap.txt b/5.1.0/doc/care/roadmap.txt new file mode 100644 index 0000000..b639835 --- /dev/null +++ b/5.1.0/doc/care/roadmap.txt
@@ -0,0 +1,42 @@ +========= + Roadmap +========= + +CARE v2.2 +========= + +* Based on PRoot v3.3. + +* Use PRoot "fake id" option ("-i"). + +* Make "re-execute.sh" print a useful message when -h/--help is + specified. + +* Add an option to archive specified path, no matter whether it is + used or not. + +* In concealed-accesses.txt, print the name of the program that tried + to access a concealed path too. + +* Fake the full utsname structure. + + +CARE v2.3 +========= + +* Add an option to augment an existing archive with content used from + a new monitored execution. + + +Not yet scheduled +================= + +Fixes +----- + +* Accept *care as valid CLI name + +* Bug: an archive for "wine notepad.exe" created on Ubuntu 12.04 + x86_64 can't be re-executed on RHEL5 x86_64. + +* Warn when an unemulated feature is used (ppoll, getcpu, flags, ...)
diff --git a/5.1.0/doc/care/stylesheets/cli.xsl b/5.1.0/doc/care/stylesheets/cli.xsl new file mode 100644 index 0000000..a31d2d6 --- /dev/null +++ b/5.1.0/doc/care/stylesheets/cli.xsl
@@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output method="text" /> + + <xsl:template match="/"> + <xsl:text>/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ + +#ifndef CARE_CLI_H +#define CARE_CLI_H + +#include "cli/cli.h" + +#ifndef VERSION +#define VERSION "</xsl:text><xsl:value-of select="//version" /><xsl:text>" +#endif + +#define CARE_MAX_SIZE 1024 + +</xsl:text> + +<xsl:apply-templates select="//option_string[.='-c']" /> +<xsl:apply-templates select="//option_string[.='-r']" /> +<xsl:apply-templates select="//option_string[.='-p']" /> +<xsl:apply-templates select="//option_string[.='-e']" /> + + <xsl:apply-templates select="//option_group" mode="handlers" /> + <xsl:text> +static int pre_initialize_bindings(Tracee *, const Cli *, size_t, char *const *, size_t); +static int post_initialize_bindings(Tracee *, const Cli *, size_t, char *const *, size_t); +</xsl:text> + <xsl:text> +static Cli care_cli = { + .version = VERSION, + .name = "care", +</xsl:text> + <xsl:apply-templates select="//subtitle"/> + <xsl:apply-templates select="//section[@names='synopsis']" /> + <xsl:apply-templates select="//section[@names='colophon']" /> + <xsl:apply-templates select="//section[@names='logo']" /> + <xsl:text> + .pre_initialize_bindings = pre_initialize_bindings, + .post_initialize_bindings = post_initialize_bindings, + + .options = { +</xsl:text> + <xsl:apply-templates select="//option_group" mode="options" /> + <xsl:text> END_OF_OPTIONS, + }, +}; + +#endif /* CARE_CLI_H */ +</xsl:text> + </xsl:template> + + <!-- Constant string definitions --> + + <xsl:template match="subtitle"> + <xsl:text> .subtitle = "</xsl:text> + <xsl:value-of select="." /> + <xsl:text>", +</xsl:text> + </xsl:template> + + <xsl:template match="section[@names='synopsis']"> + <xsl:text> .synopsis = "</xsl:text> + <xsl:value-of select="./paragraph" /> + <xsl:text>", +</xsl:text> + </xsl:template> + + <xsl:template match="section[@names='colophon']"> + <xsl:text> .colophon = "</xsl:text> + <xsl:value-of select="./paragraph" /> + <xsl:text>", +</xsl:text> + <xsl:text> .logo = "\ +</xsl:text> + <xsl:value-of select="./literal_block" /> + <xsl:text>", +</xsl:text> + </xsl:template> + + <!-- Recommanded options declarations --> + + <xsl:template match="option_string[.='-c']"> + <xsl:text>static char const *default_concealed_paths[] = { +</xsl:text> + <xsl:apply-templates select="ancestor-or-self::option_list_item//list_item" /> + <xsl:text> NULL, +}; + +</xsl:text> + + </xsl:template> + + <xsl:template match="option_string[.='-r']"> + <xsl:text>static char const *default_revealed_paths[] = { +</xsl:text> + <xsl:apply-templates select="ancestor-or-self::option_list_item//list_item" /> + <xsl:text> NULL, +}; + +</xsl:text> + + </xsl:template> + + <xsl:template match="option_string[.='-p']"> + <xsl:text>static char const *default_volatile_paths[] = { +</xsl:text> + <xsl:apply-templates select="ancestor-or-self::option_list_item//list_item" /> + <xsl:text> NULL, +}; + +</xsl:text> + + </xsl:template> + + <xsl:template match="option_string[.='-e']"> + <xsl:text>static char const *default_volatile_envars[] = { +</xsl:text> + <xsl:apply-templates select="ancestor-or-self::option_list_item//list_item" /> + <xsl:text> NULL, +}; + +</xsl:text> + </xsl:template> + + <!-- Option declarations --> + + <xsl:template match="option_group" mode="options"> + <xsl:text> { .class = "</xsl:text> + <xsl:value-of select="ancestor-or-self::section[1]/title" /> + <xsl:text>", +</xsl:text> + <xsl:text> .arguments = { +</xsl:text> + <xsl:apply-templates select="option" mode="options" /> + <xsl:text> { .name = NULL, .separator = '\0', .value = NULL } }, +</xsl:text> + <xsl:text> .handler = handle_option_</xsl:text> + <xsl:value-of select="substring(option[1]/option_string, 2, 1)" /> + <xsl:text>, +</xsl:text> + <xsl:text> .description = "</xsl:text> + <xsl:apply-templates select="../description/paragraph[1]" mode="options" /> + <xsl:text>", +</xsl:text> + <xsl:text> .detail = NULL, + }, +</xsl:text> + </xsl:template> + + <xsl:template match="emphasis" mode="options"> + <xsl:text>*</xsl:text> + <xsl:value-of select="." /> + <xsl:text>*</xsl:text> + </xsl:template> + + <xsl:template match="paragraph"> + <xsl:apply-templates/> + <xsl:text> + +</xsl:text> + </xsl:template> + + <!-- Option aliases declarations --> + + <xsl:template match="option" mode="options"> + <xsl:text> { </xsl:text> + <xsl:text>.name = "</xsl:text> + <xsl:value-of select="option_string" /> + <xsl:text>", .separator = '</xsl:text> + <xsl:choose> + <xsl:when test="option_argument"> + <xsl:value-of select="option_argument/@delimiter" /> + <xsl:text>', .value = "</xsl:text> + <xsl:value-of select="option_argument" /> + <xsl:text>"</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>\0', .value = NULL</xsl:text> + </xsl:otherwise> + </xsl:choose> + <xsl:text> }, +</xsl:text> + </xsl:template> + + <!-- Handler declarations --> + + <xsl:template match="option_group" mode="handlers"> + <xsl:text>static int handle_option_</xsl:text> + <xsl:value-of select="substring(option[1]/option_string, 2, 1)" /> + <xsl:text>(Tracee *tracee, const Cli *cli, const char *value); +</xsl:text> + </xsl:template> + +</xsl:transform>
diff --git a/5.1.0/doc/care/stylesheets/website.css b/5.1.0/doc/care/stylesheets/website.css new file mode 100644 index 0000000..cfe9317 --- /dev/null +++ b/5.1.0/doc/care/stylesheets/website.css
@@ -0,0 +1,13 @@ +@import url("website.css"); + +h1 { + color: SkyBlue; +} + +#contents a:hover { + border-bottom: 2px solid SkyBlue; +} + +a { + border-bottom: 1px solid SkyBlue; +}
diff --git a/5.1.0/doc/care/stylesheets/website.xsl b/5.1.0/doc/care/stylesheets/website.xsl new file mode 100644 index 0000000..c601514 --- /dev/null +++ b/5.1.0/doc/care/stylesheets/website.xsl
@@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:import href = "../../stylesheets/website.xsl" /> + + <xsl:template match="/"> + <html xmlns="http://www.w3.org/1999/xhtml" itemscope="" itemtype="http://schema.org/Product" xml:lang="en" lang="en"> + + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title> + <xsl:value-of select="document/title" /> — <xsl:value-of select="document/subtitle" /> + </title> + <link rel="stylesheet" href="care-website.css" type="text/css" /> + <meta itemprop="name" content="CARE" /> + <meta itemprop="description"> + <xsl:attribute name="content"> + <xsl:value-of select="//section[@names='description']/paragraph[1]" /> + </xsl:attribute> + </meta> + <script type="text/javascript"> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-46144158-1', 'reproducible.io'); + ga('send', 'pageview'); + </script> + </head> + + <body> + + <div id="title"> + <h1>CARE</h1> + <xsl:text> </xsl:text> + <div class="g-plusone" data-size="small"> + <xsl:comment>By default XSLTproc converts tags with no + content to self-closing tags</xsl:comment> + </div> + </div> + + <div id="contents"> + <ul> + <li><a href="#description">Description</a></li> + <li><a href="#example">Example</a></li> + <li><a href="#downloads">Downloads</a></li> + <li><a href="#support">Support</a></li> + <li><a href="https://plus.google.com/107771643881342199474/posts">News+</a></li> + </ul> + </div> + + <xsl:apply-templates select="//section[@names='description']" /> + <xsl:apply-templates select="//section[@names='example']" /> + <xsl:apply-templates select="//section[@names='downloads']" /> + + <div class="section" id="support"> + <h2>Support</h2> + <p>Feel free to send your questions, bug reports, + suggestions, and patchs to <a + href="mailto:reproducible@googlegroups.com">the + mailing-list</a> or to <a + href="https://groups.google.com/forum/?fromgroups#!forum/reproducible">the + forum</a>, but please be sure that your answer isn't in + the <a + href="https://raw.github.com/cedric-vincent/PRoot/next/doc/care/manual.txt">user + manual</a> first. + </p> + </div> + + <script type="text/javascript"> + (function() { + var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; + po.src = 'https://apis.google.com/js/plusone.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); + })(); + </script> + + </body> + </html> + + </xsl:template> +</xsl:transform>
diff --git a/5.1.0/doc/ecosystem/care-ecosystem.graphml b/5.1.0/doc/ecosystem/care-ecosystem.graphml new file mode 100644 index 0000000..1c9058f --- /dev/null +++ b/5.1.0/doc/ecosystem/care-ecosystem.graphml
@@ -0,0 +1,435 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd"> + <!--Created by yEd 3.13--> + <key for="graphml" id="d0" yfiles.type="resources"/> + <key for="port" id="d1" yfiles.type="portgraphics"/> + <key for="port" id="d2" yfiles.type="portgeometry"/> + <key for="port" id="d3" yfiles.type="portuserdata"/> + <key attr.name="url" attr.type="string" for="node" id="d4"/> + <key attr.name="description" attr.type="string" for="node" id="d5"/> + <key for="node" id="d6" yfiles.type="nodegraphics"/> + <key attr.name="Description" attr.type="string" for="graph" id="d7"/> + <key attr.name="url" attr.type="string" for="edge" id="d8"/> + <key attr.name="description" attr.type="string" for="edge" id="d9"/> + <key for="edge" id="d10" yfiles.type="edgegraphics"/> + <graph edgedefault="directed" id="G"> + <data key="d7"/> + <node id="n0"> + <data key="d4"><![CDATA[http://reproducible.io]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="49.53125" x="-24.765625" y="-15.0"/> + <y:Fill color="#FF9900" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="39.53125" x="5.0" y="6.015625">CARE<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n1"> + <data key="d5"><![CDATA[Gogo uses CARE to reproduce -- bit accurately -- the build of an embedded Linux distribution.]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="48.927734375" x="176.33882507911434" y="-301.66754050376784"/> + <y:Fill color="#99FFFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="38.927734375" x="5.0" y="6.015625">Gogo<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n2"> + <data key="d4"/> + <data key="d5"><![CDATA[Copyright holder and unique sponsor of CARE. +]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="142.47265625" x="261.29259233244335" y="-124.19943708374738"/> + <y:Fill color="#99FFFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="132.47265625" x="5.0" y="6.015625">STMicroelectronics<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n3"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="82.4375" x="84.6175322138738" y="-96.64086034940046"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="72.4375" x="5.0" y="6.015625">is used by<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n4"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="116.380859375" x="-163.15515147836032" y="-122.15599460304256"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="106.380859375" x="5.0" y="6.015625">is presented at<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n5"> + <data key="d4"/> + <data key="d5"><![CDATA[Linaro LAVA uses CARE to boot some ST boards and to archives failures during validation]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="96.248046875" x="-158.01053765500095" y="317.3025037418872"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="86.248046875" x="5.0" y="6.015625">Linaro LAVA<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n6"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="99.97460937500001" x="52.4701522039035" y="94.55578271979161"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="89.974609375" x="5.000000000000007" y="6.015625">is shipped in<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n7"> + <data key="d4"><![CDATA[https://aur.archlinux.org/packages/care/]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="85.625" x="271.1124520413913" y="139.7615084115239"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="75.625" x="5.0" y="6.015625">Arch Linux<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n8"> + <data key="d4"><![CDATA[http://packages.gentoo.org/package/sys-apps/proot]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="62.755859375" x="92.19422537680252" y="312.45980286557915"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="52.755859375" x="5.0" y="6.015625">Gentoo<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n9"> + <data key="d4"><![CDATA[http://www.slideshare.net/linaroorg/lcu14-211-lava-use-cases-sw-testing-reproducing-a-lava-failures-locally-using-care]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="153.44140625" x="-68.06034313332424" y="-364.8928381159217"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="143.44140625" x="5.0" y="6.015625">Linaro Connect 2014<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n10"> + <data key="d4"><![CDATA[http://ctuning.org/cm/wiki/index.php?title=Events:TRUST2014]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="95.57421875" x="-231.06930207249655" y="-313.17383828900506"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="85.57421875" x="5.0" y="6.015625">TRUST 2014<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n11"> + <data key="d4"><![CDATA[http://www.openmole.org/documentation/tutorials/embed-r-scilab/]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="81.91015625" x="-323.3281381408953" y="191.79810196725595"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="71.91015625" x="5.0" y="6.015625">OpenMole<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n12"> + <data key="d4"><![CDATA[https://archive.fosdem.org/2014/schedule/event/syscall/]]></data> + <data key="d5"><![CDATA[Lightning talk]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="104.392578125" x="-357.324279106177" y="-186.4552702366005"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="94.392578125" x="5.0" y="6.015625">Fosdem 2012<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n13"> + <data key="d4"/> + <data key="d5"><![CDATA[About 100 domains references http://reproducible.io according to Google]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="127.6953125" x="-413.5669895377619" y="-0.9861523926047937"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="117.6953125" x="5.0" y="6.015625">many web pages<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n14"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="78.681640625" x="-126.413006754006" y="107.14104284922254"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="68.681640625" x="5.0" y="6.015625">is used in<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <edge id="e0" source="n0" target="n6"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e1" source="n0" target="n3"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e2" source="n0" target="n4"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e3" source="n4" target="n9"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e4" source="n4" target="n10"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e5" source="n4" target="n12"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e6" source="n4" target="n13"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e7" source="n6" target="n8"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e8" source="n6" target="n7"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e9" source="n3" target="n2"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e10" source="n3" target="n1"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e11" source="n14" target="n11"> + <data key="d9"/> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e12" source="n0" target="n14"> + <data key="d9"/> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e13" source="n14" target="n5"> + <data key="d9"/> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + </graph> + <data key="d0"> + <y:Resources/> + </data> +</graphml>
diff --git a/5.1.0/doc/ecosystem/care-ecosystem.svg b/5.1.0/doc/ecosystem/care-ecosystem.svg new file mode 100644 index 0000000..f3fb034 --- /dev/null +++ b/5.1.0/doc/ecosystem/care-ecosystem.svg
@@ -0,0 +1,410 @@ +<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill-opacity="1" color-rendering="auto" color-interpolation="auto" stroke="black" text-rendering="auto" stroke-linecap="square" width="848" stroke-miterlimit="10" stroke-opacity="1" shape-rendering="auto" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="743" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12" stroke-dashoffset="0" image-rendering="auto"> + <!--Generated by ySVG 2.5--> + <defs id="genericDefs"/> + <g> + <defs id="defs1"> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"> + <path d="M0 0 L848 0 L848 743 L0 743 L0 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"> + <path d="M-429 -380 L419 -380 L419 363 L-429 363 L-429 -380 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"> + <path d="M0 0 L0 17 L590 17 L590 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"> + <path d="M0 0 L0 17 L297 17 L297 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath5"> + <path d="M0 0 L0 17 L564 17 L564 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6"> + <path d="M0 0 L0 17 L92 17 L92 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath7"> + <path d="M0 0 L0 17 L469 17 L469 0 Z"/> + </clipPath> + <script type="text/ecmascript"><![CDATA[ var SVGDocument = null; + var SVGRoot = null; + var TrueCoords = null; + var lastElement = null; + var initialized = null; + var tipGroup; + function Init(evt) + { + SVGDocument = evt.target.ownerDocument; + SVGRoot = SVGDocument.documentElement; + TrueCoords = SVGRoot.createSVGPoint(); + initialized = evt; + }; + function GetTrueCoords(evt) + { + var newScale = SVGRoot.currentScale; + var translation = SVGRoot.currentTranslate; + TrueCoords.x = (evt.clientX - translation.x)/newScale; + TrueCoords.y = (evt.clientY - translation.y)/newScale; + }; + function HideTooltip( evt ) + { + if(initialized == null) { + Init(evt); + } + if(tipGroup != null) { + tipGroup.setAttributeNS(null, 'visibility', 'hidden'); + } + }; + function ShowTooltip( evt ) + { + if(initialized == null) { + Init(evt); + } + GetTrueCoords( evt ); + var tipScale = 1/SVGRoot.currentScale; + var targetElement = evt.currentTarget; + if ( lastElement != targetElement ) + { + var targetId = targetElement.getAttributeNS(null, 'id'); + var tipId = 'tooltip.' + targetId; + tipGroup = SVGDocument.getElementById(tipId); + var xPos = TrueCoords.x + (10 * tipScale); + var yPos = TrueCoords.y + (10 * tipScale); + tipGroup.setAttributeNS(null, 'transform', 'translate(' + xPos + ',' + yPos + ') scale(' + tipScale + ')'); + tipGroup.setAttributeNS(null, 'visibility', 'visible'); + } + }; +]]></script> + </defs> + <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="translate(429,380)" stroke="white"> + <rect x="-429" width="848" height="743" y="-380" clip-path="url(#clipPath2)" stroke="none"/> + </g> + <g id="y.node.0"> + <a xlink:href="http://reproducible.io"> + <g fill="rgb(255,153,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(255,153,0)"> + <ellipse rx="24.7656" ry="15" cx="0" cy="0" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-17.7656" xml:space="preserve" y="4.1543" stroke="none">CARE</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="24.7656" fill="none" ry="15" cx="0" cy="0"/> + </g> + </a> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.1" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(153,255,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(153,255,255)"> + <ellipse rx="24.4639" ry="15" cx="200.8027" cy="-286.6675" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="183.3388" xml:space="preserve" y="-282.5132" stroke="none">Gogo</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="24.4639" fill="none" ry="15" cx="200.8027" cy="-286.6675"/> + </g> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.2" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(153,255,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(153,255,255)"> + <ellipse rx="71.2363" ry="15" cx="332.5289" cy="-109.1994" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="268.2926" xml:space="preserve" y="-105.0451" stroke="none">STMicroelectronics</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="71.2363" fill="none" ry="15" cx="332.5289" cy="-109.1994"/> + </g> + </g> + <g id="y.node.3"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="91.6175" xml:space="preserve" y="-77.4866" stroke="none">is used by</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <rect fill="none" x="84.6175" width="82.4375" height="30" y="-96.6409"/> + </g> + </g> + <g id="y.node.4"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-156.1552" xml:space="preserve" y="-103.0017" stroke="none">is presented at</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <rect fill="none" x="-163.1552" width="116.3809" height="30" y="-122.156"/> + </g> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.5" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(204,255,204)"> + <ellipse rx="48.124" ry="15" cx="-109.8865" cy="332.3025" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-151.0105" xml:space="preserve" y="336.4568" stroke="none">Linaro LAVA</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="48.124" fill="none" ry="15" cx="-109.8865" cy="332.3025"/> + </g> + </g> + <g id="y.node.6"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="59.4702" xml:space="preserve" y="113.7101" stroke="none">is shipped in</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <rect fill="none" x="52.4702" width="99.9746" height="30" y="94.5558"/> + </g> + </g> + <g id="y.node.7"> + <a xlink:href="https://aur.archlinux.org/packages/care/"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(204,204,255)"> + <ellipse rx="42.8125" ry="15" cx="313.925" cy="154.7615" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="278.1125" xml:space="preserve" y="158.9158" stroke="none">Arch Linux</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="42.8125" fill="none" ry="15" cx="313.925" cy="154.7615"/> + </g> + </a> + </g> + <g id="y.node.8"> + <a xlink:href="http://packages.gentoo.org/package/sys-apps/proot"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(204,204,255)"> + <ellipse rx="31.3779" ry="15" cx="123.5722" cy="327.4598" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="99.1942" xml:space="preserve" y="331.6141" stroke="none">Gentoo</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="31.3779" fill="none" ry="15" cx="123.5722" cy="327.4598"/> + </g> + </a> + </g> + <g id="y.node.9"> + <a xlink:href="http://www.slideshare.net/linaroorg/lcu14-211-lava-use-cases-sw-testing-reproducing-a-lava-failures-locally-using-care"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(255,255,153)"> + <ellipse rx="76.7207" ry="15" cx="8.6604" cy="-349.8928" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-61.0603" xml:space="preserve" y="-345.7386" stroke="none">Linaro Connect 2014</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="76.7207" fill="none" ry="15" cx="8.6604" cy="-349.8928"/> + </g> + </a> + </g> + <g id="y.node.10"> + <a xlink:href="http://ctuning.org/cm/wiki/index.php?title=Events:TRUST2014"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(255,255,153)"> + <ellipse rx="47.7871" ry="15" cx="-183.2822" cy="-298.1738" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-224.0693" xml:space="preserve" y="-294.0195" stroke="none">TRUST 2014</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="47.7871" fill="none" ry="15" cx="-183.2822" cy="-298.1738"/> + </g> + </a> + </g> + <g id="y.node.11"> + <a xlink:href="http://www.openmole.org/documentation/tutorials/embed-r-scilab/"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(204,255,204)"> + <ellipse rx="40.9551" ry="15" cx="-282.3731" cy="206.7981" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-316.3281" xml:space="preserve" y="210.9524" stroke="none">OpenMole</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="40.9551" fill="none" ry="15" cx="-282.3731" cy="206.7981"/> + </g> + </a> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.12" onmousemove="ShowTooltip(evt)"> + <a xlink:href="https://archive.fosdem.org/2014/schedule/event/syscall/"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(255,255,153)"> + <ellipse rx="52.1963" ry="15" cx="-305.128" cy="-171.4553" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-350.3243" xml:space="preserve" y="-167.301" stroke="none">Fosdem 2012</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="52.1963" fill="none" ry="15" cx="-305.128" cy="-171.4553"/> + </g> + </a> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.13" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke="rgb(255,255,153)"> + <ellipse rx="63.8477" ry="15" cx="-349.7193" cy="14.0138" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-406.567" xml:space="preserve" y="18.1681" stroke="none">many web pages</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <ellipse rx="63.8477" fill="none" ry="15" cx="-349.7193" cy="14.0138"/> + </g> + </g> + <g id="y.node.14"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,429,380)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-119.413" xml:space="preserve" y="126.2953" stroke="none">is used in</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <rect fill="none" x="-126.413" width="78.6816" height="30" y="107.141"/> + </g> + </g> + <g id="y.edge.1"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M16.9002 -10.9646 L96.0049 -62.2867"/> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path d="M102.7162 -66.6409 L89.9279 -64.3041 L95.166 -61.7424 L95.3707 -55.915 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.9"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M167.055 -87.1366 L264.4137 -100.1176"/> + <path d="M272.3435 -101.1749 L259.788 -104.5451 L263.4225 -99.9854 L261.1096 -94.6328 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.10"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M131.3209 -96.6409 L192.7037 -264.5174"/> + <path d="M195.4509 -272.0309 L186.6341 -262.4776 L192.3602 -263.5782 L196.026 -259.0436 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.2"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-12.6366 -12.9004 L-84.6733 -86.441"/> + <path d="M-90.2715 -92.156 L-85.4462 -80.0847 L-83.9736 -85.7266 L-78.3024 -87.0823 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.0"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M12.206 13.0516 L82.9649 88.7128"/> + <path d="M88.4293 94.5558 L83.8846 82.3761 L82.2819 87.9824 L76.5809 89.2066 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.8"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M152.4448 120.2416 L269.5548 145.2764"/> + <path d="M277.378 146.9488 L266.6884 139.5507 L268.5769 145.0674 L264.5979 149.3298 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.7"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M103.9109 124.5558 L121.3486 304.5132"/> + <path d="M122.1202 312.4759 L125.9396 300.0496 L121.2522 303.5178 L115.9862 301.014 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.3"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-97.9432 -122.156 L-1.7235 -327.7098"/> + <path d="M1.6681 -334.9553 L-7.9478 -326.2068 L-2.1475 -326.8041 L1.1091 -321.9673 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.4"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-111.1147 -122.156 L-174.1477 -275.8945"/> + <path d="M-177.1825 -283.2965 L-177.2565 -270.2968 L-173.7683 -274.9693 L-168.004 -274.0903 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.5"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-151.6596 -122.156 L-262.71 -157.8292"/> + <path d="M-270.3267 -160.2759 L-260.4309 -151.8454 L-261.7579 -157.5233 L-257.3725 -161.3662 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.6"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-135.2637 -92.156 L-315.1767 -3.0871"/> + <path d="M-322.3462 0.4623 L-309.3736 -0.3808 L-314.2805 -3.5307 L-313.8103 -9.3427 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.12"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-9.8172 13.7711 L-71.7351 100.6269"/> + <path d="M-76.379 107.141 L-65.3418 100.2722 L-71.1546 99.8126 L-73.4845 94.4674 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.11"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-121.6767 137.141 L-248.6006 192.1587"/> + <path d="M-255.9406 195.3404 L-242.9419 195.1554 L-247.683 191.761 L-246.9191 185.9803 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.13"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,429,380)" stroke-linecap="butt"> + <path fill="none" d="M-88.7005 137.141 L-107.3957 309.3578"/> + <path d="M-108.2591 317.3111 L-101.9932 305.9208 L-107.2878 308.3636 L-111.9348 304.8416 Z" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.1" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="590" height="17" y="0" clip-path="url(#clipPath3)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath3)" stroke="none">Gogo uses CARE to reproduce -- bit accurately -- the build of an embedded Linux distribution.</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L590 0 L590 17 L0 17 L0 0 ZM1 1 L589 1 L589 16 L1 16 L1 1 Z" clip-path="url(#clipPath3)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.2" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="297" height="17" y="0" clip-path="url(#clipPath4)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath4)" stroke="none">Copyright holder and unique sponsor of CARE.</text> + <text fill="black" x="293" xml:space="preserve" y="13" clip-path="url(#clipPath4)" stroke="none"> </text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L297 0 L297 17 L0 17 L0 0 ZM1 1 L296 1 L296 16 L1 16 L1 1 Z" clip-path="url(#clipPath4)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.5" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="564" height="17" y="0" clip-path="url(#clipPath5)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath5)" stroke="none">Linaro LAVA uses CARE to boot some ST boards and to archives failures during validation</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L564 0 L564 17 L0 17 L0 0 ZM1 1 L563 1 L563 16 L1 16 L1 1 Z" clip-path="url(#clipPath5)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.12" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="92" height="17" y="0" clip-path="url(#clipPath6)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath6)" stroke="none">Lightning talk</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L92 0 L92 17 L0 17 L0 0 ZM1 1 L91 1 L91 16 L1 16 L1 1 Z" clip-path="url(#clipPath6)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.13" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="469" height="17" y="0" clip-path="url(#clipPath7)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath7)" stroke="none">About 100 domains references http://reproducible.io according to Google</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L469 0 L469 17 L0 17 L0 0 ZM1 1 L468 1 L468 16 L1 16 L1 1 Z" clip-path="url(#clipPath7)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + </g> +</svg>
diff --git a/5.1.0/doc/ecosystem/proot-ecosystem.graphml b/5.1.0/doc/ecosystem/proot-ecosystem.graphml new file mode 100644 index 0000000..1d4d6ae --- /dev/null +++ b/5.1.0/doc/ecosystem/proot-ecosystem.graphml
@@ -0,0 +1,881 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd"> + <!--Created by yEd 3.13--> + <key for="graphml" id="d0" yfiles.type="resources"/> + <key for="port" id="d1" yfiles.type="portgraphics"/> + <key for="port" id="d2" yfiles.type="portgeometry"/> + <key for="port" id="d3" yfiles.type="portuserdata"/> + <key attr.name="url" attr.type="string" for="node" id="d4"/> + <key attr.name="description" attr.type="string" for="node" id="d5"/> + <key for="node" id="d6" yfiles.type="nodegraphics"/> + <key attr.name="Description" attr.type="string" for="graph" id="d7"/> + <key attr.name="url" attr.type="string" for="edge" id="d8"/> + <key attr.name="description" attr.type="string" for="edge" id="d9"/> + <key for="edge" id="d10" yfiles.type="edgegraphics"/> + <graph edgedefault="directed" id="G"> + <data key="d7"/> + <node id="n0"> + <data key="d4"><![CDATA[http://proot.me]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="54.259765625" x="-27.1298828125" y="-15.0"/> + <y:Fill color="#FF9900" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="44.259765625" x="5.0" y="6.015625">PRoot<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n1"> + <data key="d4"><![CDATA[https://github.com/ev3dev/brickstrap]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="85.255859375" x="8.689222029961527" y="406.8904477937454"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="75.255859375" x="5.0" y="6.015625">BrickStrap<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n2"> + <data key="d4"><![CDATA[https://packages.debian.org/sid/proot]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="61.443359375" x="354.68058384338565" y="-194.13708511994318"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="51.443359375" x="5.0" y="6.015625">Debian<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n3"> + <data key="d4"><![CDATA[https://github.com/squeaky-pl/portable-pypy]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="108.83984375" x="107.00967476271475" y="378.1481722320285"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="98.83984375" x="5.0" y="6.015625">Portable Pypy<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n4"> + <data key="d4"><![CDATA[https://play.google.com/store/apps/details?id=champion.gnuroot]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="75.1015625" x="-450.90609758795875" y="83.8047693947313"/> + <y:Fill color="#FFCCCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="65.1015625" x="5.0" y="6.015625">GNURoot<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n5"> + <data key="d4"><![CDATA[https://play.google.com/store/apps/details?id=com.cuntubuntu]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="110.55078125" x="-480.1559338693215" y="-25.075910500312432"/> + <y:Fill color="#FFCCCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="100.55078125" x="5.0" y="6.015625">Debian noroot<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n6"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="82.408203125" x="-213.7405317385705" y="14.26055813032196"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="72.408203125" x="5.0" y="6.015625">is core for<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n7"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="78.681640625" x="103.59273131298826" y="85.97029176804374"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="68.681640625" x="5.0" y="6.015625">is used in<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n8"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="99.974609375" x="54.241210084416" y="-155.5753061829868"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="89.974609375" x="5.0" y="6.015625">is shipped in<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n9"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="116.380859375" x="-167.6934869707976" y="-151.50670476431128"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="106.380859375" x="5.0" y="6.015625">is presented at<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n10"> + <data key="d4"/> + <data key="d5"><![CDATA[Copyright holder and unique sponsor of PRoot. +]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="142.47265625" x="-350.5031410359237" y="305.36705075050264"/> + <y:Fill color="#99FFFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="132.47265625" x="5.0" y="6.015625">STMicroelectronics<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n11"> + <data key="d4"><![CDATA[https://archive.fosdem.org/2014/schedule/event/syscall/]]></data> + <data key="d5"><![CDATA[Lightning talk]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="104.392578125" x="-93.55920604191515" y="-437.9823980958948"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="94.392578125" x="5.0" y="6.015625">Fosdem 2012<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n12"> + <data key="d4"><![CDATA[http://reproducible.io]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="49.53125" x="-411.72348342029915" y="160.7515741231802"/> + <y:Fill color="#FFCCCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="39.53125" x="5.0" y="6.015625">CARE<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n13"> + <data key="d4"><![CDATA[http://packages.gentoo.org/package/sys-apps/proot]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="62.755859375" x="243.83124041283997" y="-338.8594026621462"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="52.755859375" x="5.0" y="6.015625">Gentoo<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n14"> + <data key="d4"><![CDATA[https://aur.archlinux.org/packages/proot/]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="85.625" x="295.50543185367104" y="-272.2274810090376"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="75.625" x="5.0" y="6.015625">Arch Linux<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n15"> + <data key="d4"><![CDATA[https://launchpad.net/ubuntu/+source/proot]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="63.69921875" x="28.591460019473068" y="-435.6802552182031"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="53.69921875" x="5.0" y="6.015625">Ubuntu<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n16"> + <data key="d4"><![CDATA[http://gfxmonk.net/dist/0install/proot.xml]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="64.208984375" x="108.30506081770423" y="-416.13608342416507"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="54.208984375" x="5.0" y="6.015625">0install<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n17"> + <data key="d4"><![CDATA[https://github.com/NixOS/nixpkgs/tree/master/pkgs/tools/system/proot]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="54.740234375" x="184.28565441143212" y="-383.54692285903116"/> + <y:Fill color="#CCCCFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="44.740234375" x="5.0" y="6.015625">NixOS<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n18"> + <data key="d4"><![CDATA[https://nixos.org/wiki/How_to_install_nix_in_home_%28on_another_distribution%29]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="54.740234375" x="384.14171534680975" y="-121.22152175644175"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="44.740234375" x="5.0" y="6.015625">NixOS<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n19"> + <data key="d4"><![CDATA[https://aur.archlinux.org/packages/xampp/]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="91.970703125" x="216.49045883118833" y="319.2625449525815"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="81.970703125" x="5.0" y="6.015625">xampp AUR<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n20"> + <data key="d4"><![CDATA[https://aur.archlinux.org/packages/hammerwatch/]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="142.73046875" x="281.7140094424758" y="221.55876137923843"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="132.73046875" x="5.0" y="6.015625">hammerwatch AUR<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n21"> + <data key="d5"><![CDATA[Enea uses PRoot in an internal tool (host environment for API testing of target software) at a customer.]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="46.9765625" x="-209.1070661324986" y="367.32272584656107"/> + <y:Fill color="#99FFFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="36.9765625" x="5.0" y="6.015625">Enea<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n22"> + <data key="d4"><![CDATA[https://forge.ocamlcore.org/projects/opam2debian]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="110.017578125" x="356.2343185721427" y="92.25719753471871"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="100.017578125" x="5.0" y="6.015625">OPAM2Debian<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n23"> + <data key="d4"><![CDATA[http://sioworkers.readthedocs.org/en/latest/]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="98.064453125" x="375.7878196465929" y="-27.36641980947448"/> + <y:Fill color="#CCFFCC" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="88.064453125" x="5.0" y="6.015625">SIO Workers<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n24"> + <data key="d4"><![CDATA[http://adt.cs.upb.de/quf/]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="161.5859375" x="-265.6290019615982" y="-397.7017648595326"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="151.5859375" x="5.0" y="6.015625">QEMU workshop 2011<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n25"> + <data key="d4"><![CDATA[http://www.slideshare.net/linaroorg/lcu14-211-lava-use-cases-sw-testing-reproducing-a-lava-failures-locally-using-care]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="153.44140625" x="-398.40020428209834" y="-292.7540252369358"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="143.44140625" x="5.0" y="6.015625">Linaro Connect 2014<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n26"> + <data key="d4"/> + <data key="d5"><![CDATA[About 175 domains references http://proot.me according to Google]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="127.6953125" x="-464.13839253711944" y="-157.8017032206403"/> + <y:Fill color="#FFFF99" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="117.6953125" x="5.0" y="6.015625">many web pages<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n27"> + <data key="d5"><![CDATA[Sony has an internal wiki page about CE Linux that references http://proot.me]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="47.251953125" x="-377.11120095137943" y="220.95168178833467"/> + <y:Fill color="#99FFFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="37.251953125" x="5.0" y="6.015625">Sony<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n28"> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="82.4375" x="-129.84723893150272" y="135.89728609129622"/> + <y:Fill hasColor="false" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="72.4375" x="5.0" y="6.015625">is used by<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="rectangle"/> + </y:ShapeNode> + </data> + </node> + <node id="n29"> + <data key="d5"><![CDATA[Cisco has an internal wiki page that references http://proot.me]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="49.419921875" x="-146.35284463093" y="392.21985320800195"/> + <y:Fill color="#99FFFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="39.419921875" x="5.0" y="6.015625">Cisco +<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <node id="n30"> + <data key="d5"><![CDATA[Ericsson has an internal Bugzilla ticket that references http://proot.me]]></data> + <data key="d6"> + <y:ShapeNode> + <y:Geometry height="30.0" width="70.4140625" x="-79.37080412941125" y="407.69913788066305"/> + <y:Fill color="#99FFFF" transparent="false"/> + <y:BorderStyle color="#000000" type="line" width="1.0"/> + <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="DejaVu Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="60.4140625" x="5.0" y="6.015625">Ericsson<y:LabelModel> + <y:SmartNodeLabelModel distance="4.0"/> + </y:LabelModel> + <y:ModelParameter> + <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> + </y:ModelParameter> + </y:NodeLabel> + <y:Shape type="ellipse"/> + </y:ShapeNode> + </data> + </node> + <edge id="e0" source="n0" target="n7"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e1" source="n0" target="n8"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e2" source="n0" target="n9"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e3" source="n0" target="n6"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e4" source="n8" target="n2"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e5" source="n8" target="n14"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e6" source="n8" target="n13"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e7" source="n8" target="n17"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e8" source="n8" target="n16"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e9" source="n8" target="n15"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e10" source="n9" target="n11"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e11" source="n9" target="n24"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e12" source="n9" target="n25"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e13" source="n9" target="n26"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e14" source="n6" target="n5"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e15" source="n6" target="n4"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e16" source="n6" target="n12"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e17" source="n7" target="n1"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e18" source="n7" target="n3"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e19" source="n7" target="n19"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e20" source="n7" target="n20"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e21" source="n7" target="n22"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e22" source="n7" target="n23"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e23" source="n7" target="n18"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e24" source="n0" target="n28"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e25" source="n28" target="n27"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e26" source="n28" target="n10"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e27" source="n28" target="n21"> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e28" source="n28" target="n29"> + <data key="d9"/> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + <edge id="e29" source="n28" target="n30"> + <data key="d9"/> + <data key="d10"> + <y:PolyLineEdge> + <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> + <y:LineStyle color="#000000" type="line" width="1.0"/> + <y:Arrows source="none" target="standard"/> + <y:BendStyle smoothed="false"/> + </y:PolyLineEdge> + </data> + </edge> + </graph> + <data key="d0"> + <y:Resources/> + </data> +</graphml>
diff --git a/5.1.0/doc/ecosystem/proot-ecosystem.svg b/5.1.0/doc/ecosystem/proot-ecosystem.svg new file mode 100644 index 0000000..25bf298 --- /dev/null +++ b/5.1.0/doc/ecosystem/proot-ecosystem.svg
@@ -0,0 +1,761 @@ +<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill-opacity="1" color-rendering="auto" color-interpolation="auto" stroke="black" text-rendering="auto" stroke-linecap="square" width="985" stroke-miterlimit="10" stroke-opacity="1" shape-rendering="auto" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="906" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12" stroke-dashoffset="0" image-rendering="auto"> + <!--Generated by ySVG 2.5--> + <defs id="genericDefs"/> + <g> + <defs id="defs1"> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"> + <path d="M0 0 L985 0 L985 906 L0 906 L0 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"> + <path d="M-496 -453 L489 -453 L489 453 L-496 453 L-496 -453 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"> + <path d="M0 0 L0 17 L302 17 L302 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"> + <path d="M0 0 L0 17 L92 17 L92 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath5"> + <path d="M0 0 L0 17 L659 17 L659 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath6"> + <path d="M0 0 L0 17 L432 17 L432 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath7"> + <path d="M0 0 L0 17 L496 17 L496 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath8"> + <path d="M0 0 L0 17 L403 17 L403 0 Z"/> + </clipPath> + <clipPath clipPathUnits="userSpaceOnUse" id="clipPath9"> + <path d="M0 0 L0 17 L450 17 L450 0 Z"/> + </clipPath> + <script type="text/ecmascript"><![CDATA[ var SVGDocument = null; + var SVGRoot = null; + var TrueCoords = null; + var lastElement = null; + var initialized = null; + var tipGroup; + function Init(evt) + { + SVGDocument = evt.target.ownerDocument; + SVGRoot = SVGDocument.documentElement; + TrueCoords = SVGRoot.createSVGPoint(); + initialized = evt; + }; + function GetTrueCoords(evt) + { + var newScale = SVGRoot.currentScale; + var translation = SVGRoot.currentTranslate; + TrueCoords.x = (evt.clientX - translation.x)/newScale; + TrueCoords.y = (evt.clientY - translation.y)/newScale; + }; + function HideTooltip( evt ) + { + if(initialized == null) { + Init(evt); + } + if(tipGroup != null) { + tipGroup.setAttributeNS(null, 'visibility', 'hidden'); + } + }; + function ShowTooltip( evt ) + { + if(initialized == null) { + Init(evt); + } + GetTrueCoords( evt ); + var tipScale = 1/SVGRoot.currentScale; + var targetElement = evt.currentTarget; + if ( lastElement != targetElement ) + { + var targetId = targetElement.getAttributeNS(null, 'id'); + var tipId = 'tooltip.' + targetId; + tipGroup = SVGDocument.getElementById(tipId); + var xPos = TrueCoords.x + (10 * tipScale); + var yPos = TrueCoords.y + (10 * tipScale); + tipGroup.setAttributeNS(null, 'transform', 'translate(' + xPos + ',' + yPos + ') scale(' + tipScale + ')'); + tipGroup.setAttributeNS(null, 'visibility', 'visible'); + } + }; +]]></script> + </defs> + <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="translate(496,453)" stroke="white"> + <rect x="-496" width="985" height="906" y="-453" clip-path="url(#clipPath2)" stroke="none"/> + </g> + <g id="y.node.0"> + <a target="_blank" xlink:type="simple" xlink:href="http://proot.me" xlink:show="new"> + <g fill="rgb(255,153,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,153,0)"> + <ellipse rx="27.1299" ry="15" cx="0" cy="0" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-20.1299" xml:space="preserve" y="4.1543" stroke="none">PRoot</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="27.1299" fill="none" ry="15" cx="0" cy="0"/> + </g> + </a> + </g> + <g id="y.node.1"> + <a target="_blank" xlink:type="simple" xlink:href="https://github.com/ev3dev/brickstrap" xlink:show="new"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,255,204)"> + <ellipse rx="42.6279" ry="15" cx="51.3172" cy="421.8904" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="15.6892" xml:space="preserve" y="426.0447" stroke="none">BrickStrap</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="42.6279" fill="none" ry="15" cx="51.3172" cy="421.8904"/> + </g> + </a> + </g> + <g id="y.node.2"> + <a target="_blank" xlink:type="simple" xlink:href="https://packages.debian.org/sid/proot" xlink:show="new"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,204,255)"> + <ellipse rx="30.7217" ry="15" cx="385.4023" cy="-179.1371" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="361.6806" xml:space="preserve" y="-174.9828" stroke="none">Debian</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="30.7217" fill="none" ry="15" cx="385.4023" cy="-179.1371"/> + </g> + </a> + </g> + <g id="y.node.3"> + <a target="_blank" xlink:type="simple" xlink:href="https://github.com/squeaky-pl/portable-pypy" xlink:show="new"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,255,204)"> + <ellipse rx="54.4199" ry="15" cx="161.4296" cy="393.1482" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="114.0097" xml:space="preserve" y="397.3025" stroke="none">Portable Pypy</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="54.4199" fill="none" ry="15" cx="161.4296" cy="393.1482"/> + </g> + </a> + </g> + <g id="y.node.4"> + <a target="_blank" xlink:type="simple" xlink:href="https://play.google.com/store/apps/details?id=champion.gnuroot" xlink:show="new"> + <g fill="rgb(255,204,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,204,204)"> + <ellipse rx="37.5508" ry="15" cx="-413.3553" cy="98.8048" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-443.9061" xml:space="preserve" y="102.9591" stroke="none">GNURoot</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="37.5508" fill="none" ry="15" cx="-413.3553" cy="98.8048"/> + </g> + </a> + </g> + <g id="y.node.5"> + <a target="_blank" xlink:type="simple" xlink:href="https://play.google.com/store/apps/details?id=com.cuntubuntu" xlink:show="new"> + <g fill="rgb(255,204,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,204,204)"> + <ellipse rx="55.2754" ry="15" cx="-424.8805" cy="-10.0759" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-473.1559" xml:space="preserve" y="-5.9216" stroke="none">Debian noroot</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="55.2754" fill="none" ry="15" cx="-424.8805" cy="-10.0759"/> + </g> + </a> + </g> + <g id="y.node.6"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-206.7405" xml:space="preserve" y="33.4149" stroke="none">is core for</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <rect fill="none" x="-213.7405" width="82.4082" height="30" y="14.2606"/> + </g> + </g> + <g id="y.node.7"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="110.5927" xml:space="preserve" y="105.1246" stroke="none">is used in</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <rect fill="none" x="103.5927" width="78.6816" height="30" y="85.9703"/> + </g> + </g> + <g id="y.node.8"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="61.2412" xml:space="preserve" y="-136.421" stroke="none">is shipped in</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <rect fill="none" x="54.2412" width="99.9746" height="30" y="-155.5753"/> + </g> + </g> + <g id="y.node.9"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-160.6935" xml:space="preserve" y="-132.3524" stroke="none">is presented at</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <rect fill="none" x="-167.6935" width="116.3809" height="30" y="-151.5067"/> + </g> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.10" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(153,255,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(153,255,255)"> + <ellipse rx="71.2363" ry="15" cx="-279.2668" cy="320.3671" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-343.5031" xml:space="preserve" y="324.5214" stroke="none">STMicroelectronics</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="71.2363" fill="none" ry="15" cx="-279.2668" cy="320.3671"/> + </g> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.11" onmousemove="ShowTooltip(evt)"> + <a target="_blank" xlink:type="simple" xlink:href="https://archive.fosdem.org/2014/schedule/event/syscall/" xlink:show="new"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,255,153)"> + <ellipse rx="52.1963" ry="15" cx="-41.3629" cy="-422.9824" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-86.5592" xml:space="preserve" y="-418.8281" stroke="none">Fosdem 2012</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="52.1963" fill="none" ry="15" cx="-41.3629" cy="-422.9824"/> + </g> + </a> + </g> + <g id="y.node.12"> + <a target="_blank" xlink:type="simple" xlink:href="http://reproducible.io" xlink:show="new"> + <g fill="rgb(255,204,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,204,204)"> + <ellipse rx="24.7656" ry="15" cx="-386.9579" cy="175.7516" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-404.7235" xml:space="preserve" y="179.9059" stroke="none">CARE</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="24.7656" fill="none" ry="15" cx="-386.9579" cy="175.7516"/> + </g> + </a> + </g> + <g id="y.node.13"> + <a target="_blank" xlink:type="simple" xlink:href="http://packages.gentoo.org/package/sys-apps/proot" xlink:show="new"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,204,255)"> + <ellipse rx="31.3779" ry="15" cx="275.2092" cy="-323.8594" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="250.8312" xml:space="preserve" y="-319.7051" stroke="none">Gentoo</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="31.3779" fill="none" ry="15" cx="275.2092" cy="-323.8594"/> + </g> + </a> + </g> + <g id="y.node.14"> + <a target="_blank" xlink:type="simple" xlink:href="https://aur.archlinux.org/packages/proot/" xlink:show="new"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,204,255)"> + <ellipse rx="42.8125" ry="15" cx="338.3179" cy="-257.2275" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="302.5054" xml:space="preserve" y="-253.0732" stroke="none">Arch Linux</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="42.8125" fill="none" ry="15" cx="338.3179" cy="-257.2275"/> + </g> + </a> + </g> + <g id="y.node.15"> + <a target="_blank" xlink:type="simple" xlink:href="https://launchpad.net/ubuntu/+source/proot" xlink:show="new"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,204,255)"> + <ellipse rx="31.8496" ry="15" cx="60.4411" cy="-420.6803" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="35.5915" xml:space="preserve" y="-416.526" stroke="none">Ubuntu</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="31.8496" fill="none" ry="15" cx="60.4411" cy="-420.6803"/> + </g> + </a> + </g> + <g id="y.node.16"> + <a target="_blank" xlink:type="simple" xlink:href="http://gfxmonk.net/dist/0install/proot.xml" xlink:show="new"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,204,255)"> + <ellipse rx="32.1045" ry="15" cx="140.4096" cy="-401.1361" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="115.3051" xml:space="preserve" y="-396.9818" stroke="none">0install</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="32.1045" fill="none" ry="15" cx="140.4096" cy="-401.1361"/> + </g> + </a> + </g> + <g id="y.node.17"> + <a target="_blank" xlink:type="simple" xlink:href="https://github.com/NixOS/nixpkgs/tree/master/pkgs/tools/system/proot" xlink:show="new"> + <g fill="rgb(204,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,204,255)"> + <ellipse rx="27.3701" ry="15" cx="211.6558" cy="-368.5469" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="191.2857" xml:space="preserve" y="-364.3926" stroke="none">NixOS</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="27.3701" fill="none" ry="15" cx="211.6558" cy="-368.5469"/> + </g> + </a> + </g> + <g id="y.node.18"> + <a target="_blank" xlink:type="simple" xlink:href="https://nixos.org/wiki/How_to_install_nix_in_home_%28on_another_distribution%29" xlink:show="new"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,255,204)"> + <ellipse rx="27.3701" ry="15" cx="411.5118" cy="-106.2215" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="391.1417" xml:space="preserve" y="-102.0672" stroke="none">NixOS</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="27.3701" fill="none" ry="15" cx="411.5118" cy="-106.2215"/> + </g> + </a> + </g> + <g id="y.node.19"> + <a target="_blank" xlink:type="simple" xlink:href="https://aur.archlinux.org/packages/xampp/" xlink:show="new"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,255,204)"> + <ellipse rx="45.9854" ry="15" cx="262.4758" cy="334.2625" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="223.4905" xml:space="preserve" y="338.4168" stroke="none">xampp AUR</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="45.9854" fill="none" ry="15" cx="262.4758" cy="334.2625"/> + </g> + </a> + </g> + <g id="y.node.20"> + <a target="_blank" xlink:type="simple" xlink:href="https://aur.archlinux.org/packages/hammerwatch/" xlink:show="new"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,255,204)"> + <ellipse rx="71.3652" ry="15" cx="353.0792" cy="236.5588" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="288.714" xml:space="preserve" y="240.7131" stroke="none">hammerwatch AUR</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="71.3652" fill="none" ry="15" cx="353.0792" cy="236.5588"/> + </g> + </a> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.21" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(153,255,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(153,255,255)"> + <ellipse rx="23.4883" ry="15" cx="-185.6188" cy="382.3227" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-202.1071" xml:space="preserve" y="386.477" stroke="none">Enea</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="23.4883" fill="none" ry="15" cx="-185.6188" cy="382.3227"/> + </g> + </g> + <g id="y.node.22"> + <a target="_blank" xlink:type="simple" xlink:href="https://forge.ocamlcore.org/projects/opam2debian" xlink:show="new"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,255,204)"> + <ellipse rx="55.0088" ry="15" cx="411.2431" cy="107.2572" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="363.2343" xml:space="preserve" y="111.4115" stroke="none">OPAM2Debian</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="55.0088" fill="none" ry="15" cx="411.2431" cy="107.2572"/> + </g> + </a> + </g> + <g id="y.node.23"> + <a target="_blank" xlink:type="simple" xlink:href="http://sioworkers.readthedocs.org/en/latest/" xlink:show="new"> + <g fill="rgb(204,255,204)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(204,255,204)"> + <ellipse rx="49.0322" ry="15" cx="424.82" cy="-12.3664" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="382.7878" xml:space="preserve" y="-8.2121" stroke="none">SIO Workers</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="49.0322" fill="none" ry="15" cx="424.82" cy="-12.3664"/> + </g> + </a> + </g> + <g id="y.node.24"> + <a target="_blank" xlink:type="simple" xlink:href="http://adt.cs.upb.de/quf/" xlink:show="new"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,255,153)"> + <ellipse rx="80.793" ry="15" cx="-184.836" cy="-382.7018" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-258.629" xml:space="preserve" y="-378.5475" stroke="none">QEMU workshop 2011</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="80.793" fill="none" ry="15" cx="-184.836" cy="-382.7018"/> + </g> + </a> + </g> + <g id="y.node.25"> + <a target="_blank" xlink:type="simple" xlink:href="http://www.slideshare.net/linaroorg/lcu14-211-lava-use-cases-sw-testing-reproducing-a-lava-failures-locally-using-care" xlink:show="new"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,255,153)"> + <ellipse rx="76.7207" ry="15" cx="-321.6795" cy="-277.754" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-391.4002" xml:space="preserve" y="-273.5997" stroke="none">Linaro Connect 2014</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="76.7207" fill="none" ry="15" cx="-321.6795" cy="-277.754"/> + </g> + </a> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.26" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(255,255,153)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(255,255,153)"> + <ellipse rx="63.8477" ry="15" cx="-400.2907" cy="-142.8017" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-457.1384" xml:space="preserve" y="-138.6474" stroke="none">many web pages</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="63.8477" fill="none" ry="15" cx="-400.2907" cy="-142.8017"/> + </g> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.27" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(153,255,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(153,255,255)"> + <ellipse rx="23.626" ry="15" cx="-353.4852" cy="235.9517" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-370.1112" xml:space="preserve" y="240.106" stroke="none">Sony</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="23.626" fill="none" ry="15" cx="-353.4852" cy="235.9517"/> + </g> + </g> + <g id="y.node.28"> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-122.8472" xml:space="preserve" y="155.0516" stroke="none">is used by</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <rect fill="none" x="-129.8472" width="82.4375" height="30" y="135.8973"/> + </g> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.29" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(153,255,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(153,255,255)"> + <ellipse rx="24.71" ry="15" cx="-121.6429" cy="407.2199" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-139.3528" xml:space="preserve" y="411.3741" stroke="none">Cisco</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="24.71" fill="none" ry="15" cx="-121.6429" cy="407.2199"/> + </g> + </g> + <g onmouseout="HideTooltip(evt)" id="y.node.30" onmousemove="ShowTooltip(evt)"> + <g fill="rgb(153,255,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke="rgb(153,255,255)"> + <ellipse rx="35.207" ry="15" cx="-44.1638" cy="422.6991" stroke="none"/> + </g> + <g> + <g stroke-linecap="butt" transform="matrix(1,0,0,1,496,453)" text-rendering="geometricPrecision" font-family="'DejaVu Sans'" shape-rendering="geometricPrecision" font-weight="bold" stroke-miterlimit="1.45"> + <text x="-72.3708" xml:space="preserve" y="426.8534" stroke="none">Ericsson</text> + </g> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <ellipse rx="35.207" fill="none" ry="15" cx="-44.1638" cy="422.6991"/> + </g> + </g> + <g id="y.edge.3"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-25.9372 4.3987 L-123.4449 20.9351"/> + </g> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path d="M-131.3323 22.2727 L-118.6652 25.1959 L-122.459 20.7679 L-120.3373 15.3367 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.14"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-213.7405 22.8375 L-369.0458 -1.3722"/> + <path d="M-376.9503 -2.6043 L-365.8636 4.1843 L-368.0577 -1.2181 L-364.3234 -5.6964 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.15"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-213.7405 41.1596 L-375.238 87.7972"/> + <path d="M-382.924 90.0167 L-370.0078 91.4911 L-374.2773 87.5197 L-372.7823 81.8837 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.0"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M16.7213 11.8122 L115.1654 81.3545"/> + <path d="M121.6996 85.9703 L114.7833 74.9628 L114.3487 80.7775 L109.0135 83.1304 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.17"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M138.6514 115.9703 L57.774 399.2729"/> + <path d="M55.5779 406.9655 L63.68 396.7991 L58.0485 398.3113 L54.0641 394.054 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.18"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M143.8831 115.9703 L159.9748 370.1664"/> + <path d="M160.4802 378.1505 L164.7121 365.8586 L159.9116 369.1684 L154.732 366.4903 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.1"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M10.2905 -13.8791 L88.3421 -119.149"/> + <path d="M93.1069 -125.5753 L81.9433 -118.9138 L87.7466 -118.3457 L89.9762 -112.9579 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.4"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M154.2158 -147.4308 L347.8994 -173.9937"/> + <path d="M355.8252 -175.0807 L343.2571 -178.4039 L346.9087 -173.8578 L344.6159 -168.4966 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.2"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-10.9994 -13.7119 L-92.4645 -115.2664"/> + <path d="M-97.4703 -121.5067 L-93.8618 -109.0176 L-91.8388 -114.4864 L-86.0614 -115.2749 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.10"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-105.9352 -151.5067 L-46.7737 -400.2344"/> + <path d="M-44.9225 -408.0173 L-52.5636 -397.5 L-47.0051 -399.2616 L-42.835 -395.186 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.16"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-194.4922 44.2606 L-363.9232 160.0145"/> + <path d="M-370.5288 164.5274 L-357.7998 161.8865 L-363.0975 159.4504 L-363.4409 153.6295 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.6"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M118.2216 -155.5753 L256.9722 -304.3101"/> + <path d="M262.4293 -310.1599 L250.5875 -304.7959 L256.2901 -303.5789 L257.8998 -297.9745 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.5"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M134.3295 -155.5753 L306.5338 -241.3887"/> + <path d="M313.6941 -244.9568 L300.7237 -244.0798 L305.6388 -240.9427 L305.1838 -235.1295 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.9"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M101.8836 -155.5753 L64.0152 -397.8167"/> + <path d="M62.7796 -405.7207 L59.693 -393.0925 L64.1697 -396.8287 L69.573 -394.637 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.8"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M106.3114 -155.5753 L137.2307 -378.2436"/> + <path d="M138.3311 -386.1676 L131.7281 -374.9693 L137.0932 -377.2531 L141.6331 -373.5939 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.7"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M111.297 -155.5753 L201.4017 -346.7867"/> + <path d="M204.8119 -354.0234 L195.1736 -345.2997 L200.9754 -345.8821 L204.2196 -341.0369 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.23"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M162.3777 85.9703 L389.3263 -89.1067"/> + <path d="M395.6605 -93.9932 L383.1051 -90.6224 L388.5345 -88.4959 L389.2132 -82.7046 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.19"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M150.6198 115.9703 L251.2465 312.3481"/> + <path d="M254.8948 319.4678 L253.8722 306.5081 L250.7905 311.4581 L244.9726 311.0684 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.20"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M166.1817 115.9703 L324.2522 217.9592"/> + <path d="M330.9744 222.2964 L323.6018 211.5892 L323.4119 217.417 L318.1803 219.992 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.21"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M182.2744 101.8921 L348.4385 105.7856"/> + <path d="M356.4363 105.973 L344.5567 100.6933 L347.4388 105.7622 L344.3224 110.6905 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.22"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M180.241 85.9703 L387.7073 2.5553"/> + <path d="M395.1298 -0.429 L382.1308 -0.5916 L386.7795 2.9284 L385.8613 8.6866 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.11"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-114.0929 -151.5067 L-177.9128 -360.076"/> + <path d="M-180.2536 -367.7259 L-181.5236 -354.7881 L-177.6202 -359.1198 L-171.9612 -357.7141 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.12"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-132.0355 -151.5067 L-293.4008 -258.9287"/> + <path d="M-300.0602 -263.3619 L-292.8419 -252.55 L-292.5684 -258.3746 L-287.3004 -260.8742 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.13"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-167.6935 -137.7664 L-328.7143 -141.2522"/> + <path d="M-336.7124 -141.4254 L-324.8235 -136.1668 L-327.7145 -141.2306 L-324.607 -146.1645 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.24"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-8.3794 14.2666 L-75.7668 128.9991"/> + <path d="M-79.8183 135.8973 L-69.4296 128.0823 L-75.2603 128.1369 L-78.0523 123.0178 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.25"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-129.8472 164.134 L-324.7858 226.7353"/> + <path d="M-332.4027 229.1814 L-319.4486 230.2729 L-323.8337 226.4296 L-322.5062 220.7518 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.26"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-105.5021 165.8973 L-256.8684 300.4558"/> + <path d="M-262.8475 305.7709 L-250.5569 301.5351 L-256.121 299.7914 L-257.2009 294.0613 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.27"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-94.915 165.8973 L-176.4538 360.4545"/> + <path d="M-179.546 367.8327 L-170.2963 358.698 L-176.0673 359.5322 L-179.5191 354.8328 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.28"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-90.5605 165.8973 L-118.6948 384.331"/> + <path d="M-119.7168 392.2655 L-113.2248 381.0025 L-118.5671 383.3392 L-123.1429 379.7251 Z" stroke="none"/> + </g> + </g> + <g id="y.edge.29"> + <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,496,453)" stroke-linecap="butt"> + <path fill="none" d="M-86.1746 165.8973 L-47.9033 399.8404"/> + <path d="M-46.6117 407.7354 L-43.6147 395.0856 L-48.0647 398.8535 L-53.4835 396.7001 Z" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.10" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="302" height="17" y="0" clip-path="url(#clipPath3)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath3)" stroke="none">Copyright holder and unique sponsor of PRoot.</text> + <text fill="black" x="298" xml:space="preserve" y="13" clip-path="url(#clipPath3)" stroke="none"> </text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L302 0 L302 17 L0 17 L0 0 ZM1 1 L301 1 L301 16 L1 16 L1 1 Z" clip-path="url(#clipPath3)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.11" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="92" height="17" y="0" clip-path="url(#clipPath4)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath4)" stroke="none">Lightning talk</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L92 0 L92 17 L0 17 L0 0 ZM1 1 L91 1 L91 16 L1 16 L1 1 Z" clip-path="url(#clipPath4)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.21" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="659" height="17" y="0" clip-path="url(#clipPath5)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath5)" stroke="none">Enea uses PRoot in an internal tool (host environment for API testing of target software) at a customer.</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L659 0 L659 17 L0 17 L0 0 ZM1 1 L658 1 L658 16 L1 16 L1 1 Z" clip-path="url(#clipPath5)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.26" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="432" height="17" y="0" clip-path="url(#clipPath6)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath6)" stroke="none">About 175 domains references http://proot.me according to Google</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L432 0 L432 17 L0 17 L0 0 ZM1 1 L431 1 L431 16 L1 16 L1 1 Z" clip-path="url(#clipPath6)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.27" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="496" height="17" y="0" clip-path="url(#clipPath7)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath7)" stroke="none">Sony has an internal wiki page about CE Linux that references http://proot.me</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L496 0 L496 17 L0 17 L0 0 ZM1 1 L495 1 L495 16 L1 16 L1 1 Z" clip-path="url(#clipPath7)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.29" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="403" height="17" y="0" clip-path="url(#clipPath8)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath8)" stroke="none">Cisco has an internal wiki page that references http://proot.me</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L403 0 L403 17 L0 17 L0 0 ZM1 1 L402 1 L402 16 L1 16 L1 1 Z" clip-path="url(#clipPath8)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + <g visibility="hidden" id="tooltip.y.node.30" pointer-events="none"> + <g fill="rgb(195,212,232)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(195,212,232)"> + <rect x="0" width="450" height="17" y="0" clip-path="url(#clipPath9)" stroke="none"/> + <text fill="black" x="4" xml:space="preserve" y="13" clip-path="url(#clipPath9)" stroke="none">Ericsson has an internal Bugzilla ticket that references http://proot.me</text> + </g> + <g fill="rgb(49,106,196)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(49,106,196)"> + <path d="M0 0 L450 0 L450 17 L0 17 L0 0 ZM1 1 L449 1 L449 16 L1 16 L1 1 Z" clip-path="url(#clipPath9)" fill-rule="evenodd" stroke="none"/> + </g> + </g> + </g> +</svg>
diff --git a/5.1.0/doc/howto-release.txt b/5.1.0/doc/howto-release.txt new file mode 100644 index 0000000..59bb724 --- /dev/null +++ b/5.1.0/doc/howto-release.txt
@@ -0,0 +1,45 @@ +How to make a release of PRoot? +=============================== + +This document summarizes checks that must be performed before +releasing PRoot or CARE. + +Checks +------ + ++ Sanity checks: + * on ARM and *all* OBS distros (x86 and x86_64): `make -C test` + * on x86_64, with *and* without seccomp: + - `make -C tests` on *all* OBS distros + - `make -C tests memcheck` + - `CFLAGS=-fsanitize=address LDFLAGS=-lasan` + - `make -C tests V=1 2>&1 | grep talloc` + ++ Fonctional checks: no regressions must appear with respect to + tests/validation.mk and to the configurations tested in the previous + release (`git show tags/...`). + ++ Performance checks: the following command must not suffer from + unexpected performance regression:: + + time proot -R / perl -e 'system("/usr/bin/true") for (1..10000)' + + where "/usr/bin/true" is a symlink to "/bin/true". + ++ Static analysis: Coverity Scan and Clang Scan Build must not report + new issues. + + +Documentation Update +-------------------- + +0. update "doc/changelog.txt" + +1. update the release number in "doc/proot/manual.txt" + +2. regenerate the documentation: `make -C doc` + +3. regenerate the command-line interface: `cp doc/*.h + src/cli/; $EDITOR src/cli/{proot,care}.h` + +4. upload the generated RPM .spec file to OBS
diff --git a/5.1.0/doc/proot/changelog.txt b/5.1.0/doc/proot/changelog.txt new file mode 100644 index 0000000..cb86311 --- /dev/null +++ b/5.1.0/doc/proot/changelog.txt
@@ -0,0 +1,1171 @@ +Release v5.1.0 +============== + +New features +------------ + ++ Processes under PRoot now appear with their real names, that is, + they are not renamed "ld-linux.so" or "prooted-..." anymore: + + before: + + $ proot-v4.0.3 ps + PID TTY TIME CMD + 7885 pts/11 00:00:00 bash + 8131 pts/11 00:00:00 proot-v4.0.3 + 8132 pts/11 00:00:00 ld-2.17.so + + $ proot-v5.0.0 ps + PID TTY TIME CMD + 7885 pts/11 00:00:00 bash + 7916 pts/11 00:00:00 proot-v5.0.0 + 7917 pts/11 00:00:00 prooted-7916-Jb + + now: + + $ proot-v5.1.0 ps + PID TTY TIME CMD + 7885 pts/11 00:00:00 bash + 8585 pts/11 00:00:00 proot-v5.1.0 + 8586 pts/11 00:00:00 ps + +Fixes +----- + ++ It is now possible to use GDB against multi-threaded programs under + PRoot x86_64 and x86. + ++ It is possible to execute x86_64 programs from x86 programs again. + ++ It is possible to use x86 ptrace-based programs (strace, gdb, ...) + under PRoot x86_64 again. + ++ The loader is now built with the "build-id" linker option explicitly + disabled. This special section might interfere with loaded + programs. + ++ The loader can now load relocatable objects that have a predefined + base address. + +Acknowledgements +---------------- + +Thanks to Erwan Gouriou, Sébastien Gandon, Christian milkylainen, +Henrik Wallin, and Frank Teo for their bug reports and tests. + +Thanks to Jérôme Audu, Yann Droneaud, and Christophe Monat for their +precious help. + + +Release v5.0.0 +============== + +Highlight +--------- + +PRoot used to rely on the ELF loader embedded in the ELF interpreter +from the GNU libc. Sadly this latter suffers from many issues: + ++ programs that use constructors or destructors might crash: a typical + example is C++ programs. + ++ programs that rely on the "rpath" mechanism and that are invoked + through a symlink might not start: a typical example is the JVM on + Debian. + ++ programs that read processes command-line migth be confused because + initial argv[0] is replaced: typical examples are ps and top. + +Moreover not all ELF interpreters provide this feature. For instance, +ELF interpreters shipped with Bionic (Android) and some versions of +the uClibC can't be used as ELF loaders. As a consequence it was not +possible to proot into a rootfs that uses such C library. + +Now PRoot has its own loader, that means all the limitations above +doesn't exist anymore. + +Fixes +----- + ++ Most bugs related to shebang support -- ie. "#!" at the beginning of + a program -- were fixed. + +Command-line interface changes +------------------------------ + ++ PRoot now starts a login shell when no command is specified; this + makes the shell read profile files from the guest rootfs, as + expected by some guest programs. To get the old behavior, launch + "/bin/sh" explicitly: + + proot -r whatever /bin/sh + ++ The -R option now binds "/run" and "/var/run/dbus/system_bus_socket" + too. This is useful for guest programs that need to communicate + with host services. + + +Release v4.0.3 +============== + ++ Heap emulation is disabled when a "suspicious" call to brk(2) is + actually legit, as it might be the case when launching the very + first program. + ++ The "-0" and "-S" options ("root" identity emulation) now fake + success of mknodat(2), as it was the case for mknod(2) previously. + This missing feature was revealed by the AArch64 port. + ++ The "-k" option (kernel compatibility emulation) now works on + Linux/AArch64. + +Thanks to Rémi Duraffort for the bug reports and for his LAVA testing +platform! + + +Release v4.0.2 +============== + ++ Fix how the very first program is launched by PRoot. Previously, + argv[0] was not preserved when the very first program was launched + through a symbolic link. This old behavior used to bug programs + like Busybox and python-exec. Thanks to "hhm", Ivailo "fluxer" + Monev, and Joakim Tjernlund for the bug reports. + ++ Fix renameat(2) sysexit support. There was a bug in PRoot that was + exposed by the Aarch64 (a.k.a arm64) port only but that might affect + other architectures. + ++ Fix build for AArch64. Thanks to Rémi Duraffort for the patches and + for the Debian/arm64 testing platform. + ++ Fix support for "long" socket paths. These can only be 108 bytes + long; this limit might be easily reached with PRoot since the path + to the rootfs is always prepended. The solution was to + automatically bind this long path to a shorter path. This bug was + exposed by LibreOffice and Yocto's pseudo. Thanks to Christophe + Guillon for the bug report. + + +Release v4.0.1 +============== + ++ Fix a couple of portability issues in the testsuite. Thanks to Rémi + Duraffort for all the tests he made on his instance of Linaro LAVA. + ++ Set $PWD to the value specified by the -w option, otherwise Bash pwd + builtin might be confused under some specific circumstances. Thanks + to Jérémy Bobbio for the bug report. + ++ Fix support for accessat and fchmodat syscalls: they have only three + parameters, not four. This bug was exposed by Gentoo's sandbox: + + proot -S gentoo-amd64-hardened+nomultilib-rootfs emerge util-linux + + +Release v4.0.0 +============== + +Highlights +---------- + ++ It is now possible to use GDB, Strace, or any other program based on + "ptrace" under PRoot. This was not the case previously because it + is not possible to stack ptracers on Linux, so an emulation layer + was developed in order to bypass this limitation. This has required + a lot of changes in PRoot, hence the major number version bumping. + It was mostly tested on x86_64, and partially tested on x86 and ARM. + This ptrace emulation support is still experimental, and there are a + couple of known issues, but feel free to report unexpected behaviors + if you need a fix. + ++ A new command-line option is available: "-S". It is similar to the + "-R" option expect it enables the "-0" option and binds only a + minimal set of paths that are known to not be updated by package + installations, to avoid unexpected changes on host files. This + option is useful to safely create and install packages into the + guest rootfs. For example: + + $ proot -S ubuntu-14.04-rootfs/ apt-get install samba + + or: + + $ proot -S ubuntu-14.04-rootfs/ + # apt-get install samba + + If "-0 -R" is used instead of "-S", the same command fails since it + tries to update "/etc/group", which is bound to the host system and + is not writable (assuming PRoot is ran without privileges): + + $ proot -0 -R ubuntu-14.04-rootfs/ + # apt-get install samba + [...] + Adding group `sambashare' (GID 105) ... + Permission denied + ++ The fake_id0 extension can now fake any user and group identifiers. + That means, when "-0" is specified, PRoot-ed processes can change + their real, effective and saved identifiers, with respect to the + rules described in setuid, setfsuid, setreuid, setresuid, and + setfsuid manuals. Also, the new command-line option "-i" was added + to change explicitly the identifiers to the specified values. This + option will be used by CARE to re-execute with the same initial + identifiers, but it could also be useful to threaten your teammates + ;). Note that the "-0" option is actually the same as "-i 0:0". + ++ The old command-line interface is not supported anymore. That means + it is now impossible to specify the path to the guest rootfs without + using -r or -R. Also, -Q and -B options are definitively gone, + instead the -R option must be specified, respectively with and + without -q. See PRoot v3.1 release notes for details. + +Fixes +----- + ++ getcwd(2) and chdir(2) now return the correct error code when, + respectively, the current directory does not exist anymore and the + target directory doesn't have the "search" permission. + ++ Named file descriptors (ie. links in /proc/<pid>/fd/*) are not + dereferenced anymore since they may point to special objects like + pipes, sockets, inodes, ... Such objects do not exist on the + file-system name-space, so dereferencing them used to cause + unexpected errors. + ++ Extensions now see every component of canonicalized paths. An + optimization in the canonicalization loop used to skip the first + part of a path if it was known to be already canonicalized, sadly + this short-cut may confuse some extensions, like -0. + ++ Temporary files and directories created by PRoot for its own purpose + are now automatically deleted when PRoot exits. + + +Miscellaneous +------------- + ++ PRoot does not rely on GCC C extensions anymore, like nested + functions. That means its stack does not have to be executable + (this is required for hardened Linux systems), and it can now be + compiled with Clang. + ++ The ASLR (Address Space Layout Randomization) is not disabled + anymore, and the heap is now emulated on all architectures. + + +Internal changes +---------------- + +This section is dedicated to developers. + ++ PRoot now remembers the parent of all tracees, it is similar to a + traced process tree. This was required for the ptrace emulation + support, but this could be useful to some extensions. + ++ It is now possible to restart a tracee with any ptrace restart mode: + single-step, single-block, ... + ++ Functions {peek,poke}_mem were replaced with functions + {peek,poke}_{,u}int{8,16,32,64}. These new functions performs type + conversion and fetch only the necessary amount of data in target + tracee's memory to avoid invalid accesses. + ++ There is a new interface to handle ELF auxiliary vectors. See + ptrace emulation, kompat and fake_id0 extensions for usage examples. + ++ There is a new interface to create temporary files and directories + that are automatically deleted on exit. See CARE extension, glue + and auxv support for usage examples. + ++ When built with GCC function instrumentation support, PRoot prints + the currently called function on standard error stream (stderr). + +Thanks +------ + +Thanks go to Stephen McCamant, Oren Tirosh, Jérôme Audu, and Carlos +Hernan Prada Rojas for their bug reports and tests; and to Rémi +Duraffort for his contributions. + + +Release v3.2.2 +============== + ++ Remove a useless memory layout constraint on x86_64 that bugs some + programs like java and or qemu. + ++ It is now possible to launch the initial program from a relative + path without specifying the "./" prefix, for example: + + $ proot path/to/program + ++ Don't discard fcntl(F_DUPFD_CLOEXEC) systematically when the kompat + extension is enabled (-k option). + ++ Don't use syscalls that require Linux >= 2.6.16 anymore. + + +Release v3.2.1 +============== + ++ Make ptrace/seccomp even more portable on Ubuntu. + +Thanks to Maxence Dalmais for the bug report and tests. + + +Release v3.2 +============ + +This release was mostly driven by the requirements of "CARE", a new +project based on PRoot that will be released publicly soon on +http://reproducible.io. For information, "CARE" is the short for +"Comprehensive Archiver for Reproducible Execution". + +Highlights +---------- + ++ Many bugs exposed by a couple of static code analyzers (Coverity, + Clang, ...) and some test-suites (Linux Test Project, libuv, ...) + are now fixed. + ++ The "kompat" extension ("-k" option) can now emulate most of the + kernel features that would be required by the guest system but that + are not available on the host kernel. For example, it can now make + programs from Ubuntu 13.04 64-bit run on RedHat 5 64-bit without any + further tweaks: + + rh5-64$ proot -k 3.8 -R ubuntu-13.04-64bit/ ... + ++ On ARM and x86_64, the heap segment is now emulated with a regular + memory mapping to ensure this former always exists. This was + required because some kernels might put a non-fixed memory mapping + right after the regular heap when using some GNU ELF interpreters + (ld.so) as loaders. Without the heap segment emulation, some + programs like Bash would crash because the heap can't grow anymore: + + bash: xmalloc: locale.c:73: cannot allocate 2 bytes (0 bytes allocated) + +Miscellaneous +------------- + ++ When using the "-R" option, the path to the guest rootfs is now + bound into the guest rootfs itself. This is required to run + programs that search for their DSOs in /proc/self/maps, like VLC for + instance. + ++ When using the "-v" option with a level greater than 2, syscalls are + now printed as strings instead of numbers, à la strace: + + $ proot -v 3 true + [...] + proot info: pid 29847: sysenter start: mmap(0x0, 0x2d141, 0x1, 0x2, 0x3, 0x0) [...] + [...] + ++ The article about the migration from ScratchBox2 is now publicly + available: + + https://github.com/cedric-vincent/PRoot/blob/v3.2/doc/articles/howto_migrate_from_scratchbox2.txt + +Internal changes +---------------- + ++ Tools based on PRoot (CARE, DepsTracker, ATOS, ...) can now easily + replace the original command-line interface with their own + command-line interface. + ++ It is now possible to chain forged syscalls to a regular syscall. + Search for "register_chained_syscall" in the sources for details. + ++ A couple of new helpers are now visible from the extensions. + +Thanks +------ + ++ Bug reports and tests: Corbin Champion, Maxence Dalmais, and Nicolas + Cornu. + ++ Static code analysis: Antoine Moynault and Christophe Guillon. + ++ Patches: Rémi Duraffort. + ++ Unexpected hint: Christophe Monat :) + + +Release v3.1 +============ + +Command-line interface changes +------------------------------ + ++ The initial command is not search in "." anymore, unless the "./" + prefix is specified or unless "." is in $PATH, as expected. + ++ The "-B" and "-Q" options are obsoleted by the new "-R" option. + This latter is equivalent to "-B -r", as there was actually no point + at using the "-B" option without "-r". + ++ A warning is now emitted when the rootfs is specified à la + chroot(1), that is, without using "-r" or "-R". + +The old command-line interface is not documented anymore, but it will +be still supported for a couple of releases. Although, users are +strongly encouraged to switch to the new one: + + ====================== ================= + old CLI new CLI + ====================== ================= + proot rootfs proot -r rootfs + proot -B rootfs proot -R rootfs + proot -B -r rootfs proot -R rootfs + proot -Q qemu rootfs proot -R rootfs -q qemu + proot -Q qemu -r rootfs proot -R rootfs -q qemu + ======================= ======================= + +Extensions +---------- + ++ The "kompat" extension ("-k" option) has been greatly enhanced. For + example, it can now make programs from Ubuntu 13.04 32-bit run on + RedHat 5 64-bit: + + rh5-64$ proot -k 3.8 -R ubuntu-13.04-32bit/ ... + ++ The "fake id0" extension ("-0" option) handles more syscalls: + mknod(2), capset(2), setxattr(2), setresuid(2), setresgid(2), + getresuid(2), and getresgid(2). + +Miscellaneous +------------- + ++ PRoot is now compiled with large file-system support (LFS), this + make it works with 64-bit file-systems (eg. CIFS) on 32-bit + platforms. + ++ The special symbolic link "/proc/self/root" now points to the guest + rootfs, that is, to the path specified by "-r" or "-R". Just like + with chroot(2), this symlink may be broken as the referenced host + path likely does not exist in the guest rootfs. Although, this + symlink is typically used to know if a process is under a chroot-ed + environment. + ++ Under QEMU, LD_LIBRARY_PATH is not clobbered anymore when a guest + program is launched by a host program. + ++ When seccomp-filter is enabled, this release is about 8% faster than + the previous one. + ++ A couple of bugs reported by Scan Coverity are fixed. + +Thanks +------ + +Special thanks to Stephan Hadamik, Jérôme Audu, and Rémi Duraffort for +their valuable help. + + +Release 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/<PID>/exe, for any <PID> monitored by PRoot; and + + * /proc/<PID>/fd/<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 <p1>:<p2>" where <p1> is the location of the mirror + within the alternate rootfs and <p2> 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. +
diff --git a/5.1.0/doc/proot/man.1 b/5.1.0/doc/proot/man.1 new file mode 100644 index 0000000..f810410 --- /dev/null +++ b/5.1.0/doc/proot/man.1
@@ -0,0 +1,727 @@ +.\" Man page generated from reStructuredText. +. +.TH PROOT 1 "2014-12-12" "5.1.0" "" +.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 use PRoot as a generic Linux process instrumentation engine thanks +to its extension mechanism, see \fI\%CARE\fP for an example. 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\fP,\fB \ \-\-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. +.sp +It is recommended to use the \fB\-R\fP or \fB\-S\fP options instead. +.TP +.BI \-b \ path\fP,\fB \ \-\-bind\fB= path\fP,\fB \ \-m \ path\fP,\fB \ \-\-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\fP,\fB \ \-\-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. +.TP +.BI \-w \ path\fP,\fB \ \-\-pwd\fB= path\fP,\fB \ \-\-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\fP,\fB \ \-\-verbose\fB= value +Set the level of debug information to \fIvalue\fP\&. +.sp +The higher the integer \fIvalue\fP is, the more detailed debug +information is printed to the standard error stream. A negative +\fIvalue\fP makes PRoot quiet except on fatal errors. +.TP +.B \-V\fP,\fB \-\-version\fP,\fB \-\-about +Print version, copyright, license and contact, then exit. +.TP +.B \-h\fP,\fB \-\-help\fP,\fB \-\-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\fP,\fB \ \-\-kernel\-release\fB= string +Make current kernel appear as kernel release \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 features that are available in the kernel release specified by +\fIstring\fP but that are missing in the current kernel. +.TP +.B \-0\fP,\fB \-\-root\-id +Make current user appear as "root" and fake its privileges. +.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\&. +.TP +.BI \-i \ string\fP,\fB \ \-\-change\-id\fB= string +Make current user and group appear as \fIstring\fP "uid:gid". +.sp +This option makes the current user and group appear as \fIuid\fP and +\fIgid\fP\&. Likewise, files actually owned by the current user and +group appear as if they were owned by \fIuid\fP and \fIgid\fP instead. +Note that the \fB\-0\fP option is the same as \fB\-i 0:0\fP\&. +.UNINDENT +.SS Alias options +.sp +The following options are aliases for handy sets of options. +.INDENT 0.0 +.TP +.BI \-R \ path +Alias: \fB\-r *path*\fP + a couple of recommended \fB\-b\fP\&. +.sp +Programs isolated in \fIpath\fP, a guest rootfs, might still need to +access information about the host system, as it is illustrated in +the \fBExamples\fP section of the manual. These host information +are typically: user/group definition, network setup, run\-time +information, users\(aq files, ... On all Linux distributions, they +all lie in a couple of host files and directories that are +automatically bound by this option: +.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 +/run/ +.IP \(bu 2 +/var/run/dbus/system_bus_socket +.IP \(bu 2 +$HOME +.IP \(bu 2 +\fIpath\fP +.UNINDENT +.TP +.BI \-S \ path +Alias: \fB\-0 \-r *path*\fP + a couple of recommended \fB\-b\fP\&. +.sp +This option is useful to safely create and install packages into +the guest rootfs. It is similar to the \fB\-R\fP option expect it +enables the \fB\-0\fP option and binds only the following minimal set +of paths to avoid unexpected changes on host files: +.INDENT 7.0 +.IP \(bu 2 +/etc/host.conf +.IP \(bu 2 +/etc/hosts +.IP \(bu 2 +/etc/nsswitch.conf +.IP \(bu 2 +/etc/resolv.conf +.IP \(bu 2 +/dev/ +.IP \(bu 2 +/sys/ +.IP \(bu 2 +/proc/ +.IP \(bu 2 +/tmp/ +.IP \(bu 2 +/run/shm +.IP \(bu 2 +$HOME +.IP \(bu 2 +\fIpath\fP +.UNINDENT +.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/<pid>/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 enables one to relocate files and directories. This is +typically useful to trick 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/ +? \- +? 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\-R\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/ + +$ ps \-o tty,command +TT COMMAND +pts/6 \-bash +pts/6 proot \-R /mnt/slackware\-8.0/ +pts/6 \- +pts/6 ps \-o tty,command +.ft P +.fi +.UNINDENT +.UNINDENT +.SS \fBchroot\fP + \fBmount \-\-bind\fP + \fBsu\fP equivalent +.sp +Some programs will not work correctly if they are not run by the +"root" user, this is typically the case with package managers. PRoot +can fake the root identity and its privileges when the \fB\-0\fP (zero) +option is specified: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +proot \-r /mnt/slackware\-8.0/ \-0 + +# id +uid=0(root) gid=0(root) [...] + +# mkdir /tmp/foo +# chmod a\-rwx /tmp/foo +# echo \(aqI bypass file\-system permissions.\(aq > /tmp/foo/bar +# cat /tmp/foo/bar +I bypass file\-system permissions. +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +This option is typically required to create or install packages into +the guest rootfs. Note it is \fInot\fP recommended to use the \fB\-R\fP +option when installing packages since they may try to update bound +system files, like \fB/etc/group\fP\&. Instead, it is recommended to use +the \fB\-S\fP option. This latter enables the \fB\-0\fP option and binds +only paths that are known to not be updated by packages: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +proot \-S /mnt/slackware\-8.0/ + +# installpkg perl.tgz +Installing package perl... +.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: +.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\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 one 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\&. It is also +available as highly compatible static binaries: +.INDENT 0.0 +.IP \(bu 2 +for x86_64: \fI\%http://static.proot.me/proot\-x86_64\fP +.IP \(bu 2 +for x86: \fI\%http://static.proot.me/proot\-x86\fP +.IP \(bu 2 +for ARM: \fI\%http://static.proot.me/proot\-arm\fP +.IP \(bu 2 +other architectures: on demand. +.UNINDENT +.SS Rootfs +.sp +Here follows a couple of URLs where some rootfs archives can be freely +downloaded. Note that \fBmknod\fP errors reported by \fBtar\fP when +extracting these archives can be safely ignored since special files +are typically bound (see \fB\-R\fP option for details). +.INDENT 0.0 +.IP \(bu 2 +\fI\%http://download.openvz.org/template/precreated/\fP +.IP \(bu 2 +\fI\%https://images.linuxcontainers.org/images/\fP +.IP \(bu 2 +\fI\%http://distfiles.gentoo.org/releases/\fP +.IP \(bu 2 +\fI\%http://cdimage.ubuntu.com/ubuntu\-core/releases/\fP +.IP \(bu 2 +\fI\%http://archlinuxarm.org/developers/downloads\fP +.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, patches, ... +Copyright (C) 2014 STMicroelectronics, licensed under GPL v2 or later. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C + _____ _____ ___ +| __ \e __ \e_____ _____| |_ +| __/ / _ \e/ _ \e _| +|__| |__|__\e_____/\e_____/\e____| +.ft P +.fi +.UNINDENT +.UNINDENT +.\" Generated by docutils manpage writer. +.
diff --git a/5.1.0/doc/proot/manual.txt b/5.1.0/doc/proot/manual.txt new file mode 100644 index 0000000..53938c5 --- /dev/null +++ b/5.1.0/doc/proot/manual.txt
@@ -0,0 +1,560 @@ +======= + PRoot +======= + +------------------------------------------------------------------------- +``chroot``, ``mount --bind``, and ``binfmt_misc`` without privilege/setup +------------------------------------------------------------------------- + +:Date: 2014-12-12 +:Version: 5.1.0 +: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 use PRoot as a generic Linux process instrumentation engine thanks +to its extension mechanism, see CARE_ for an example. 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. + +.. _CARE: http://reproducible.io + + +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. + + It is recommended to use the ``-R`` or ``-S`` options instead. + +-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. + +-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 detailed 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 + Make current kernel appear as kernel release *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 features that are available in the kernel release specified by + *string* but that are missing in the current kernel. + +-0, --root-id + Make current user appear as "root" and fake its privileges. + + 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``. + +-i string, --change-id=string + Make current user and group appear as *string* "uid:gid". + + This option makes the current user and group appear as *uid* and + *gid*. Likewise, files actually owned by the current user and + group appear as if they were owned by *uid* and *gid* instead. + Note that the ``-0`` option is the same as ``-i 0:0``. + + +Alias options +------------- + +The following options are aliases for handy sets of options. + +-R path + Alias: ``-r *path*`` + a couple of recommended ``-b``. + + Programs isolated in *path*, a guest rootfs, might still need to + access information about the host system, as it is illustrated in + the ``Examples`` section of the manual. These host information + are typically: user/group definition, network setup, run-time + information, users' files, ... On all Linux distributions, they + all lie in a couple of host files and directories that are + automatically bound by this option: + + * /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/ + * /run/ + * /var/run/dbus/system_bus_socket + * $HOME + * *path* + +-S path + Alias: ``-0 -r *path*`` + a couple of recommended ``-b``. + + This option is useful to safely create and install packages into + the guest rootfs. It is similar to the ``-R`` option expect it + enables the ``-0`` option and binds only the following minimal set + of paths to avoid unexpected changes on host files: + + * /etc/host.conf + * /etc/hosts + * /etc/nsswitch.conf + * /etc/resolv.conf + * /dev/ + * /sys/ + * /proc/ + * /tmp/ + * /run/shm + * $HOME + * *path* + + +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/<pid>/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 enables one to relocate files and directories. This is +typically useful to trick 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/ + ? sh + ? ps -o tty,command + +Actually there's a bunch of such specific files, that's why PRoot +provides the option ``-R`` to bind automatically a pre-defined list of +recommended paths:: + + proot -R /mnt/slackware-8.0/ + + $ ps -o tty,command + TT COMMAND + pts/6 bash + pts/6 proot -R /mnt/slackware-8.0/ + pts/6 sh + pts/6 ps -o tty,command + + +``chroot`` + ``mount --bind`` + ``su`` equivalent +------------------------------------------------- + +Some programs will not work correctly if they are not run by the +"root" user, this is typically the case with package managers. PRoot +can fake the root identity and its privileges when the ``-0`` (zero) +option is specified:: + + proot -r /mnt/slackware-8.0/ -0 + + # id + uid=0(root) gid=0(root) [...] + + # mkdir /tmp/foo + # chmod a-rwx /tmp/foo + # echo 'I bypass file-system permissions.' > /tmp/foo/bar + # cat /tmp/foo/bar + I bypass file-system permissions. + +This option is typically required to create or install packages into +the guest rootfs. Note it is *not* recommended to use the ``-R`` +option when installing packages since they may try to update bound +system files, like ``/etc/group``. Instead, it is recommended to use +the ``-S`` option. This latter enables the ``-0`` option and binds +only paths that are known to not be updated by packages:: + + proot -S /mnt/slackware-8.0/ + + # installpkg perl.tgz + Installing package perl... + + +``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``:: + + proot -R /mnt/armslack-12.2/ -q qemu-arm + + $ cat /etc/motd + Welcome to ARMedSlack Linux 12.2 + +The parameter of the ``-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 one 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. It is also +available as highly compatible static binaries: + +* for x86_64: http://static.proot.me/proot-x86_64 + +* for x86: http://static.proot.me/proot-x86 + +* for ARM: http://static.proot.me/proot-arm + +* other architectures: on demand. + + +Rootfs +------ + +Here follows a couple of URLs where some rootfs archives can be freely +downloaded. Note that ``mknod`` errors reported by ``tar`` when +extracting these archives can be safely ignored since special files +are typically bound (see ``-R`` option for details). + +* http://download.openvz.org/template/precreated/ + +* https://images.linuxcontainers.org/images/ + +* http://distfiles.gentoo.org/releases/ + +* http://cdimage.ubuntu.com/ubuntu-core/releases/ + +* http://archlinuxarm.org/developers/downloads + +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, patches, ... +Copyright (C) 2014 STMicroelectronics, licensed under GPL v2 or later. + +:: + + _____ _____ ___ + | __ \ __ \_____ _____| |_ + | __/ / _ \/ _ \ _| + |__| |__|__\_____/\_____/\____| +
diff --git a/5.1.0/doc/proot/roadmap.txt b/5.1.0/doc/proot/roadmap.txt new file mode 100644 index 0000000..d2dd1f1 --- /dev/null +++ b/5.1.0/doc/proot/roadmap.txt
@@ -0,0 +1,195 @@ +========= + Roadmap +========= + +PRoot v5.1.1 +============ + +* Fix ptrace emulation: on ARM. + +* Fix ``proot-x86_64 -k ... -r rootfs-i686 uname -m`` + +* Fix TODO in test-517e1d6a + +* Understand why loader-wrapped.o is so big on x86_64? + + +PRoot v5.2: leveraging on the loader +==================================== + +Highlight +--------- + +* Position-independent programs are loaded to predefined addresses, + even if ASLR is enabled. This might results in conflicts with + mappings created implicitly by the kernel ("[vdso]", "[stack]", + ...). A reliable solution is to let the kernel choose the position + of position-independent programs. + +* The loader is loaded to a predefined address, si it might conflicts + with programs that are loaded to the same address. A solution is to + detect such situation and to make the loader relocate itself. + +* The loader stays in memory, even once it is not used anymore. This + might create useless address-space pressure for programs that need + large memory mappings (ex. JVM). + +* Some programs assumes the heap segment is right after the data + segment (cf. issue 52 on Github). + +Misc. +----- + +* Write a loader for a.out programs, to be able to execute programs + from very old Linux distros :) + + +PRoot v6.0: VFS +=============== + +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. + + +Not yet scheduled +================= + +Fixes +----- + +* Fix ptrace emulation support on ARM. + +* Fix support for GDB v/fork & execve catchpoints. + +* Add support for unimplemented ptrace requests. + +* Add new hidden option "PROOT_MIN_HEAP_SIZE". + +* Fix ``mkdir foo; cd foo; rmdir ../foo; readlink /proc/self/cwd``. + +* 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 +-------- + +* Use PTRACE_O_EXITKILL if possible. + +* 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 + +* 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"). + +* 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 +------------- + +* Mention "container" in the 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 + + +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 Trinity/Scrashme (syscall fuzzers) against PRoot + + +Performance +----------- + +* prefetch_mem(): cache write-through memory access (read_string, fetch_args). + +* Fallback to /proc/<pid>/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)
diff --git a/5.1.0/doc/proot/rpm-spec b/5.1.0/doc/proot/rpm-spec new file mode 100644 index 0000000..87738a5 --- /dev/null +++ b/5.1.0/doc/proot/rpm-spec
@@ -0,0 +1,1232 @@ +%define version v5.1.0 + +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 + +BuildRequires: libtalloc-devel + +%if 0%{?suse_version} >= 1210 || 0%{?fedora_version} >= 15 +BuildRequires: glibc-static +%endif + +%if !0%{?suse_version} != 0 +BuildRequires: which +%endif + +%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 use PRoot as a generic Linux process instrumentation engine thanks +to its extension mechanism, see CARE for an example. 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/man.1 %{buildroot}/%{_mandir}/man1/proot.1 + +%check +env LD_SHOW_AUXV=1 true +cat /proc/cpuinfo +./src/proot -V +./src/proot -v 1 true +make -C tests + +%clean +rm -rf %{buildroot} + +%files +%defattr(-,root,root) +%{prefix}/bin/proot +%doc %{_mandir}/man1/proot.1* +%doc COPYING +%doc doc/* + +%changelog +* Thu Dec 11 2014 Cédric VINCENT <cedric.vincent@st.com> +Release v5.1.0 +============== + +New features +------------ + ++ Processes under PRoot now appear with their real names, that is, + they are not renamed "ld-linux.so" or "prooted-..." anymore: + + before: + + $ proot-v4.0.3 ps + PID TTY TIME CMD + 7885 pts/11 00:00:00 bash + 8131 pts/11 00:00:00 proot-v4.0.3 + 8132 pts/11 00:00:00 ld-2.17.so + + $ proot-v5.0.0 ps + PID TTY TIME CMD + 7885 pts/11 00:00:00 bash + 7916 pts/11 00:00:00 proot-v5.0.0 + 7917 pts/11 00:00:00 prooted-7916-Jb + + now: + + $ proot-v5.1.0 + PID TTY TIME CMD + 7885 pts/11 00:00:00 bash + 8585 pts/11 00:00:00 proot-v5.1.0 + 8586 pts/11 00:00:00 ps + +Fixes +----- + ++ It is now possible to use GDB against multi-threaded programs under + PRoot. + ++ It is possible to execute 32-bit programs from 64-bit programs + again. + ++ It is possible to use 32-bit ptrace-based programs (strace, gdb, + ...) under PRoot 64-bit again. + ++ The loader is now built with the "build-id" linker option explicitly + disabled. This special section might interfere with loaded + programs. + +Thanks +------ + +Thanks to Erwan Gouriou, Sébastien Gandon, Christian "milkylainen", +and Henrik Wallin for their bug reports and tests. + +Thanks to Jérôme Audu, Yann Droneaud and Christophe Monat for their +precious help to fix bugs. + + +Release v5.0.0 +============== + +Highlight +--------- + +PRoot used to rely on the ELF loader embedded in the ELF interpreter +from the GNU libc. Sadly this latter suffers from many issues: + ++ programs that use constructors or destructors might crash: a typical + example is C++ programs. + ++ programs that rely on the "rpath" mechanism and that are invoked + through a symlink might not start: a typical example is the JVM on + Debian. + ++ programs that read processes command-line migth be confused because + initial argv[0] is replaced: typical examples are ps and top. + +Moreover not all ELF interpreters provide this feature. For instance, +ELF interpreters shipped with Bionic (Android) and some versions of +the uClibC can't be used as ELF loaders. As a consequence it was not +possible to proot into a rootfs that uses such C library. + +Now PRoot has its own loader, that means all the limitations above +doesn't exist anymore. + +Fixes +----- + ++ Most bugs related to shebang support -- ie. "#!" at the beginning of + a program -- were fixed. + +Command-line interface changes +------------------------------ + ++ PRoot now starts a login shell when no command is specified; this + makes the shell read profile files from the guest rootfs, as + expected by some guest programs. To get the old behavior, launch + "/bin/sh" explicitly: + + proot -r whatever /bin/sh + ++ The -R option now binds "/run" and "/var/run/dbus/system_bus_socket" + too. This is useful for guest programs that need to communicate + with host services. + + +Release v4.0.3 +============== + ++ Heap emulation is disabled when a "suspicious" call to brk(2) is + actually legit, as it might be the case when launching the very + first program. + ++ The "-0" and "-S" options ("root" identity emulation) now fake + success of mknodat(2), as it was the case for mknod(2) previously. + This missing feature was revealed by the AArch64 port. + ++ The "-k" option (kernel compatibility emulation) now works on + Linux/AArch64. + +Thanks to Rémi Duraffort for the bug reports and for his LAVA testing +platform! + + +Release v4.0.2 +============== + ++ Fix how the very first program is launched by PRoot. Previously, + argv[0] was not preserved when the very first program was launched + through a symbolic link. This old behavior used to bug programs + like Busybox and python-exec. Thanks to "hhm", Ivailo "fluxer" + Monev, and Joakim Tjernlund for the bug reports. + ++ Fix renameat(2) sysexit support. There was a bug in PRoot that was + exposed by the Aarch64 (a.k.a arm64) port only but that might affect + other architectures. + ++ Fix build for AArch64. Thanks to Rémi Duraffort for the patches and + for the Debian/arm64 testing platform. + ++ Fix support for "long" socket paths. These can only be 108 bytes + long; this limit might be easily reached with PRoot since the path + to the rootfs is always prepended. The solution was to + automatically bind this long path to a shorter path. This bug was + exposed by LibreOffice and Yocto's pseudo. Thanks to Christophe + Guillon for the bug report. + + +Release v4.0.1 +============== + ++ Fix a couple of portability issues in the testsuite. Thanks to Rémi + Duraffort for all the tests he made on his instance of Linaro LAVA. + ++ Set $PWD to the value specified by the -w option, otherwise Bash pwd + builtin might be confused under some specific circumstances. Thanks + to Jérémy Bobbio for the bug report. + ++ Fix support for accessat and fchmodat syscalls: they have only three + parameters, not four. This bug was exposed by Gentoo's sandbox: + + proot -S gentoo-amd64-hardened+nomultilib-rootfs emerge util-linux + + +Release v4.0.0 +============== + +Highlights +---------- + ++ It is now possible to use GDB, Strace, or any other program based on + "ptrace" under PRoot. This was not the case previously because it + is not possible to stack ptracers on Linux, so an emulation layer + was developed in order to bypass this limitation. This has required + a lot of changes in PRoot, hence the major number version bumping. + It was mostly tested on x86_64, and partially tested on x86 and ARM. + This ptrace emulation support is still experimental, and there are a + couple of known issues, but feel free to report unexpected behaviors + if you need a fix. + ++ A new command-line option is available: "-S". It is similar to the + "-R" option expect it enables the "-0" option and binds only a + minimal set of paths that are known to not be updated by package + installations, to avoid unexpected changes on host files. This + option is useful to safely create and install packages into the + guest rootfs. For example: + + $ proot -S ubuntu-14.04-rootfs/ apt-get install samba + + or: + + $ proot -S ubuntu-14.04-rootfs/ + # apt-get install samba + + If "-0 -R" is used instead of "-S", the same command fails since it + tries to update "/etc/group", which is bound to the host system and + is not writable (assuming PRoot is ran without privileges): + + $ proot -0 -R ubuntu-14.04-rootfs/ + # apt-get install samba + [...] + Adding group `sambashare' (GID 105) ... + Permission denied + ++ The fake_id0 extension can now fake any user and group identifiers. + That means, when "-0" is specified, PRoot-ed processes can change + their real, effective and saved identifiers, with respect to the + rules described in setuid, setfsuid, setreuid, setresuid, and + setfsuid manuals. Also, the new command-line option "-i" was added + to change explicitly the identifiers to the specified values. This + option will be used by CARE to re-execute with the same initial + identifiers, but it could also be useful to threaten your teammates + ;). Note that the "-0" option is actually the same as "-i 0:0". + ++ The old command-line interface is not supported anymore. That means + it is now impossible to specify the path to the guest rootfs without + using -r or -R. Also, -Q and -B options are definitively gone, + instead the -R option must be specified, respectively with and + without -q. See PRoot v3.1 release notes for details. + +Fixes +----- + ++ getcwd(2) and chdir(2) now return the correct error code when, + respectively, the current directory does not exist anymore and the + target directory doesn't have the "search" permission. + ++ Named file descriptors (ie. links in /proc/<pid>/fd/*) are not + dereferenced anymore since they may point to special objects like + pipes, sockets, inodes, ... Such objects do not exist on the + file-system name-space, so dereferencing them used to cause + unexpected errors. + ++ Extensions now see every component of canonicalized paths. An + optimization in the canonicalization loop used to skip the first + part of a path if it was known to be already canonicalized, sadly + this short-cut may confuse some extensions, like -0. + ++ Temporary files and directories created by PRoot for its own purpose + are now automatically deleted when PRoot exits. + + +Miscellaneous +------------- + ++ PRoot does not rely on GCC C extensions anymore, like nested + functions. That means its stack does not have to be executable + (this is required for hardened Linux systems), and it can now be + compiled with Clang. + ++ The ASLR (Address Space Layout Randomization) is not disabled + anymore, and the heap is now emulated on all architectures. + + +Internal changes +---------------- + +This section is dedicated to developers. + ++ PRoot now remembers the parent of all tracees, it is similar to a + traced process tree. This was required for the ptrace emulation + support, but this could be useful to some extensions. + ++ It is now possible to restart a tracee with any ptrace restart mode: + single-step, single-block, ... + ++ Functions {peek,poke}_mem were replaced with functions + {peek,poke}_{,u}int{8,16,32,64}. These new functions performs type + conversion and fetch only the necessary amount of data in target + tracee's memory to avoid invalid accesses. + ++ There is a new interface to handle ELF auxiliary vectors. See + ptrace emulation, kompat and fake_id0 extensions for usage examples. + ++ There is a new interface to create temporary files and directories + that are automatically deleted on exit. See CARE extension, glue + and auxv support for usage examples. + ++ When built with GCC function instrumentation support, PRoot prints + the currently called function on standard error stream (stderr). + +Thanks +------ + +Thanks go to Stephen McCamant, Oren Tirosh, Jérôme Audu, and Carlos +Hernan Prada Rojas for their bug reports and tests; and to Rémi +Duraffort for his contributions. + + +Release v3.2.2 +============== + ++ Remove a useless memory layout constraint on x86_64 that bugs some + programs like java and or qemu. + ++ It is now possible to launch the initial program from a relative + path without specifying the "./" prefix, for example: + + $ proot path/to/program + ++ Don't discard fcntl(F_DUPFD_CLOEXEC) systematically when the kompat + extension is enabled (-k option). + ++ Don't use syscalls that require Linux >= 2.6.16 anymore. + + +Release v3.2.1 +============== + ++ Make ptrace/seccomp even more portable on Ubuntu. + +Thanks to Maxence Dalmais for the bug report and tests. + + +Release v3.2 +============ + +This release was mostly driven by the requirements of "CARE", a new +project based on PRoot that will be released publicly soon on +http://reproducible.io. For information, "CARE" is the short for +"Comprehensive Archiver for Reproducible Execution". + +Highlights +---------- + ++ Many bugs exposed by a couple of static code analyzers (Coverity, + Clang, ...) and some test-suites (Linux Test Project, libuv, ...) + are now fixed. + ++ The "kompat" extension ("-k" option) can now emulate most of the + kernel features that would be required by the guest system but that + are not available on the host kernel. For example, it can now make + programs from Ubuntu 13.04 64-bit run on RedHat 5 64-bit without any + further tweaks: + + rh5-64$ proot -k 3.8 -R ubuntu-13.04-64bit/ ... + ++ On ARM and x86_64, the heap segment is now emulated with a regular + memory mapping to ensure this former always exists. This was + required because some kernels might put a non-fixed memory mapping + right after the regular heap when using some GNU ELF interpreters + (ld.so) as loaders. Without the heap segment emulation, some + programs like Bash would crash because the heap can't grow anymore: + + bash: xmalloc: locale.c:73: cannot allocate 2 bytes (0 bytes allocated) + +Miscellaneous +------------- + ++ When using the "-R" option, the path to the guest rootfs is now + bound into the guest rootfs itself. This is required to run + programs that search for their DSOs in /proc/self/maps, like VLC for + instance. + ++ When using the "-v" option with a level greater than 2, syscalls are + now printed as strings instead of numbers, à la strace: + + $ proot -v 3 true + [...] + proot info: pid 29847: sysenter start: mmap(0x0, 0x2d141, 0x1, 0x2, 0x3, 0x0) [...] + [...] + ++ The article about the migration from ScratchBox2 is now publicly + available: + + https://github.com/cedric-vincent/PRoot/blob/v3.2/doc/articles/howto_migrate_from_scratchbox2.txt + +Internal changes +---------------- + ++ Tools based on PRoot (CARE, DepsTracker, ATOS, ...) can now easily + replace the original command-line interface with their own + command-line interface. + ++ It is now possible to chain forged syscalls to a regular syscall. + Search for "register_chained_syscall" in the sources for details. + ++ A couple of new helpers are now visible from the extensions. + +Thanks +------ + ++ Bug reports and tests: Corbin Champion, Maxence Dalmais, and Nicolas + Cornu. + ++ Static code analysis: Antoine Moynault and Christophe Guillon. + ++ Patches: Rémi Duraffort. + ++ Unexpected hint: Christophe Monat :) + + +Release v3.1 +============ + +Command-line interface changes +------------------------------ + ++ The initial command is not search in "." anymore, unless the "./" + prefix is specified or unless "." is in $PATH, as expected. + ++ The "-B" and "-Q" options are obsoleted by the new "-R" option. + This latter is equivalent to "-B -r", as there was actually no point + at using the "-B" option without "-r". + ++ A warning is now emitted when the rootfs is specified à la + chroot(1), that is, without using "-r" or "-R". + +The old command-line interface is not documented anymore, but it will +be still supported for a couple of releases. Although, users are +strongly encouraged to switch to the new one: + + ====================== ================= + old CLI new CLI + ====================== ================= + proot rootfs proot -r rootfs + proot -B rootfs proot -R rootfs + proot -B -r rootfs proot -R rootfs + proot -Q qemu rootfs proot -R rootfs -q qemu + proot -Q qemu -r rootfs proot -R rootfs -q qemu + ======================= ======================= + +Extensions +---------- + ++ The "kompat" extension ("-k" option) has been greatly enhanced. For + example, it can now make programs from Ubuntu 13.04 32-bit run on + RedHat 5 64-bit: + + rh5-64$ proot -k 3.8 -R ubuntu-13.04-32bit/ ... + ++ The "fake id0" extension ("-0" option) handles more syscalls: + mknod(2), capset(2), setxattr(2), setresuid(2), setresgid(2), + getresuid(2), and getresgid(2). + +Miscellaneous +------------- + ++ PRoot is now compiled with large file-system support (LFS), this + make it works with 64-bit file-systems (eg. CIFS) on 32-bit + platforms. + ++ The special symbolic link "/proc/self/root" now points to the guest + rootfs, that is, to the path specified by "-r" or "-R". Just like + with chroot(2), this symlink may be broken as the referenced host + path likely does not exist in the guest rootfs. Although, this + symlink is typically used to know if a process is under a chroot-ed + environment. + ++ Under QEMU, LD_LIBRARY_PATH is not clobbered anymore when a guest + program is launched by a host program. + ++ When seccomp-filter is enabled, this release is about 8% faster than + the previous one. + ++ A couple of bugs reported by Scan Coverity are fixed. + +Thanks +------ + +Special thanks to Stephan Hadamik, Jérôme Audu, and Rémi Duraffort for +their valuable help. + + +Release 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/<PID>/exe, for any <PID> monitored by PRoot; and + + * /proc/<PID>/fd/<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 <p1>:<p2>" where <p1> is the location of the mirror + within the alternate rootfs and <p2> 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. +
diff --git a/5.1.0/doc/proot/stylesheets/cli.xsl b/5.1.0/doc/proot/stylesheets/cli.xsl new file mode 100644 index 0000000..f2cf60b --- /dev/null +++ b/5.1.0/doc/proot/stylesheets/cli.xsl
@@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output method="text" /> + + <xsl:template match="/"> + <xsl:text>/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ + +#ifndef PROOT_CLI_H +#define PROOT_CLI_H + +#include "cli/cli.h" + +#ifndef VERSION +#define VERSION "</xsl:text><xsl:value-of select="//version" /><xsl:text>" +#endif +</xsl:text> + + <xsl:text> +</xsl:text> + + <xsl:apply-templates select="//option_string[.='-R']" /> + <xsl:apply-templates select="//option_string[.='-S']" /> + + <xsl:apply-templates select="//option_group" mode="handlers" /> + <xsl:text> +static int pre_initialize_bindings(Tracee *, const Cli *, size_t, char *const *, size_t); +static int post_initialize_command(Tracee *, const Cli *, size_t, char *const *, size_t); +</xsl:text> + <xsl:text> +static Cli proot_cli = { + .version = VERSION, + .name = "proot", +</xsl:text> + <xsl:apply-templates select="//subtitle"/> + <xsl:apply-templates select="//section[@names='synopsis']" /> + <xsl:apply-templates select="//section[@names='colophon']" /> + <xsl:apply-templates select="//section[@names='logo']" /> + <xsl:text> + .pre_initialize_bindings = pre_initialize_bindings, + .post_initialize_command = post_initialize_command, + + .options = { +</xsl:text> + <xsl:apply-templates select="//option_group" mode="options" /> + <xsl:text> END_OF_OPTIONS, + }, +}; + +#endif /* PROOT_CLI_H */ +</xsl:text> + </xsl:template> + + <!-- Constant string definitions --> + + <xsl:template match="subtitle"> + <xsl:text> .subtitle = "</xsl:text> + <xsl:value-of select="." /> + <xsl:text>", +</xsl:text> + </xsl:template> + + <xsl:template match="section[@names='synopsis']"> + <xsl:text> .synopsis = "</xsl:text> + <xsl:value-of select="./paragraph" /> + <xsl:text>", +</xsl:text> + </xsl:template> + + <xsl:template match="section[@names='colophon']"> + <xsl:text> .colophon = "</xsl:text> + <xsl:value-of select="./paragraph" /> + <xsl:text>", +</xsl:text> + <xsl:text> .logo = "\ +</xsl:text> + <xsl:value-of select="./literal_block" /> + <xsl:text>", +</xsl:text> + </xsl:template> + + <!-- Recommended bindings declarations --> + + <xsl:template match="option_string[.='-R']"> + <xsl:text>static const char *recommended_bindings[] = { +</xsl:text> + <xsl:apply-templates select="ancestor-or-self::option_list_item//list_item" /> + <xsl:text> NULL, +}; + +</xsl:text> + </xsl:template> + + <!-- Recommended "su" bindings declarations --> + + <xsl:template match="option_string[.='-S']"> + <xsl:text>static const char *recommended_su_bindings[] = { +</xsl:text> + <xsl:apply-templates select="ancestor-or-self::option_list_item//list_item" /> + <xsl:text> NULL, +}; + +</xsl:text> + </xsl:template> + + <xsl:template match="list_item"> + <xsl:text> "</xsl:text> + <xsl:value-of select="." /> + <xsl:text>", +</xsl:text> + </xsl:template> + + <!-- Option declarations --> + + <xsl:template match="option_group" mode="options"> + <xsl:text> { .class = "</xsl:text> + <xsl:value-of select="ancestor-or-self::section[1]/title" /> + <xsl:text>", +</xsl:text> + <xsl:text> .arguments = { +</xsl:text> + <xsl:apply-templates select="option" mode="options" /> + <xsl:text> { .name = NULL, .separator = '\0', .value = NULL } }, +</xsl:text> + <xsl:text> .handler = handle_option_</xsl:text> + <xsl:value-of select="substring(option[1]/option_string, 2, 1)" /> + <xsl:text>, +</xsl:text> + <xsl:text> .description = "</xsl:text> + <xsl:apply-templates select="../description/paragraph[1]" mode="options" /> + <xsl:text>", +</xsl:text> + <xsl:text> .detail = "</xsl:text> + <xsl:apply-templates select="../description/paragraph[position() > 1]" /> + <xsl:text>", + }, +</xsl:text> + </xsl:template> + + <xsl:template match="emphasis" mode="options"> + <xsl:text>*</xsl:text> + <xsl:value-of select="." /> + <xsl:text>*</xsl:text> + </xsl:template> + + <xsl:template match="paragraph"> + <xsl:apply-templates/> + <xsl:text> + +</xsl:text> + </xsl:template> + + <!-- Option aliases declarations --> + + <xsl:template match="option" mode="options"> + <xsl:text> { </xsl:text> + <xsl:text>.name = "</xsl:text> + <xsl:value-of select="option_string" /> + <xsl:text>", .separator = '</xsl:text> + <xsl:choose> + <xsl:when test="option_argument"> + <xsl:value-of select="option_argument/@delimiter" /> + <xsl:text>', .value = "</xsl:text> + <xsl:value-of select="option_argument" /> + <xsl:text>"</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>\0', .value = NULL</xsl:text> + </xsl:otherwise> + </xsl:choose> + <xsl:text> }, +</xsl:text> + </xsl:template> + + <!-- Handler declarations --> + + <xsl:template match="option_group" mode="handlers"> + <xsl:text>static int handle_option_</xsl:text> + <xsl:value-of select="substring(option[1]/option_string, 2, 1)" /> + <xsl:text>(Tracee *tracee, const Cli *cli, const char *value); +</xsl:text> + </xsl:template> + +</xsl:transform>
diff --git a/5.1.0/doc/proot/stylesheets/rpm-spec.xsl b/5.1.0/doc/proot/stylesheets/rpm-spec.xsl new file mode 100644 index 0000000..7015091 --- /dev/null +++ b/5.1.0/doc/proot/stylesheets/rpm-spec.xsl
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output method="text"/> + + <xsl:template match="/">%define version v<xsl:value-of select="/document/docinfo/version"/> + +Summary : <xsl:value-of select="/document/subtitle"/> +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 + +BuildRequires: libtalloc-devel + +%if 0%{?suse_version} >= 1210 || 0%{?fedora_version} >= 15 +BuildRequires: glibc-static +%endif + +%if !0%{?suse_version} != 0 +BuildRequires: which +%endif + +%description +<xsl:value-of select="/document/section[@names='description']/paragraph[1]"/> + +%prep +%setup -n proot-%{version} + +%build +make -C src + +%install +make -C src install PREFIX=%{buildroot}/%{prefix} +install -D doc/proot/man.1 %{buildroot}/%{_mandir}/man1/proot.1 + +%check +env LD_SHOW_AUXV=1 true +cat /proc/cpuinfo +./src/proot -V +./src/proot -v 1 true +make -C tests + +%clean +rm -rf %{buildroot} + +%files +%defattr(-,root,root) +%{prefix}/bin/proot +%doc %{_mandir}/man1/proot.1* +%doc COPYING +%doc doc/* + +%changelog +</xsl:template> +</xsl:transform>
diff --git a/5.1.0/doc/proot/stylesheets/website.css b/5.1.0/doc/proot/stylesheets/website.css new file mode 100644 index 0000000..63afcb8 --- /dev/null +++ b/5.1.0/doc/proot/stylesheets/website.css
@@ -0,0 +1,13 @@ +@import url("website.css"); + +h1 { + color: orange; +} + +#contents a:hover { + border-bottom: 2px solid orange; +} + +a { + border-bottom: 1px solid orange; +}
diff --git a/5.1.0/doc/proot/stylesheets/website.xsl b/5.1.0/doc/proot/stylesheets/website.xsl new file mode 100644 index 0000000..e8ecce1 --- /dev/null +++ b/5.1.0/doc/proot/stylesheets/website.xsl
@@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:import href = "../../stylesheets/website.xsl" /> + + <xsl:template match="/"> + <html xmlns="http://www.w3.org/1999/xhtml" itemscope="" itemtype="http://schema.org/Product" xml:lang="en" lang="en"> + + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title> + <xsl:value-of select="document/title" /> — <xsl:value-of select="document/subtitle" /> + </title> + <link rel="stylesheet" href="proot-website.css" type="text/css" /> + <meta itemprop="name" content="PRoot" /> + <meta itemprop="description"> + <xsl:attribute name="content"> + <xsl:value-of select="//section[@names='description']/paragraph[1]" /> + </xsl:attribute> + </meta> + <script type="text/javascript"> + var _gaq = _gaq || []; + _gaq.push(['_setAccount', 'UA-20176046-1']); + _gaq.push(['_trackPageview']); + (function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); + })(); + </script> + </head> + + <body> + + <div id="title"> + <h1>PRoot</h1> + <xsl:text> </xsl:text> + <div class="g-plusone" data-size="small"> + <xsl:comment>By default XSLTproc converts tags with no + content to self-closing tags</xsl:comment> + </div> + </div> + + <div id="contents"> + <ul> + <li><a href="#description">Description</a></li> + <li><a href="#examples">Examples</a></li> + <li><a href="#downloads">Downloads</a></li> + <li><a href="#support">Support</a></li> + <li><a href="https://plus.google.com/107605112469213359575/posts">News+</a></li> + </ul> + </div> + + <xsl:apply-templates select="//section[@names='description']" /> + <xsl:apply-templates select="//section[@names='examples']" /> + <xsl:apply-templates select="//section[@names='downloads']" /> + + <div class="section" id="support"> + <h2>Support</h2> + <p>Feel free to send your questions, bug reports, + suggestions, and patchs to <a + href="mailto:proot_me@googlegroups.com">the + mailing-list</a> or to <a + href="https://groups.google.com/forum/?fromgroups#!forum/proot_me">the + forum</a>, but please be sure that your answer isn't in + the <a + href="https://github.com/cedric-vincent/PRoot#proot">user + manual</a> first. + </p> + <p>Also, Rémi Duraffort has written interesting articles + on <a + href="http://ivoire.dinauz.org/blog/post/2012/04/16/PRoot-sorcery">how + to use a foreign Debian rootfs with PRoot</a> in order to + <a + href="http://ivoire.dinauz.org/blog/post/2012/05/04/Making-VLC-at-home">build + and test VLC on this guest Linux distribution</a>. + </p> + </div> + + <script type="text/javascript"> + (function() { + var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; + po.src = 'https://apis.google.com/js/plusone.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); + })(); + </script> + + </body> + </html> + + </xsl:template> +</xsl:transform>
diff --git a/5.1.0/doc/stylesheets/website.css b/5.1.0/doc/stylesheets/website.css new file mode 100644 index 0000000..b779b23 --- /dev/null +++ b/5.1.0/doc/stylesheets/website.css
@@ -0,0 +1,176 @@ +* { + 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 { + 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 black; +} + +a { + text-decoration: none; + border-bottom: 1px solid black; +} + +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; +} + +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; + } +} \ No newline at end of file
diff --git a/5.1.0/doc/stylesheets/website.xsl b/5.1.0/doc/stylesheets/website.xsl new file mode 100644 index 0000000..cd04d3a --- /dev/null +++ b/5.1.0/doc/stylesheets/website.xsl
@@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +--> + +<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + + <xsl:template match="section"> + <div class="section"> + <xsl:attribute name="id"><xsl:value-of select="@names" /> + </xsl:attribute> + <xsl:apply-templates/> + </div> + </xsl:template> + + <xsl:template match="section/title"> + <h2> + <xsl:apply-templates/> + </h2> + </xsl:template> + + <xsl:template match="section/section/title"> + <h3> + <xsl:apply-templates/> + </h3> + </xsl:template> + + <xsl:template match="paragraph"> + <p> + <xsl:apply-templates/> + </p> + </xsl:template> + + <xsl:template match="literal"> + <tt> + <xsl:apply-templates/> + </tt> + </xsl:template> + + <xsl:template match="emphasis"> + <em> + <xsl:apply-templates/> + </em> + </xsl:template> + + <xsl:template match="literal_block"> + <pre> + <xsl:apply-templates/> + </pre> + </xsl:template> + + <xsl:template match="strong"> + <strong> + <xsl:apply-templates/> + </strong> + </xsl:template> + + <xsl:template match="comment"> + </xsl:template> + + <xsl:template match="bullet_list"> + <ul> + <xsl:apply-templates/> + </ul> + </xsl:template> + + <xsl:template match="list_item"> + <li> + <xsl:apply-templates/> + </li> + </xsl:template> + + <xsl:template match="reference"> + <a> + <xsl:attribute name="href"><xsl:value-of select="@refuri" /> + </xsl:attribute> + <xsl:apply-templates/> + </a> + </xsl:template> + + <xsl:template match="footnote_reference"> + [<xsl:apply-templates/>] + </xsl:template> + + <xsl:template match="footnote"> + <p> + <xsl:apply-templates/> + </p> + </xsl:template> + + <xsl:template match="footnote/label"> + [<xsl:apply-templates/>] + </xsl:template> + + <xsl:template match="footnote/paragraph"> + <xsl:apply-templates/> + </xsl:template> + +</xsl:transform>
diff --git a/5.1.0/src/.check_process_vm.c b/5.1.0/src/.check_process_vm.c new file mode 100644 index 0000000..e7f6de2 --- /dev/null +++ b/5.1.0/src/.check_process_vm.c
@@ -0,0 +1,8 @@ +#include <sys/uio.h> +#include <stdlib.h> + +int main(void) +{ + return process_vm_readv(0, NULL, 0, NULL, 0, 0) + + process_vm_writev(0, NULL, 0, NULL, 0, 0); +}
diff --git a/5.1.0/src/.check_seccomp_filter.c b/5.1.0/src/.check_seccomp_filter.c new file mode 100644 index 0000000..cd79ded --- /dev/null +++ b/5.1.0/src/.check_seccomp_filter.c
@@ -0,0 +1,31 @@ +#include <sys/prctl.h> /* prctl(2), PR_* */ +#include <linux/seccomp.h> /* SECCOMP_MODE_FILTER, */ +#include <linux/filter.h> /* struct sock_*, */ +#include <linux/audit.h> /* AUDIT_ARCH_*, */ +#include <stddef.h> /* 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; +} +
diff --git a/5.1.0/src/GNUmakefile b/5.1.0/src/GNUmakefile new file mode 100644 index 0000000..aed8800 --- /dev/null +++ b/5.1.0/src/GNUmakefile
@@ -0,0 +1,242 @@ +# If you want to build outside of the source tree, use the -f option: +# make -f ${SOMEWHERE}/proot/src/GNUmakefile + +# the VPATH variable must point to the actual makefile directory +VPATH := $(dir $(lastword $(MAKEFILE_LIST))) +SRC = $(dir $(firstword $(MAKEFILE_LIST))) + +GIT = git +RM = rm +INSTALL = install +CC = $(CROSS_COMPILE)gcc +LD = $(CC) +STRIP = $(CROSS_COMPILE)strip +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump + +CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I. -I$(VPATH) +CFLAGS += -Wall -Wextra -O2 +LDFLAGS += -ltalloc + +CARE_LDFLAGS = -larchive + +OBJECTS += \ + cli/cli.o \ + cli/proot.o \ + cli/note.o \ + execve/enter.o \ + execve/exit.o \ + execve/shebang.o \ + execve/elf.o \ + execve/ldso.o \ + execve/auxv.o \ + execve/aoxp.o \ + path/binding.o \ + path/glue.o \ + path/canon.o \ + path/path.o \ + path/proc.o \ + path/temp.o \ + syscall/seccomp.o \ + syscall/syscall.o \ + syscall/chain.o \ + syscall/enter.o \ + syscall/exit.o \ + syscall/sysnum.o \ + syscall/socket.o \ + syscall/heap.o \ + tracee/tracee.o \ + tracee/mem.o \ + tracee/reg.o \ + tracee/event.o \ + ptrace/ptrace.o \ + ptrace/user.o \ + ptrace/wait.o \ + extension/extension.o \ + extension/kompat/kompat.o \ + extension/fake_id0/fake_id0.o \ + loader/loader-wrapped.o + +define define_from_arch.h +$2$1 := $(shell $(CC) $1 -E -dM -DNO_LIBC_HEADER $(SRC)/arch.h | grep -w $2 | cut -f 3 -d ' ') +endef + +$(eval $(call define_from_arch.h,,HAS_LOADER_32BIT)) + +ifdef HAS_LOADER_32BIT + OBJECTS += loader/loader-m32-wrapped.o +endif + +CARE_OBJECTS = \ + cli/care.o \ + cli/care-manual.o \ + extension/care/care.o \ + extension/care/final.o \ + extension/care/extract.o \ + extension/care/archive.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 = process_vm +CHECK_PROGRAMS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature)) +CHECK_OBJECTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).o) +CHECK_RESULTS = $(foreach feature,$(CHECK_FEATURES),.check_$(feature).res) + +.SILENT .IGNORE .INTERMEDIATE: $(CHECK_OBJECTS) $(CHECK_PROGRAMS) + +.check_%.o: .check_%.c + -$(COMPILE:echo=false) $(silently) + +.check_%: .check_%.o + -$(LINK:echo=false) $(silently) + +.check_%.res: .check_% + $(Q)if [ -e $< ]; then echo "#define HAVE_$(shell echo $* | tr a-z A-Z)" > $@; else echo "" > $@; fi + +build.h: $(CHECK_RESULTS) + $($(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)cat $^ >> $@ + $(Q)echo "#endif /* BUILD_H */" >> $@ + +BUILD_ID_NONE := $(shell if ld --build-id=none --version >/dev/null 2>&1; then echo ',--build-id=none'; fi) + +###################################################################### +# Build rules + +COMPILE = $($(quiet)CC) $(CPPFLAGS) $(CFLAGS) -MD -c $(SRC)$< -o $@ +LINK = $($(quiet)LD) -o $@ $^ $(LDFLAGS) + +OBJIFY = $($(quiet)GEN) \ + $(OBJCOPY) \ + --input binary \ + --output `env LANG=C $(OBJDUMP) -f cli/cli.o | \ + grep 'file format' | awk '{print $$4}'` \ + --binary-architecture `env LANG=C $(OBJDUMP) -f cli/cli.o | \ + grep architecture | cut -f 1 -d , | awk '{print $$2}'` \ + $< $@ + +proot: $(OBJECTS) + $(LINK) + +care: $(OBJECTS) $(CARE_OBJECTS) + $(LINK) $(CARE_LDFLAGS) + +# Special case to compute which files depend on the auto-generated +# file "build.h". +USE_BUILD_H := $(patsubst $(SRC)%.c,%.o,$(shell egrep -sl 'include[[:space:]]+"build.h"' $(patsubst %.o,$(SRC)%.c,$(OBJECTS) $(CARE_OBJECTS)))) +$(USE_BUILD_H): build.h + +%.o: %.c + @mkdir -p $(dir $@) + $(COMPILE) + +.INTERMEDIATE: manual +manual: $(VPATH)/../doc/care/manual.txt + $(Q)cp $< $@ + +cli/care-manual.o: manual cli/cli.o + $(OBJIFY) + +cli/%-licenses.o: licenses cli/cli.o + $(OBJIFY) + +###################################################################### +# Build rules for the loader + +define build_loader +LOADER$1_OBJECTS = loader/loader$1.o loader/assembly$1.o + +$(eval $(call define_from_arch.h,$1,LOADER_ARCH_CFLAGS)) +$(eval $(call define_from_arch.h,$1,LOADER_ADDRESS)) + +LOADER_CFLAGS$1 += -fPIC -ffreestanding $(LOADER_ARCH_CFLAGS$1) +LOADER_LDFLAGS$1 += -static -nostdlib -Wl$(BUILD_ID_NONE),-Ttext=$(LOADER_ADDRESS$1) + +loader/loader$1.o: loader/loader.c + @mkdir -p $$(dir $$@) + $$(COMPILE) $1 $$(LOADER_CFLAGS$1) + +loader/assembly$1.o: loader/assembly.S + @mkdir -p $$(dir $$@) + $$(COMPILE) $1 $$(LOADER_CFLAGS$1) + +loader/loader$1: $$(LOADER$1_OBJECTS) + $$($$(quiet)LD) $1 -o $$@ $$^ $$(LOADER_LDFLAGS$1) + +.INTERMEDIATE: loader$1.exe +loader$1.exe: loader/loader$1 + $$(Q)cp $$< $$@ + $$(Q)$(STRIP) $$@ + +loader/loader$1-wrapped.o: loader$1.exe cli/cli.o + $$(OBJIFY) +endef + +$(eval $(build_loader)) + +ifdef HAS_LOADER_32BIT +$(eval $(call build_loader,-m32)) +endif + +###################################################################### +# Dependencies + +.DELETE_ON_ERROR: +$(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS): $(firstword $(MAKEFILE_LIST)) + +DEPS = $(OBJECTS:.o=.d) $(CARE_OBJECTS:.o=.d) $(LOADER_OBJECTS:.o=.d) $(LOADER-m32_OBJECTS:.o=.d) $(CHECK_OBJECTS:.o=.d) +-include $(DEPS) + +###################################################################### +# PHONY targets + +PREFIX = /usr/local +DESTDIR = $(PREFIX)/bin + +.PHONY: clean distclean install install-care uninstall +clean distclean: + -$(RM) -f $(CHECK_OBJECTS) $(CHECK_PROGRAMS) $(CHECK_RESULTS) $(OBJECTS) $(CARE_OBJECTS) $(LOADER_OBJECTS) $(LOADER-m32_OBJECTS) proot care loader/loader loader/loader-m32 cli/care-manual.o $(DEPS) build.h licenses + +install: proot + $($(quiet)INSTALL) -D $< $(DESTDIR)/usr/bin/$< + +install-care: care + $($(quiet)INSTALL) -D $< $(DESTDIR)/$< + +uninstall: + -$(RM) -f $(DESTDIR)/usr/bin/proot + +uninstall-care: + -$(RM) -f $(DESTDIR)/care
diff --git a/5.1.0/src/arch.h b/5.1.0/src/arch.h new file mode 100644 index 0000000..d5a024b --- /dev/null +++ b/5.1.0/src/arch.h
@@ -0,0 +1,175 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 ARCH_H +#define ARCH_H + +#ifndef NO_LIBC_HEADER +#include <sys/ptrace.h> /* linux.git:c0a3a20b */ +#include <linux/audit.h> /* AUDIT_ARCH_*, */ +#endif + +typedef unsigned long word_t; +typedef unsigned char byte_t; + +#define SYSCALL_AVOIDER ((word_t) -2) +#define SYSTRAP_NUM SYSARG_NUM + +#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 SYSNUMS_HEADER1 "syscall/sysnums-x86_64.h" + #define SYSNUMS_HEADER2 "syscall/sysnums-i386.h" + #define SYSNUMS_HEADER3 "syscall/sysnums-x32.h" + + #define SYSNUMS_ABI1 sysnums_x86_64 + #define SYSNUMS_ABI2 sysnums_i386 + #define SYSNUMS_ABI3 sysnums_x32 + + #undef SYSTRAP_NUM + #define SYSTRAP_NUM SYSARG_RESULT + #define SYSTRAP_SIZE 2 + + #define SECCOMP_ARCHS { \ + { .value = AUDIT_ARCH_X86_64, .nb_abis = 2, .abis = { ABI_DEFAULT, ABI_3 } }, \ + { .value = AUDIT_ARCH_I386, .nb_abis = 1, .abis = { ABI_2 } }, \ + } + + #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 + + #define LOADER_ADDRESS 0x600000000000 + #define HAS_LOADER_32BIT true + + #define EXEC_PIC_ADDRESS 0x500000000000 + #define INTERP_PIC_ADDRESS 0x6f0000000000 + #define EXEC_PIC_ADDRESS_32 0x0f000000 + #define INTERP_PIC_ADDRESS_32 0xaf000000 + +#elif defined(ARCH_ARM_EABI) + + #define SYSNUMS_HEADER1 "syscall/sysnums-arm.h" + #define SYSNUMS_ABI1 sysnums_arm + + #define SYSTRAP_SIZE 4 + + #define SECCOMP_ARCHS { { .value = AUDIT_ARCH_ARM, .nb_abis = 1, .abis = { ABI_DEFAULT } } } + + #define user_regs_struct user_regs + #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 LOADER_ADDRESS 0x10000000 + + #define EXEC_PIC_ADDRESS 0x0f000000 + #define INTERP_PIC_ADDRESS 0x1f000000 + + /* The syscall number has to be valid on ARM, so use tuxcall(2) as + * the "void" syscall since it has no side effects. */ + #undef SYSCALL_AVOIDER + #define SYSCALL_AVOIDER ((word_t) 222) + +#elif defined(ARCH_ARM64) + + #define SYSNUMS_HEADER1 "syscall/sysnums-arm64.h" + #define SYSNUMS_ABI1 sysnums_arm64 + + #define SYSTRAP_SIZE 4 + + #define SECCOMP_ARCHS { } + + #define HOST_ELF_MACHINE {183, 0}; + #define RED_ZONE_SIZE 0 + #define OFFSETOF_STAT_UID_32 0 + #define OFFSETOF_STAT_GID_32 0 + + #define EXEC_PIC_ADDRESS 0x500000000000 + #define INTERP_PIC_ADDRESS 0x6f0000000000 + +#elif defined(ARCH_X86) + + #define SYSNUMS_HEADER1 "syscall/sysnums-i386.h" + #define SYSNUMS_ABI1 sysnums_i386 + + #undef SYSTRAP_NUM + #define SYSTRAP_NUM SYSARG_RESULT + #define SYSTRAP_SIZE 2 + + #define SECCOMP_ARCHS { { .value = AUDIT_ARCH_I386, .nb_abis = 1, .abis = { ABI_DEFAULT } } } + + #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 LOADER_ADDRESS 0xa0000000 + #define LOADER_ARCH_CFLAGS -mregparm=3 + + #define EXEC_PIC_ADDRESS 0x0f000000 + #define INTERP_PIC_ADDRESS 0xaf000000 + +#elif defined(ARCH_SH4) + + #define SYSNUMS_HEADER1 "syscall/sysnums-sh4.h" + #define SYSNUMS_ABI1 sysnums_sh4 + + #define SYSTRAP_SIZE 2 + + #define SECCOMP_ARCHS { } + + #define user_regs_struct pt_regs + #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 */
diff --git a/5.1.0/src/attribute.h b/5.1.0/src/attribute.h new file mode 100644 index 0000000..934888b --- /dev/null +++ b/5.1.0/src/attribute.h
@@ -0,0 +1,32 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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))) +#define DONT_INSTRUMENT __attribute__((no_instrument_function)) +#define PACKED __attribute__((packed)) +#define WEAK __attribute__((weak)) + +#endif /* ATTRIBUTE_H */
diff --git a/5.1.0/src/cli/care.c b/5.1.0/src/cli/care.c new file mode 100644 index 0000000..8ece4bc --- /dev/null +++ b/5.1.0/src/cli/care.c
@@ -0,0 +1,374 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of CARE. + * + * Copyright (C) 2014 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 <string.h> /* str*(3), */ +#include <assert.h> /* assert(3), */ +#include <strings.h> /* bzero(3), */ +#include <sys/queue.h> /* STAILQ_*, */ +#include <stdint.h> /* INT_MIN, */ +#include <unistd.h> /* getpid(2), close(2), */ +#include <stdio.h> /* printf(3), fflush(3), */ +#include <unistd.h> /* getcwd(2), */ +#include <errno.h> /* errno(3), */ + +#include "cli/cli.h" +#include "cli/note.h" +#include "path/binding.h" +#include "path/temp.h" +#include "extension/extension.h" +#include "extension/care/care.h" +#include "extension/care/extract.h" +#include "attribute.h" + +/* These should be included last. */ +#include "build.h" +#include "cli/care.h" + +static int handle_option_o(Tracee *tracee UNUSED, const Cli *cli, const char *value) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + options->output = value; + return 0; +} + +static int handle_option_c(Tracee *tracee UNUSED, const Cli *cli, const char *value) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + Item *item = queue_item(options, &options->concealed_paths, value); + return (item != NULL ? 0 : -1); +} + +static int handle_option_r(Tracee *tracee UNUSED, const Cli *cli, const char *value) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + Item *item = queue_item(options, &options->revealed_paths, value); + return (item != NULL ? 0 : -1); +} + +static int handle_option_p(Tracee *tracee UNUSED, const Cli *cli, const char *value) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + Item *item = queue_item(options, &options->volatile_paths, value); + return (item != NULL ? 0 : -1); +} + +static int handle_option_e(Tracee *tracee UNUSED, const Cli *cli, const char *value) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + Item *item = queue_item(options, &options->volatile_envars, value); + return (item != NULL ? 0 : -1); +} + +static int handle_option_m(Tracee *tracee, const Cli *cli, const char *value) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + return parse_integer_option(tracee, &options->max_size, value, "-m"); +} + +static int handle_option_d(Tracee *tracee UNUSED, const Cli *cli, const char *value UNUSED) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + options->ignore_default_config = true; + return 0; +} + +static int handle_option_v(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + int status; + + status = parse_integer_option(tracee, &tracee->verbose, value, "-v"); + if (status < 0) + return status; + + global_verbose_level = tracee->verbose; + return 0; +} + +extern unsigned char WEAK _binary_licenses_start; +extern unsigned char WEAK _binary_licenses_end; + +static int handle_option_V(Tracee *tracee UNUSED, const Cli *cli, const char *value UNUSED) +{ + size_t size; + + print_version(cli); + + printf("suitable for self-extracting archives (.bin): %s\n", +#if defined(CARE_BINARY_IS_PORTABLE) + "yes" +#else + "no" +#endif + ); + + printf("\n%s\n", cli->colophon); + fflush(stdout); + + size = &_binary_licenses_end - &_binary_licenses_start; + if (size > 0) + write(1, &_binary_licenses_start, size); + + exit_failure = false; + return -1; +} + +static int handle_option_x(Tracee *tracee UNUSED, const Cli *cli UNUSED, const char *value) +{ + int status = extract_archive_from_file(value); + exit_failure = (status < 0); + return -1; +} + +extern unsigned char WEAK _binary_manual_start; +extern unsigned char WEAK _binary_manual_end; + +static int handle_option_h(Tracee *tracee UNUSED, const Cli *cli UNUSED, const char *value UNUSED) +{ + size_t size; + + size = &_binary_manual_end - &_binary_manual_start; + if (size != 0) + write(1, &_binary_manual_start, size); + else + printf("No manual found, please visit http://reproducible.io instead.\n"); + + exit_failure = false; + return -1; +} + +/** + * Allocate a new binding for the given @tracee that will conceal the + * content of @path with an empty file or directory. This function + * complains about missing @host path only if @must_exist is true. + */ +static Binding *new_concealing_binding(Tracee *tracee, const char *path, bool must_exist) +{ + struct stat statl; + Binding *binding; + const char *temp; + int status; + + status = stat(path, &statl); + if (status < 0) { + if (must_exist) + note(tracee, WARNING, SYSTEM, "can't conceal %s", path); + return NULL; + } + + if (S_ISDIR(statl.st_mode)) + temp = create_temp_directory(NULL, tracee->tool_name); + else + temp = create_temp_file(NULL, tracee->tool_name); + if (temp == NULL) { + note(tracee, WARNING, INTERNAL, "can't conceal %s", path); + return NULL; + } + + binding = new_binding(tracee, temp, path, must_exist); + if (binding == NULL) + return NULL; + + return binding; +} + +/** + * Initialize @tracee's fields that are mandatory for PRoot/CARE but + * that are not specifiable on the command line. + */ +static int pre_initialize_bindings(Tracee *tracee, const Cli *cli, + size_t argc, char *const argv[], size_t cursor) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + char path[PATH_MAX]; + Binding *binding; + const char *home; + const char *pwd; + Item *item; + size_t i; + + if (cursor >= argc) { + note(tracee, ERROR, USER, "no command specified"); + return -1; + } + options->command = &argv[cursor]; + + home = getenv("HOME"); + pwd = getenv("PWD"); + + /* Set these variables to their default values (ie. when not + * set), this simplifies the binding setup to the related + * files. */ + setenv("XAUTHORITY", talloc_asprintf(tracee->ctx, "%s/.Xauthority", home), 0); + setenv("ICEAUTHORITY", talloc_asprintf(tracee->ctx, "%s/.ICEauthority", home), 0); + + /* Enable default option first. */ + if (!options->ignore_default_config) { + const char *expanded; + Binding *binding; + int status; + + /* Bind an empty file/directory over default concealed + * paths. */ + for (i = 0; default_concealed_paths[i] != NULL; i++) { + expanded = expand_front_variable(tracee->ctx, default_concealed_paths[i]); + binding = new_concealing_binding(tracee, expanded, false); + if (binding != NULL) + VERBOSE(tracee, 0, "concealed path: %s %s", + default_concealed_paths[i], + default_concealed_paths[i] != expanded ? expanded : ""); + } + + /* Bind default revealed paths over concealed + * paths. */ + for (i = 0; default_revealed_paths[i] != NULL; i++) { + expanded = expand_front_variable(tracee->ctx, default_revealed_paths[i]); + binding = new_binding(tracee, expanded, NULL, false); + if (binding != NULL) + VERBOSE(tracee, 0, "revealed path: %s %s", + default_revealed_paths[i], + default_revealed_paths[i] != expanded ? expanded : ""); + } + + /* Ensure the initial command is accessible. */ + status = which(NULL, NULL, path, options->command[0]); + if (status < 0) + return -1; /* This failure was already noticed by which(). */ + + binding = new_binding(tracee, path, NULL, false); + if (binding != NULL) + VERBOSE(tracee, 0, "revealed path: %s", path); + + /* Sanity check. Note: it is assumed $HOME and $PWD + * are canonicalized. */ + if (home != NULL && pwd != NULL && strcmp(home, pwd) == 0) + note(tracee, WARNING, USER, + "$HOME is implicitely revealed since it is the same as $PWD, " + "change your current working directory to be sure " + "your personal data will be not archivable."); + + /* Add the default volatile paths to the list of user + * volatile paths. */ + for (i = 0; default_volatile_paths[i] != NULL; i++) { + Item *item; + + expanded = expand_front_variable(tracee->ctx, default_volatile_paths[i]); + item = queue_item(tracee, &options->volatile_paths, expanded); + + /* Remember the non expanded form, later used + * by archive_re_execute_sh(). */ + if (item != NULL && expanded != default_volatile_paths[i]) + talloc_set_name_const(item, default_volatile_paths[i]); + } + + for (i = 0; default_volatile_envars[i] != NULL; i++) + queue_item(tracee, &options->volatile_envars, default_volatile_envars[i]); + + if (options->max_size == INT_MIN) + options->max_size = CARE_MAX_SIZE; + } + else + if (options->max_size == INT_MIN) + options->max_size = -1; /* Unlimited. */ + + VERBOSE(tracee, 1, "max size: %d", options->max_size); + + /* Bind an empty file/directory over user concealed paths. */ + if (options->concealed_paths != NULL) { + STAILQ_FOREACH(item, options->concealed_paths, link) { + binding = new_concealing_binding(tracee, item->load, true); + if (binding != NULL) + VERBOSE(tracee, 0, "concealed path: %s", (char *) item->load); + } + } + + /* Bind user revealed paths over concealed paths. */ + if (options->revealed_paths != NULL) { + STAILQ_FOREACH(item, options->revealed_paths, link) { + binding = new_binding(tracee, item->load, NULL, true); + if (binding != NULL) + VERBOSE(tracee, 0, "revealed path: %s", (char *) item->load); + } + } + + /* Bind volatile paths over concealed paths. */ + if (options->volatile_paths != NULL) { + STAILQ_FOREACH(item, options->volatile_paths, link) { + binding = new_binding(tracee, item->load, NULL, false); + if (binding != NULL) + VERBOSE(tracee, 1, "volatile path: %s", (char *) item->load); + } + } + + VERBOSE(tracee, 0, "----------------------------------------------------------------------"); + + /* Initialize @tracee->fs->cwd with a path already canonicalized + * as required by care.c:handle_initialization(). */ + if (getcwd(path, PATH_MAX) == NULL) { + note(tracee, ERROR, SYSTEM, "can't get current working directory"); + return -1; + } + + tracee->fs->cwd = talloc_strdup(tracee->fs, path); + if (tracee->fs->cwd == NULL) + return -1; + talloc_set_name_const(tracee->fs->cwd, "$cwd"); + + /* Initialize @tracee's root (required by PRoot). */ + binding = new_binding(tracee, "/", "/", true); + if (binding == NULL) + return -1; + + return cursor; +} + +/** + * Initialize CARE extensions. + */ +static int post_initialize_bindings(Tracee *tracee, const Cli *cli, + size_t argc UNUSED, char *const argv[] UNUSED, size_t cursor) +{ + Options *options = talloc_get_type_abort(cli->private, Options); + int status; + + status = initialize_extension(tracee, care_callback, (void *) options); + if (status < 0) { + note(tracee, WARNING, INTERNAL, "can't initialize the care extension"); + return -1; + } + + return cursor; +} + +const Cli *get_care_cli(TALLOC_CTX *context) +{ + Options *options; + + global_tool_name = care_cli.name; + + options = talloc_zero(context, Options); + if (options == NULL) + return NULL; + options->max_size = INT_MIN; + + care_cli.private = options; + return &care_cli; +}
diff --git a/5.1.0/src/cli/care.h b/5.1.0/src/cli/care.h new file mode 100644 index 0000000..6d5b017 --- /dev/null +++ b/5.1.0/src/cli/care.h
@@ -0,0 +1,192 @@ +/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ + +#ifndef CARE_CLI_H +#define CARE_CLI_H + +#include "cli/cli.h" + +#ifndef VERSION +#define VERSION "2.2" +#endif + +#define CARE_MAX_SIZE 1024 + +static char const *default_concealed_paths[] = { + "$HOME", + "/tmp", + NULL, +}; + +static char const *default_revealed_paths[] = { + "$PWD", + NULL, +}; + +static char const *default_volatile_paths[] = { + "/dev", + "/proc", + "/sys", + "/run/shm", + "/tmp/.X11-unix", + "/tmp/.ICE-unix", + "$XAUTHORITY", + "$ICEAUTHORITY", + "/var/run/dbus/system_bus_socket", + "/var/tmp/kdecache-$LOGNAME", + NULL, +}; + +static char const *default_volatile_envars[] = { + "DISPLAY", + "http_proxy", + "https_proxy", + "ftp_proxy", + "all_proxy", + "HTTP_PROXY", + "HTTPS_PROXY", + "FTP_PROXY", + "ALL_PROXY", + "DBUS_SESSION_BUS_ADDRESS", + "SESSION_MANAGER", + "XDG_SESSION_COOKIE", + NULL, +}; + +static int handle_option_o(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_c(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_r(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_p(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_e(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_m(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_d(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_v(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_V(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_x(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value); + +static int pre_initialize_bindings(Tracee *, const Cli *, size_t, char *const *, size_t); +static int post_initialize_bindings(Tracee *, const Cli *, size_t, char *const *, size_t); + +static Cli care_cli = { + .version = VERSION, + .name = "care", + .subtitle = "Comprehensive Archiver for Reproducible Execution", + .synopsis = "care [option] ... command", + .colophon = "Visit http://reproducible.io for help, bug reports, suggestions, patches, ...\n\ +Copyright (C) 2014 STMicroelectronics, licensed under GPL v2 or later.", + .logo = "\ + _____ ____ _____ ____\n\ + / __/ __ | __ \\ __|\n\ +/ /_/ | / __|\n\ +\\_____|__|__|__|__\\____|", + + .pre_initialize_bindings = pre_initialize_bindings, + .post_initialize_bindings = post_initialize_bindings, + + .options = { + { .class = "Options", + .arguments = { + { .name = "-o", .separator = ' ', .value = "path" }, + { .name = "--output", .separator = '=', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_o, + .description = "Archive in *path*, its suffix specifies the format.", + .detail = NULL, + }, + { .class = "Options", + .arguments = { + { .name = "-c", .separator = ' ', .value = "path" }, + { .name = "--concealed-path", .separator = '=', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_c, + .description = "Make *path* content appear empty during the original execution.", + .detail = NULL, + }, + { .class = "Options", + .arguments = { + { .name = "-r", .separator = ' ', .value = "path" }, + { .name = "--revealed-path", .separator = '=', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_r, + .description = "Make *path* content accessible when nested in a concealed path.", + .detail = NULL, + }, + { .class = "Options", + .arguments = { + { .name = "-p", .separator = ' ', .value = "path" }, + { .name = "--volatile-path", .separator = '=', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_p, + .description = "Don't archive *path* content, reuse actual *path* instead.", + .detail = NULL, + }, + { .class = "Options", + .arguments = { + { .name = "-e", .separator = ' ', .value = "name" }, + { .name = "--volatile-env", .separator = '=', .value = "name" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_e, + .description = "Don't archive *name* env. variable, reuse actual value instead.", + .detail = NULL, + }, + { .class = "Options", + .arguments = { + { .name = "-m", .separator = ' ', .value = "value" }, + { .name = "--max-archivable-size", .separator = '=', .value = "value" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_m, + .description = "Set the maximum size of archivable files to *value* megabytes.", + .detail = NULL, + }, + { .class = "Options", + .arguments = { + { .name = "-d", .separator = '\0', .value = NULL }, + { .name = "--ignore-default-config", .separator = '\0', .value = NULL }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_d, + .description = "Don't use the default options.", + .detail = NULL, + }, + { .class = "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 = NULL, + }, + { .class = "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 = NULL, + }, + { .class = "Options", + .arguments = { + { .name = "-x", .separator = ' ', .value = "file" }, + { .name = "--extract", .separator = '=', .value = "file" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_x, + .description = "Extract content of the archive *file*, then exit.", + .detail = NULL, + }, + { .class = "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 user manual, then exit.", + .detail = NULL, + }, + END_OF_OPTIONS, + }, +}; + +#endif /* CARE_CLI_H */
diff --git a/5.1.0/src/cli/cli.c b/5.1.0/src/cli/cli.c new file mode 100644 index 0000000..832a42c --- /dev/null +++ b/5.1.0/src/cli/cli.c
@@ -0,0 +1,580 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdio.h> /* printf(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <linux/limits.h> /* ARG_MAX, PATH_MAX, */ +#include <string.h> /* str*(3), basename(3), */ +#include <talloc.h> /* talloc*, */ +#include <stdlib.h> /* exit(3), EXIT_*, strtol(3), {g,s}etenv(3), */ +#include <assert.h> /* assert(3), */ +#include <sys/types.h> /* getpid(2), */ +#include <unistd.h> /* getpid(2), */ +#include <errno.h> /* errno(3), */ +#include <execinfo.h> /* backtrace_symbols(3), */ +#include <limits.h> /* INT_MAX, */ + +#include "cli/cli.h" +#include "cli/note.h" +#include "extension/care/extract.h" +#include "extension/extension.h" +#include "tracee/tracee.h" +#include "tracee/event.h" +#include "path/binding.h" +#include "path/canon.h" +#include "path/path.h" + +#include "build.h" + +/** + * Print a (@detailed) usage of PRoot. + */ +void print_usage(Tracee *tracee, const Cli *cli, bool detailed) +{ + const char *current_class = "none"; + const Option *options; + size_t i, j; + +#define DETAIL(a) if (detailed) a + + DETAIL(printf("%s %s: %s.\n\n", cli->name, cli->version, cli->subtitle)); + printf("Usage:\n %s\n", cli->synopsis); + DETAIL(printf("\n")); + + options = cli->options; + for (i = 0; options[i].class != NULL; i++) { + for (j = 0; ; j++) { + const 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", cli->colophon); +} + +/** + * Print the version of PRoot. + */ +void print_version(const Cli *cli) +{ + printf("%s %s\n\n", cli->logo, cli->version); + printf("built-in accelerators: process_vm = %s, seccomp_filter = %s\n", +#if defined(HAVE_PROCESS_VM) + "yes", +#else + "no", +#endif +#if defined(HAVE_SECCOMP_FILTER) + "yes" +#else + "no" +#endif + ); +} + +static void print_execve_help(const Tracee *tracee, const char *argv0, int status) +{ + note(tracee, ERROR, SYSTEM, "execve(\"%s\")", argv0); + + /* Ubuntu kernel bug? */ + if (status == -EPERM && getenv("PROOT_NO_SECCOMP") == NULL) { + note(tracee, INFO, USER, +"It seems your kernel contains this bug: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1202161\n" +"To workaround it, set the env. variable PROOT_NO_SECCOMP to 1."); + return; + } + + note(tracee, INFO, USER, "possible causes:\n" +" * the program is a script but its interpreter (eg. /bin/sh) was not found;\n" +" * the program is an ELF but its interpreter (eg. ld-linux.so) was not found;\n" +" * the program is a foreign binary but qemu was not specified;\n" +" * qemu does not work correctly (if specified);\n" +" * the loader was not found or doesn't work."); +} + +static void print_error_separator(const Tracee *tracee, const Argument *argument) +{ + if (argument->separator == '\0') + note(tracee, ERROR, USER, "option '%s' expects no value.", argument->name); + else + note(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 *const argv[]) +{ + char string[ARG_MAX] = ""; + size_t i; + + if (!argv) + return; + +#define APPEND(post) \ + do { \ + ssize_t length = sizeof(string) - (strlen(string) + strlen(post)); \ + if (length <= 0) \ + return; \ + strncat(string, post, length); \ + } while (0) + + APPEND(prompt); + APPEND(" ="); + for (i = 0; argv[i] != NULL; i++) { + APPEND(" "); + APPEND(argv[i]); + } + string[sizeof(string) - 1] = '\0'; + +#undef APPEND + + note(tracee, INFO, USER, "%s", string); +} + +static void print_config(Tracee *tracee, char *const argv[]) +{ + assert(tracee != NULL); + + if (tracee->verbose <= 0) + return; + + if (tracee->qemu) + note(tracee, INFO, USER, "host rootfs = %s", HOST_ROOTFS); + + if (tracee->glue) + note(tracee, INFO, USER, "glue rootfs = %s", tracee->glue); + + note(tracee, INFO, USER, "exe = %s", tracee->exe); + print_argv(tracee, "argv", argv); + print_argv(tracee, "qemu", tracee->qemu); + note(tracee, INFO, USER, "initial cwd = %s", tracee->fs->cwd); + note(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; + + /* Compute the base directory. */ + if (tracee->fs->cwd[0] != '/') { + status = getcwd2(tracee->reconf.tracee, path); + if (status < 0) { + note(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) { + note(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) { + note(tracee, WARNING, USER, "can't chdir(\"%s\") in the guest rootfs: %s", + path2, strerror(-status)); + note(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"); + + /* Keep this special environment variable consistent. */ + setenv("PWD", path, 1); + + return 0; +} + +/** + * Initialize @tracee->exe from @exe, i.e. canonicalize it from a + * guest point-of-view. + */ +static int initialize_exe(Tracee *tracee, const char *exe) +{ + char path[PATH_MAX]; + int status; + + status = which(tracee, tracee->reconf.paths, path, exe ?: "/bin/sh"); + 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"); + + return 0; +} + +/** + * Configure @tracee according to the command-line arguments stored in + * @argv[]. This function returns the index in @argv[] of the command + * to launch, otherwise -1 if an error occured. + */ +static int parse_config(Tracee *tracee, size_t argc, char *const argv[]) +{ + option_handler_t handler = NULL; + const Option *options; + const Cli *cli = NULL; + size_t argc_offset; + size_t i, j, k; + int status; + + if (get_care_cli != NULL) { + /* Check if it's an self-extracting CARE archive. */ + status = extract_archive_from_file("/proc/self/exe"); + if (status == 0) { + /* Yes it is, nothing more to do. */ + exit_failure = 0; + return -1; + } + + /* Check if it's a valid CARE tool name. */ + if (strncasecmp(basename(argv[0]), "care", strlen("care")) == 0) + cli = get_care_cli(tracee->ctx); + } + + /* Unknown tool name? Default to PRoot. */ + if (cli == NULL) + cli = get_proot_cli(tracee->ctx); + tracee->tool_name = cli->name; + + if (argc == 1) { + print_usage(tracee, cli, false); + return -1; + } + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + /* The current argument is the value of a short option. */ + if (handler != NULL) { + status = handler(tracee, cli, arg); + if (status < 0) + return -1; + handler = NULL; + continue; + } + + if (arg[0] != '-') + break; /* End of PRoot options. */ + + options = cli->options; + for (j = 0; options[j].class != NULL; j++) { + const Option *option = &options[j]; + + /* A given option has several aliases. */ + for (k = 0; ; k++) { + const 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, cli, NULL); + 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, cli, &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; + } + } + + note(tracee, ERROR, USER, "unknown option '%s'.", arg); + return -1; + + known_option: + if (handler != NULL && i == argc - 1) { + note(tracee, ERROR, USER, "missing value for option '%s'.", arg); + return -1; + } + } + argc_offset = i; + +#define HOOK_CONFIG(callback) \ + do { \ + if (cli->callback != NULL) { \ + status = cli->callback(tracee, cli, argc, argv, i); \ + if (status < 0) \ + return -1; \ + i = status; \ + } \ + } while (0) + + HOOK_CONFIG(pre_initialize_bindings); + + /* 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; + + HOOK_CONFIG(post_initialize_bindings); + HOOK_CONFIG(pre_initialize_cwd); + + /* 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; + + HOOK_CONFIG(post_initialize_cwd); + HOOK_CONFIG(pre_initialize_exe); + + /* Bindings are now installed and the current working + * directory is canonicalized: resolve path to @tracee->exe + * and configure @tracee->cmdline. */ + status = initialize_exe(tracee, argv[argc_offset]); + if (status < 0) + return -1; + + HOOK_CONFIG(post_initialize_exe); +#undef HOOK_CONFIG + + print_config(tracee, &argv[argc_offset]); + + return argc_offset; +} + +bool exit_failure = true; + +int main(int argc, char *const 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, &argv[status]); + if (status < 0) { + print_execve_help(tracee, tracee->exe, status); + goto error; + } + + /* Start tracing the first tracee and all its children. */ + exit(event_loop()); + +error: + TALLOC_FREE(tracee); + + if (exit_failure) { + fprintf(stderr, "fatal error: see `%s --help`.\n", basename(argv[0])); + exit(EXIT_FAILURE); + } + else + exit(EXIT_SUCCESS); +} + +/** + * Convert @value into an integer, then put the result into + * *@variable. This function prints a warning and returns -1 if a + * conversion error occured, otherwise it returns 0. + */ +int parse_integer_option(const Tracee *tracee, int *variable, const char *value, const char *option) +{ + char *end_ptr = NULL; + + errno = 0; + *variable = strtol(value, &end_ptr, 10); + if (errno != 0 || end_ptr == value) { + note(tracee, ERROR, USER, "option `%s` expects an integer value.", option); + return -1; + } + + return 0; +} + +/** + * Expand the environment variable in front of @string, if any. For + * example, this function can expand "$HOME" or "$HOME/.ICEauthority". + */ +const char *expand_front_variable(TALLOC_CTX *context, const char *string) +{ + const char *suffix; + char *expanded; + ptrdiff_t size; + + if (string[0] != '$') + return string; + + suffix = strchr(string, '/'); + if (suffix == NULL) + return (getenv(&string[1]) ?: string); + + size = suffix - string; + if (size <= 1) + return string; + + expanded = talloc_strndup(context, &string[1], size - 1); + if (expanded == NULL) + return string; + + expanded = getenv(expanded); + if (expanded == NULL) + return string; + + expanded = talloc_asprintf(context, "%s%s", expanded, suffix); + if (expanded == NULL) + return string; + + return expanded; +} + +/* Here follows the support for GCC function instrumentation. Build + * with CFLAGS='-finstrument-functions -O0 -g' and LDFLAGS='-rdynamic' + * to enable this mechanism. */ + +static int indent_level = 0; + +void __cyg_profile_func_enter(void *this_function, void *call_site) DONT_INSTRUMENT; +void __cyg_profile_func_enter(void *this_function, void *call_site) +{ + void *const pointers[] = { this_function, call_site }; + char **symbols = NULL; + + symbols = backtrace_symbols(pointers, 2); + if (symbols == NULL) + goto end; + + fprintf(stderr, "%*s from %s\n", (int) strlen(symbols[0]) + indent_level, symbols[0], symbols[1]); + +end: + if (symbols != NULL) + free(symbols); + + if (indent_level < INT_MAX) + indent_level++; +} + +void __cyg_profile_func_exit(void *this_function UNUSED, void *call_site UNUSED) DONT_INSTRUMENT; +void __cyg_profile_func_exit(void *this_function UNUSED, void *call_site UNUSED) +{ + if (indent_level > 0) + indent_level--; +}
diff --git a/5.1.0/src/cli/cli.h b/5.1.0/src/cli/cli.h new file mode 100644 index 0000000..67bd92b --- /dev/null +++ b/5.1.0/src/cli/cli.h
@@ -0,0 +1,65 @@ +/* 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 "attribute.h" + +typedef struct { + const char *name; + char separator; + const char *value; +} Argument; + +struct Cli; +typedef int (*option_handler_t)(Tracee *tracee, const struct Cli *cli, const char *value); + +typedef struct { + const char *class; + option_handler_t handler; + const char *description; + const char *detail; + Argument arguments[5]; +} Option; + +#define END_OF_OPTIONS { .class = NULL, \ + .arguments = {{ .name = NULL, .separator = '\0', .value = NULL }}, \ + .handler = NULL, \ + .description = NULL, \ + .detail = NULL \ + } + +typedef int (*initialization_hook_t)(Tracee *tracee, const struct Cli *cli, + size_t argc, char *const argv[], size_t cursor); +typedef struct Cli { + const char *name; + const char *version; + const char *subtitle; + const char *synopsis; + const char *colophon; + const char *logo; + + initialization_hook_t pre_initialize_bindings; + initialization_hook_t post_initialize_bindings; + initialization_hook_t pre_initialize_cwd; + initialization_hook_t post_initialize_cwd; + initialization_hook_t pre_initialize_exe; + initialization_hook_t post_initialize_exe; + void *private; + + const Option options[]; +} Cli; + +extern const Cli *get_proot_cli(TALLOC_CTX *context); +extern const Cli * WEAK get_care_cli(TALLOC_CTX *context); + +extern void print_usage(Tracee *tracee, const Cli *cli, bool detailed); +extern void print_version(const Cli *cli); +extern int parse_integer_option(const Tracee *tracee, int *variable, const char *value, const char *option); +extern const char *expand_front_variable(TALLOC_CTX *context, const char *string); + +extern bool exit_failure; + +#endif /* CLI_H */
diff --git a/5.1.0/src/cli/note.c b/5.1.0/src/cli/note.c new file mode 100644 index 0000000..83f8865 --- /dev/null +++ b/5.1.0/src/cli/note.c
@@ -0,0 +1,97 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* errno, */ +#include <string.h> /* strerror(3), */ +#include <stdarg.h> /* va_*, */ +#include <stdio.h> /* vfprintf(3), */ +#include <limits.h> /* INT_MAX, */ + +#include "cli/note.h" +#include "tracee/tracee.h" + +int global_verbose_level; +const char *global_tool_name; + +/** + * Print @message to the standard error stream according to its + * @severity and @origin. + */ +void note(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...) +{ + const char *tool_name; + va_list extra_params; + int verbose_level; + + if (tracee == NULL) { + verbose_level = global_verbose_level; + tool_name = global_tool_name ?: ""; + } + else { + verbose_level = tracee->verbose; + tool_name = tracee->tool_name; + } + + if (verbose_level < 0 && severity != ERROR) + return; + + switch (severity) { + case WARNING: + fprintf(stderr, "%s warning: ", tool_name); + break; + + case ERROR: + fprintf(stderr, "%s error: ", tool_name); + break; + + case INFO: + default: + fprintf(stderr, "%s info: ", tool_name); + 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; +} +
diff --git a/5.1.0/src/cli/note.h b/5.1.0/src/cli/note.h new file mode 100644 index 0000000..4993872 --- /dev/null +++ b/5.1.0/src/cli/note.h
@@ -0,0 +1,54 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 NOTE_H +#define NOTE_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)) \ + note(tracee, INFO, INTERNAL, (message), ## args); \ + } while (0) + +extern void note(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...) FORMAT(printf, 4, 5); + +extern int global_verbose_level; +extern const char *global_tool_name; + +#endif /* NOTE_H */
diff --git a/5.1.0/src/cli/proot.c b/5.1.0/src/cli/proot.c new file mode 100644 index 0000000..e46d87e --- /dev/null +++ b/5.1.0/src/cli/proot.c
@@ -0,0 +1,324 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <string.h> /* str*(3), */ +#include <assert.h> /* assert(3), */ +#include <stdio.h> /* printf(3), fflush(3), */ +#include <unistd.h> /* write(2), */ + +#include "cli/cli.h" +#include "cli/note.h" +#include "extension/extension.h" +#include "path/binding.h" +#include "attribute.h" + +/* These should be included last. */ +#include "build.h" +#include "cli/proot.h" + +static int handle_option_r(Tracee *tracee, const Cli *cli UNUSED, const 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, const Cli *cli UNUSED, const char *value) +{ + char *host; + char *guest; + + host = talloc_strdup(tracee->ctx, value); + if (host == NULL) { + note(tracee, ERROR, INTERNAL, "can't allocate memory"); + return -1; + } + + guest = strchr(host, ':'); + if (guest != NULL) { + *guest = '\0'; + guest++; + } + + new_binding(tracee, host, guest, true); + return 0; +} + +static int handle_option_q(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + const char *ptr; + size_t nb_args; + bool last; + 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; + do { + const void *start; + const void *end; + last = true; + + /* Keep consecutive non-space characters. */ + start = ptr; + while (*ptr != ' ' && *ptr != '\0') + ptr++; + end = ptr; + + /* End-of-string ? */ + if (*ptr == '\0') + goto next; + + /* Remove consecutive space separators. */ + while (*ptr == ' ' && *ptr != '\0') + ptr++; + + /* End-of-string ? */ + if (*ptr == '\0') + goto next; + + last = false; + next: + tracee->qemu[i] = talloc_strndup(tracee->qemu, start, end - start); + if (tracee->qemu[i] == NULL) + return -1; + i++; + } while (!last); + 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, const Cli *cli UNUSED, const 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, const Cli *cli UNUSED, const char *value) +{ + int status; + + status = initialize_extension(tracee, kompat_callback, value); + if (status < 0) + note(tracee, WARNING, INTERNAL, "option \"-k %s\" discarded", value); + + return 0; +} + +static int handle_option_i(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + (void) initialize_extension(tracee, fake_id0_callback, value); + return 0; +} + +static int handle_option_0(Tracee *tracee, const Cli *cli, const char *value UNUSED) +{ + return handle_option_i(tracee, cli, "0:0"); +} + +static int handle_option_v(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + int status; + + status = parse_integer_option(tracee, &tracee->verbose, value, "-v"); + if (status < 0) + return status; + + global_verbose_level = tracee->verbose; + return 0; +} + +extern unsigned char WEAK _binary_licenses_start; +extern unsigned char WEAK _binary_licenses_end; + +static int handle_option_V(Tracee *tracee UNUSED, const Cli *cli, const char *value UNUSED) +{ + size_t size; + + print_version(cli); + printf("\n%s\n", cli->colophon); + fflush(stdout); + + size = &_binary_licenses_end - &_binary_licenses_start; + if (size > 0) + write(1, &_binary_licenses_start, size); + + exit_failure = false; + return -1; +} + +static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value UNUSED) +{ + print_usage(tracee, cli, true); + exit_failure = false; + return -1; +} + +static void new_bindings(Tracee *tracee, const char *bindings[], const char *value) +{ + int i; + + for (i = 0; bindings[i] != NULL; i++) { + const char *path; + + path = (strcmp(bindings[i], "*path*") != 0 + ? expand_front_variable(tracee->ctx, bindings[i]) + : value); + + new_binding(tracee, path, NULL, false); + } +} + +static int handle_option_R(Tracee *tracee, const Cli *cli, const char *value) +{ + int status; + + status = handle_option_r(tracee, cli, value); + if (status < 0) + return status; + + new_bindings(tracee, recommended_bindings, value); + + return 0; +} + +static int handle_option_S(Tracee *tracee, const Cli *cli, const char *value) +{ + int status; + + status = handle_option_0(tracee, cli, value); + if (status < 0) + return status; + + status = handle_option_r(tracee, cli, value); + if (status < 0) + return status; + + new_bindings(tracee, recommended_su_bindings, value); + + return 0; +} + +/** + * Initialize @tracee->qemu. + */ +static int post_initialize_exe(Tracee *tracee, const Cli *cli UNUSED, + size_t argc UNUSED, char *const argv[] UNUSED, size_t cursor UNUSED) +{ + char path[PATH_MAX]; + int status; + + /* 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; + + return 0; +} + +/** + * Initialize @tracee's fields that are mandatory for PRoot but that + * are not required on the command line, i.e. "-w" and "-r". + */ +static int pre_initialize_bindings(Tracee *tracee, const Cli *cli, + size_t argc UNUSED, char *const argv[] UNUSED, size_t cursor) +{ + int status; + + /* Default to "." if no CWD were specified. */ + if (tracee->fs->cwd == NULL) { + status = handle_option_w(tracee, cli, "."); + if (status < 0) + return -1; + } + + /* The default guest rootfs is "/" if none was specified. */ + if (get_root(tracee) == NULL) { + status = handle_option_r(tracee, cli, "/"); + if (status < 0) + return -1; + } + + return cursor; +} + +const Cli *get_proot_cli(TALLOC_CTX *context UNUSED) +{ + global_tool_name = proot_cli.name; + return &proot_cli; +}
diff --git a/5.1.0/src/cli/proot.h b/5.1.0/src/cli/proot.h new file mode 100644 index 0000000..a06abd7 --- /dev/null +++ b/5.1.0/src/cli/proot.h
@@ -0,0 +1,273 @@ +/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ + +#ifndef PROOT_CLI_H +#define PROOT_CLI_H + +#include "cli/cli.h" + +#ifndef VERSION +#define VERSION "5.1.0" +#endif + +static const 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/", + "/run/", + "/var/run/dbus/system_bus_socket", +/* "/var/tmp/kdecache-$LOGNAME", */ + "$HOME", + "*path*", + NULL, +}; + +static const char *recommended_su_bindings[] = { + "/etc/host.conf", + "/etc/hosts", + "/etc/nsswitch.conf", + "/etc/resolv.conf", + "/dev/", + "/sys/", + "/proc/", + "/tmp/", + "/run/shm", + "$HOME", + "*path*", + NULL, +}; + +static int handle_option_r(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_b(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_q(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_w(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_v(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_V(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_k(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_0(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_i(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_R(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_S(Tracee *tracee, const Cli *cli, const char *value); + +static int pre_initialize_bindings(Tracee *, const Cli *, size_t, char *const *, size_t); +static int post_initialize_exe(Tracee *, const Cli *, size_t, char *const *, size_t); + +static Cli proot_cli = { + .version = VERSION, + .name = "proot", + .subtitle = "chroot, mount --bind, and binfmt_misc without privilege/setup", + .synopsis = "proot [option] ... [command]", + .colophon = "Visit http://proot.me for help, bug reports, suggestions, patchs, ...\n\ +Copyright (C) 2014 STMicroelectronics, licensed under GPL v2 or later.", + .logo = "\ + _____ _____ ___\n\ +| __ \\ __ \\_____ _____| |_\n\ +| __/ / _ \\/ _ \\ _|\n\ +|__| |__|__\\_____/\\_____/\\____|", + + .pre_initialize_bindings = pre_initialize_bindings, + .post_initialize_exe = post_initialize_exe, + + .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.\n\ +\t\n\ +\tIt is recommended to use the -R or -S options instead.", + }, + { .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.", + }, + { .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 detailed 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 = "Make current kernel appear as kernel release *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 features that are available in the kernel release specified by\n\ +\t*string* 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 = "Make current user appear as \"root\" and fake its privileges.", + .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 = "Extension options", + .arguments = { + { .name = "-i", .separator = ' ', .value = "string" }, + { .name = "--change-id", .separator = '=', .value = "string" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_i, + .description = "Make current user and group appear as *string* \"uid:gid\".", + .detail = "\tThis option makes the current user and group appear as uid and\n\ +\tgid. Likewise, files actually owned by the current user and\n\ +\tgroup appear as if they were owned by uid and gid instead.\n\ +\tNote that the -0 option is the same as -i 0:0.", + }, + { .class = "Alias options", + .arguments = { + { .name = "-R", .separator = ' ', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_R, + .description = "Alias: -r *path* + a couple of recommended -b.", + .detail = "\tPrograms isolated in *path*, a guest rootfs, might still need to\n\ +\taccess information about the host system, as it is illustrated in\n\ +\tthe Examples section of the manual. These host information\n\ +\tare typically: user/group definition, network setup, run-time\n\ +\tinformation, users' files, ... On all Linux distributions, they\n\ +\tall lie in a couple of host files and directories that are\n\ +\tautomatically bound by this option:\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 = "-S", .separator = ' ', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_S, + .description = "Alias: -0 -r *path* + a couple of recommended -b.", + .detail = "\tThis option is useful to safely create and install packages into\n\ +\tthe guest rootfs. It is similar to the -R option expect it\n\ +\tenables the -0 option and binds only the following minimal set\n\ +\tof paths to avoid unexpected changes on host files:\n\ +\t\n\ +\t * /etc/host.conf\n\ +\t * /etc/hosts\n\ +\t * /etc/nsswitch.conf\n\ +\t * /dev/\n\ +\t * /sys/\n\ +\t * /proc/\n\ +\t * /tmp/\n\ +\t * $HOME", + }, + END_OF_OPTIONS, + }, +}; + +#endif /* PROOT_CLI_H */
diff --git a/5.1.0/src/compat.h b/5.1.0/src/compat.h new file mode 100644 index 0000000..e991c39 --- /dev/null +++ b/5.1.0/src/compat.h
@@ -0,0 +1,247 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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_NULL +# define AT_NULL 0 +# endif +# ifndef AT_PHDR +# define AT_PHDR 3 +# endif +# ifndef AT_PHENT +# define AT_PHENT 4 +# endif +# ifndef AT_PHNUM +# define AT_PHNUM 5 +# endif +# ifndef AT_BASE +# define AT_BASE 7 +# endif +# ifndef AT_ENTRY +# define AT_ENTRY 9 +# endif +# ifndef AT_RANDOM +# define AT_RANDOM 25 +# endif +# ifndef AT_EXECFN +# define AT_EXECFN 31 +# endif +# ifndef AT_SYSINFO +# define AT_SYSINFO 32 +# endif +# ifndef AT_SYSINFO_EHDR +# define AT_SYSINFO_EHDR 33 +# endif +# 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_GETREGS +# define PTRACE_GETREGS 12 +# endif +# ifndef PTRACE_SETREGS +# define PTRACE_SETREGS 13 +# endif +# ifndef PTRACE_GETFPREGS +# define PTRACE_GETFPREGS 14 +# endif +# ifndef PTRACE_SETFPREGS +# define PTRACE_SETFPREGS 15 +# endif +# ifndef PTRACE_GETFPXREGS +# define PTRACE_GETFPXREGS 18 +# endif +# ifndef PTRACE_SETFPXREGS +# define PTRACE_SETFPXREGS 19 +# endif +# ifndef PTRACE_SETOPTIONS +# define PTRACE_SETOPTIONS 0x4200 +# endif +# ifndef PTRACE_GETEVENTMSG +# define PTRACE_GETEVENTMSG 0x4201 +# endif +# ifndef PTRACE_GETREGSET +# define PTRACE_GETREGSET 0x4204 +# endif +# ifndef PTRACE_SETREGSET +# define PTRACE_SETREGSET 0x4205 +# endif +# ifndef PTRACE_SEIZE +# define PTRACE_SEIZE 0x4206 +# endif +# ifndef PTRACE_INTERRUPT +# define PTRACE_INTERRUPT 0x4207 +# endif +# ifndef PTRACE_LISTEN +# define PTRACE_LISTEN 0x4208 +# 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 0x00000080 +# 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 +# if PTRACE_EVENT_SECCOMP == 7 +# define PTRACE_EVENT_SECCOMP2 8 +# elif PTRACE_EVENT_SECCOMP == 8 +# define PTRACE_EVENT_SECCOMP2 7 +# else +# error "unknown PTRACE_EVENT_SECCOMP value" +# endif +# endif +# ifndef PTRACE_SET_SYSCALL +# define PTRACE_SET_SYSCALL 23 +# endif +# ifndef PTRACE_GET_THREAD_AREA +# define PTRACE_GET_THREAD_AREA 25 +# endif +# ifndef PTRACE_SET_THREAD_AREA +# define PTRACE_SET_THREAD_AREA 26 +# endif +# ifndef PTRACE_GETVFPREGS +# define PTRACE_GETVFPREGS 27 +# endif +# ifndef PTRACE_ARCH_PRCTL +# define PTRACE_ARCH_PRCTL 30 +# endif +# ifndef ARCH_SET_GS +# define ARCH_SET_GS 0x1001 +# endif +# ifndef ARCH_SET_FS +# define ARCH_SET_FS 0x1002 +# endif +# ifndef ARCH_GET_GS +# define ARCH_GET_FS 0x1003 +# endif +# ifndef ARCH_GET_FS +# define ARCH_GET_GS 0x1004 +# endif +# ifndef PTRACE_SINGLEBLOCK +# define PTRACE_SINGLEBLOCK 33 +# 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_NAME +# define PR_SET_NAME 15 +# 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 +# ifndef FUTEX_PRIVATE_FLAG +# define FUTEX_PRIVATE_FLAG 128 +# endif +# ifndef EFD_SEMAPHORE +# define EFD_SEMAPHORE 1 +# endif +# ifndef F_DUPFD_CLOEXEC +# define F_DUPFD_CLOEXEC 1030 +# endif +# ifndef O_RDONLY +# define O_RDONLY 00000000 +# endif +# ifndef O_CLOEXEC +# define O_CLOEXEC 02000000 +# endif +# ifndef MAP_PRIVATE +# define MAP_PRIVATE 0x02 +# endif +# ifndef MAP_FIXED +# define MAP_FIXED 0x10 +# endif +# ifndef MAP_ANONYMOUS +# define MAP_ANONYMOUS 0x20 +# endif + +#endif /* COMPAT_H */
diff --git a/5.1.0/src/execve/aoxp.c b/5.1.0/src/execve/aoxp.c new file mode 100644 index 0000000..8a78eaa --- /dev/null +++ b/5.1.0/src/execve/aoxp.c
@@ -0,0 +1,439 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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/limits.h> /* ARG_MAX, */ +#include <assert.h> /* assert(3), */ +#include <string.h> /* strlen(3), memcmp(3), memcpy(3), */ +#include <strings.h> /* bzero(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <errno.h> /* E*, */ +#include <stdarg.h> /* va_*, */ +#include <stdint.h> /* uint32_t, */ +#include <talloc.h> /* talloc_*, */ + +#include "arch.h" +#include "tracee/tracee.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "build.h" + +struct mixed_pointer { + /* Pointer -- in tracee's address space -- to the current + * object, if local == NULL. */ + word_t remote; + + /* Pointer -- in tracer's address space -- to the current + * object, if local != NULL. */ + void *local; +}; + +#include "execve/aoxp.h" + +/** + * Read object pointed to by @array[@index] from tracee's memory, then + * make @local_pointer points to the locally *cached* version. This + * function returns -errno when an error occured, otherwise 0. + */ +int read_xpointee_as_object(ArrayOfXPointers *array, size_t index, void **local_pointer) +{ + int status; + int size; + + assert(index < array->length); + + /* Already cached locally? */ + if (array->_xpointers[index].local != NULL) + goto end; + + /* Remote NULL is mapped to local NULL. */ + if (array->_xpointers[index].remote == 0) { + array->_xpointers[index].local = NULL; + goto end; + } + + size = sizeof_xpointee(array, index); + if (size < 0) + return size; + + array->_xpointers[index].local = talloc_size(array, size); + if (array->_xpointers[index].local == NULL) + return -ENOMEM; + + /* Copy locally the remote object. */ + status = read_data(TRACEE(array), array->_xpointers[index].local, + array->_xpointers[index].remote, size); + if (status < 0) { + array->_xpointers[index].local = NULL; + return status; + } + +end: + *local_pointer = array->_xpointers[index].local; + return 0; +} + +/** + * Read string pointed to by @array[@index] from tracee's memory, then + * make @local_pointer points to the locally *cached* version. This + * function returns -errno when an error occured, otherwise 0. + */ +int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **local_pointer) +{ + char tmp[ARG_MAX]; + int status; + + assert(index < array->length); + + /* Already cached locally? */ + if (array->_xpointers[index].local != NULL) + goto end; + + /* Remote NULL is mapped to local NULL. */ + if (array->_xpointers[index].remote == 0) { + array->_xpointers[index].local = NULL; + goto end; + } + + /* Copy locally the remote string into a temporary buffer. */ + status = read_string(TRACEE(array), tmp, array->_xpointers[index].remote, ARG_MAX); + if (status < 0) + return status; + if (status >= ARG_MAX) + return -ENOMEM; + + /* Save the local string in a "persistent" buffer. */ + array->_xpointers[index].local = talloc_strdup(array, tmp); + if (array->_xpointers[index].local == NULL) + return -ENOMEM; + +end: + *local_pointer = array->_xpointers[index].local; + return 0; +} + +/** + * This function returns the number of bytes of the string pointed to + * by @array[@index], otherwise -errno if an error occured. + */ +int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index) +{ + char *string; + int status; + + assert(index < array->length); + + status = read_xpointee_as_string(array, index, &string); + if (status < 0) + return status; + + if (string == NULL) + return 0; + + return strlen(string) + 1; +} + +/** + * Compare object pointed to by @array[@index] with object pointed to + * by @local_reference. This function returns 1 if they are + * equivalent, 0 otherwise. On error, -errno is returned. + */ +int compare_xpointee_generic(ArrayOfXPointers *array, size_t index, const void *local_reference) +{ + void *object; + int status; + + assert(index < array->length); + + status = read_xpointee(array, index, &object); + if (status < 0) + return status; + + if (object == NULL && local_reference == NULL) + return 1; + + if (object == NULL && local_reference != NULL) + return 0; + + if (object != NULL && local_reference == NULL) + return 0; + + status = sizeof_xpointee(array, index); + if (status < 0) + return status; + + return (int) (memcmp(object, local_reference, status) == 0); +} + +/** + * This function returns the index in @array of the first pointee + * equivalent to the @local_reference pointee, otherwise it returns + * -errno if an error occured. + */ +int find_xpointee(ArrayOfXPointers *array, const void *local_reference) +{ + size_t i; + + for (i = 0; i < array->length; i++) { + int status; + + status = compare_xpointee(array, i, local_reference); + if (status < 0) + return status; + if (status != 0) + break; + } + + return i; +} + +/** + * Make @array[@index] points to a copy of the string pointed to by + * @string. This function returns -errno when an error occured, + * otherwise 0. + */ +int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string) +{ + assert(index < array->length); + + array->_xpointers[index].local = talloc_strdup(array, string); + if (array->_xpointers[index].local == NULL) + return -ENOMEM; + + return 0; +} + +/** + * Make @array[@index ... @index + @nb_xpointees] points to a copy of + * the variadic arguments. This function returns -errno when an error + * occured, otherwise 0. + */ +int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...) +{ + va_list va_xpointees; + int status; + size_t i; + + va_start(va_xpointees, nb_xpointees); + + for (i = 0; i < nb_xpointees; i++) { + void *object = va_arg(va_xpointees, void *); + + status = write_xpointee(array, index + i, object); + if (status < 0) + goto end; + } + status = 0; + +end: + va_end(va_xpointees); + 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_of_xpointers(ArrayOfXPointers *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->_xpointers, XPointer, new_length); + if (tmp == NULL) + return -ENOMEM; + array->_xpointers = tmp; + + memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index, + nb_moved_entries * sizeof(XPointer)); + + bzero(array->_xpointers + index, delta_nb_entries * sizeof(XPointer)); + } + else { + assert(delta_nb_entries <= 0); + assert(index >= (size_t) -delta_nb_entries); + + memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index, + nb_moved_entries * sizeof(XPointer)); + + tmp = talloc_realloc(array, array->_xpointers, XPointer, new_length); + if (tmp == NULL) + return -ENOMEM; + array->_xpointers = tmp; + } + + array->length = new_length; + return 0; +} + +/** + * Copy into *@array_ the pointer array pointed to by @reg from + * @tracee's memory space. 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_of_xpointers(Tracee *tracee, ArrayOfXPointers **array_, Reg reg, size_t nb_entries) +{ + word_t pointer = 1; /* ie. != 0 */ + word_t address; + ArrayOfXPointers *array; + size_t i; + + assert(array_ != NULL); + + *array_ = talloc_zero(tracee->ctx, ArrayOfXPointers); + 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->_xpointers, XPointer, i + 1); + if (tmp == NULL) + return -ENOMEM; + array->_xpointers = tmp; + + pointer = peek_word(tracee, address + i * sizeof_word(tracee)); + if (errno != 0) + return -errno; + + array->_xpointers[i].remote = pointer; + array->_xpointers[i].local = NULL; + } + array->length = i; + + /* By default, assume it is an array of string pointers. */ + array->read_xpointee = (read_xpointee_t) read_xpointee_as_string; + array->sizeof_xpointee = sizeof_xpointee_as_string; + array->write_xpointee = (write_xpointee_t) write_xpointee_as_string; + + /* By default, use generic callbacks: they rely on + * array->read_xpointee() and array->sizeof_xpointee(). */ + array->compare_xpointee = compare_xpointee_generic; + + return 0; +} + +/** + * Copy @array into tracee's memory space, then put in @reg the + * address where it was copied. This function returns -errno if an + * error occured, otherwise 0. + */ +int push_array_of_xpointers(ArrayOfXPointers *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 pointee + 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 pointee. */ + for (i = 0; i < array->length; i++) { + ssize_t size; + + if (array->_xpointers[i].local == NULL) + continue; + + /* At this moment, we only know the offsets in the + * tracee's memory block. */ + array->_xpointers[i].remote = total_size; + + size = sizeof_xpointee(array, i); + if (size < 0) + return size; + total_size += size; + + local[local_count].iov_base = array->_xpointers[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 pointees 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->_xpointers[i].local != NULL) + array->_xpointers[i].remote += tracee_ptr; + + if (is_32on64_mode(tracee)) + ((uint32_t *) pod_array)[i] = array->_xpointers[i].remote; + else + pod_array[i] = array->_xpointers[i].remote; + } + + /* Write all the modified pointees 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; +}
diff --git a/5.1.0/src/execve/aoxp.h b/5.1.0/src/execve/aoxp.h new file mode 100644 index 0000000..3ccfc35 --- /dev/null +++ b/5.1.0/src/execve/aoxp.h
@@ -0,0 +1,80 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 AOXP_H +#define AOXP_H + +#include <stdbool.h> + +#include "tracee/reg.h" +#include "arch.h" + +typedef struct array_of_xpointers ArrayOfXPointers; +typedef int (*read_xpointee_t)(ArrayOfXPointers *array, size_t index, void **object); +typedef int (*write_xpointee_t)(ArrayOfXPointers *array, size_t index, const void *object); +typedef int (*compare_xpointee_t)(ArrayOfXPointers *array, size_t index, const void *reference); +typedef int (*sizeof_xpointee_t)(ArrayOfXPointers *array, size_t index); + +typedef struct mixed_pointer XPointer; +struct array_of_xpointers { + XPointer *_xpointers; + size_t length; + + read_xpointee_t read_xpointee; + write_xpointee_t write_xpointee; + compare_xpointee_t compare_xpointee; + sizeof_xpointee_t sizeof_xpointee; +}; + +static inline int read_xpointee(ArrayOfXPointers *array, size_t index, void **object) +{ + return array->read_xpointee(array, index, object); +} + +static inline int write_xpointee(ArrayOfXPointers *array, size_t index, const void *object) +{ + return array->write_xpointee(array, index, object); +} + +static inline int compare_xpointee(ArrayOfXPointers *array, size_t index, const void *reference) +{ + return array->compare_xpointee(array, index, reference); +} + +static inline int sizeof_xpointee(ArrayOfXPointers *array, size_t index) +{ + return array->sizeof_xpointee(array, index); +} + +extern int find_xpointee(ArrayOfXPointers *array, const void *reference); +extern int resize_array_of_xpointers(ArrayOfXPointers *array, size_t index, ssize_t nb_delta_entries); +extern int fetch_array_of_xpointers(Tracee *tracee, ArrayOfXPointers **array, Reg reg, size_t nb_entries); +extern int push_array_of_xpointers(ArrayOfXPointers *array, Reg reg); + +extern int read_xpointee_as_object(ArrayOfXPointers *array, size_t index, void **object); +extern int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **string); +extern int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string); +extern int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...); +extern int compare_xpointee_generic(ArrayOfXPointers *array, size_t index, const void *reference); +extern int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index); + +#endif /* AOXP_H */
diff --git a/5.1.0/src/execve/auxv.c b/5.1.0/src/execve/auxv.c new file mode 100644 index 0000000..f3eb97e --- /dev/null +++ b/5.1.0/src/execve/auxv.c
@@ -0,0 +1,184 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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/auxvec.h> /* AT_*, */ +#include <assert.h> /* assert(3), */ +#include <errno.h> /* E*, */ +#include <unistd.h> /* write(3), close(3), */ +#include <sys/types.h> /* open(2), */ +#include <sys/stat.h> /* open(2), */ +#include <fcntl.h> /* open(2), */ + +#include "execve/auxv.h" +#include "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "tracee/mem.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "arch.h" + + +/** + * Add the given vector [@type, @value] to @vectors. This function + * returns -errno if an error occurred, otherwise 0. + */ +int add_elf_aux_vector(ElfAuxVector **vectors, word_t type, word_t value) +{ + ElfAuxVector *tmp; + size_t nb_vectors; + + assert(*vectors != NULL); + + nb_vectors = talloc_array_length(*vectors); + + /* Sanity checks. */ + assert(nb_vectors > 0); + assert((*vectors)[nb_vectors - 1].type == AT_NULL); + + tmp = talloc_realloc(talloc_parent(*vectors), *vectors, ElfAuxVector, nb_vectors + 1); + if (tmp == NULL) + return -ENOMEM; + *vectors = tmp; + + /* Replace the sentinel with the new vector. */ + (*vectors)[nb_vectors - 1].type = type; + (*vectors)[nb_vectors - 1].value = value; + + /* Restore the sentinel. */ + (*vectors)[nb_vectors].type = AT_NULL; + (*vectors)[nb_vectors].value = 0; + + return 0; +} + +/** + * Get the address of the the ELF auxiliary vectors table for the + * given @tracee. This function returns 0 if an error occurred. + */ +word_t get_elf_aux_vectors_address(const Tracee *tracee) +{ + word_t address; + word_t data; + + /* Sanity check: this works only in execve sysexit. */ + assert(IS_IN_SYSEXIT2(tracee, PR_execve)); + + /* Right after execve, the stack layout is: + * + * argc, argv[0], ..., 0, envp[0], ..., 0, auxv[0].type, auxv[0].value, ..., 0, 0 + */ + address = peek_reg(tracee, CURRENT, STACK_POINTER); + + /* Read: argc */ + data = peek_word(tracee, address); + if (errno != 0) + return 0; + + /* Skip: argc, argv, 0 */ + address += (1 + data + 1) * sizeof_word(tracee); + + /* Skip: envp, 0 */ + do { + data = peek_word(tracee, address); + if (errno != 0) + return 0; + address += sizeof_word(tracee); + } while (data != 0); + + return address; +} + +/** + * Fetch ELF auxiliary vectors stored at the given @address in + * @tracee's memory. This function returns NULL if an error occurred, + * otherwise it returns a pointer to the new vectors, in an ABI + * independent form (the Talloc parent of this pointer is + * @tracee->ctx). + */ +ElfAuxVector *fetch_elf_aux_vectors(const Tracee *tracee, word_t address) +{ + ElfAuxVector *vectors = NULL; + ElfAuxVector vector; + int status; + + /* It is assumed the sentinel always exists. */ + vectors = talloc_array(tracee->ctx, ElfAuxVector, 1); + if (vectors == NULL) + return NULL; + vectors[0].type = AT_NULL; + vectors[0].value = 0; + + while (1) { + vector.type = peek_word(tracee, address); + if (errno != 0) + return NULL; + address += sizeof_word(tracee); + + if (vector.type == AT_NULL) + break; /* Already added. */ + + vector.value = peek_word(tracee, address); + if (errno != 0) + return NULL; + address += sizeof_word(tracee); + + status = add_elf_aux_vector(&vectors, vector.type, vector.value); + if (status < 0) + return NULL; + } + + return vectors; +} + +/** + * Push ELF auxiliary @vectors to the given @address in @tracee's + * memory. This function returns -errno if an error occurred, + * otherwise 0. + */ +int push_elf_aux_vectors(const Tracee* tracee, ElfAuxVector *vectors, word_t address) +{ + size_t i; + + for (i = 0; vectors[i].type != AT_NULL; i++) { + poke_word(tracee, address, vectors[i].type); + if (errno != 0) + return -errno; + address += sizeof_word(tracee); + + poke_word(tracee, address, vectors[i].value); + if (errno != 0) + return -errno; + address += sizeof_word(tracee); + } + + poke_word(tracee, address, AT_NULL); + if (errno != 0) + return -errno; + address += sizeof_word(tracee); + + poke_word(tracee, address, 0); + if (errno != 0) + return -errno; + address += sizeof_word(tracee); + + return 0; +}
diff --git a/5.1.0/src/execve/auxv.h b/5.1.0/src/execve/auxv.h new file mode 100644 index 0000000..12ceb0d --- /dev/null +++ b/5.1.0/src/execve/auxv.h
@@ -0,0 +1,39 @@ +/* -*- 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 AUXV +#define AUXV + +#include "tracee/tracee.h" +#include "arch.h" + +typedef struct elf_aux_vector { + word_t type; + word_t value; +} ElfAuxVector; + +extern word_t get_elf_aux_vectors_address(const Tracee *tracee); +extern ElfAuxVector *fetch_elf_aux_vectors(const Tracee *tracee, word_t address); +extern int add_elf_aux_vector(ElfAuxVector **vectors, word_t type, word_t value); +extern int push_elf_aux_vectors(const Tracee* tracee, ElfAuxVector *vectors, word_t address); + +#endif /* AUXV */
diff --git a/5.1.0/src/execve/elf.c b/5.1.0/src/execve/elf.c new file mode 100644 index 0000000..c87ae0e --- /dev/null +++ b/5.1.0/src/execve/elf.c
@@ -0,0 +1,178 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <fcntl.h> /* open(2), */ +#include <unistd.h> /* read(2), close(2), */ +#include <errno.h> /* EACCES, ENOTSUP, */ +#include <stdint.h> /* UINT64_MAX, */ +#include <limits.h> /* PATH_MAX, */ +#include <string.h> /* str*(3), memcpy(3), */ +#include <assert.h> /* assert(3), */ +#include <talloc.h> /* talloc_*, */ +#include <stdbool.h> /* bool, true, false, */ + +#include "execve/elf.h" +#include "tracee/tracee.h" +#include "cli/note.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) { + status = -errno; + 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; +} + +/** + * Invoke @callback(..., @data) for each program headers from the + * specified ELF file (referenced by @fd, with the given @elf_header). + * This function returns -errno if an error occured, or it returns + * immediately the value != 0 returned by @callback, otherwise 0. + */ +int iterate_program_headers(const Tracee *tracee, int fd, const ElfHeader *elf_header, + program_headers_iterator_t callback, void *data) +{ + ProgramHeader program_header; + + 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) { + note(tracee, WARNING, INTERNAL, "%d: big PH tables are not yet supported.", fd); + return -ENOTSUP; + } + + if (!KNOWN_PHENTSIZE(*elf_header, elf_phentsize)) { + note(tracee, WARNING, INTERNAL, "%d: unsupported size of program header.", fd); + return -ENOTSUP; + } + + 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) + return (status < 0 ? -errno : -ENOTSUP); + + status = callback(elf_header, &program_header, data); + if (status != 0) + return status; + } + + return 0; +} + +/** + * Check if @host_path is an ELF file for the host architecture. + */ +bool is_host_elf(const Tracee *tracee, const char *host_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(host_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", host_path); + return true; + } + } + + return false; +}
diff --git a/5.1.0/src/execve/elf.h b/5.1.0/src/execve/elf.h new file mode 100644 index 0000000..83e70ac --- /dev/null +++ b/5.1.0/src/execve/elf.h
@@ -0,0 +1,178 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdint.h> +#include <stdbool.h> + +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; + +/* Object type: */ +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 + +/* Segment flags: */ +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 + +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))) + +#define IS_POSITION_INDENPENDANT(elf_header) \ + (ELF_FIELD((elf_header), type) == ET_DYN) + +#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); + +typedef int (* program_headers_iterator_t)(const ElfHeader *elf_header, + const ProgramHeader *program_header, void *data); + +extern int iterate_program_headers(const Tracee *tracee, int fd, const ElfHeader *elf_header, + program_headers_iterator_t callback, void *data); + +#endif /* ELF_H */
diff --git a/5.1.0/src/execve/enter.c b/5.1.0/src/execve/enter.c new file mode 100644 index 0000000..4c163a1 --- /dev/null +++ b/5.1.0/src/execve/enter.c
@@ -0,0 +1,667 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* lstat(2), lseek(2), */ +#include <sys/stat.h> /* lstat(2), lseek(2), fchmod(2), */ +#include <unistd.h> /* access(2), lstat(2), close(2), read(2), */ +#include <errno.h> /* E*, */ +#include <assert.h> /* assert(3), */ +#include <talloc.h> /* talloc*, */ +#include <sys/mman.h> /* PROT_*, */ +#include <string.h> /* strlen(3), strcpy(3), */ +#include <stdlib.h> /* getenv(3), */ +#include <stdio.h> /* fwrite(3), */ +#include <assert.h> /* assert(3), */ + +#include "execve/execve.h" +#include "execve/shebang.h" +#include "execve/aoxp.h" +#include "execve/ldso.h" +#include "execve/elf.h" +#include "path/path.h" +#include "path/temp.h" +#include "tracee/tracee.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "arch.h" +#include "cli/note.h" + +#define P(a) PROGRAM_FIELD(load_info->elf_header, *program_header, a) + +/** + * Add @program_header (type PT_LOAD) to @load_info->mappings. This + * function returns -errno if an error occured, otherwise it returns + * 0. + */ +static int add_mapping(const Tracee *tracee UNUSED, LoadInfo *load_info, + const ProgramHeader *program_header) +{ + size_t index; + word_t start_address; + word_t end_address; + static word_t page_size = 0; + static word_t page_mask = 0; + + if (page_size == 0) { + page_size = sysconf(_SC_PAGE_SIZE); + if ((int) page_size <= 0) + page_size = 0x1000; + page_mask = ~(page_size - 1); + } + + if (load_info->mappings == NULL) + index = 0; + else + index = talloc_array_length(load_info->mappings); + + load_info->mappings = talloc_realloc(load_info, load_info->mappings, Mapping, index + 1); + if (load_info->mappings == NULL) + return -ENOMEM; + + start_address = P(vaddr) & page_mask; + end_address = (P(vaddr) + P(filesz) + page_size) & page_mask; + + load_info->mappings[index].fd = -1; /* Unknown yet. */ + load_info->mappings[index].offset = P(offset) & page_mask; + load_info->mappings[index].addr = start_address; + load_info->mappings[index].length = end_address - start_address; + load_info->mappings[index].flags = MAP_PRIVATE | MAP_FIXED; + load_info->mappings[index].prot = ( (P(flags) & PF_R ? PROT_READ : 0) + | (P(flags) & PF_W ? PROT_WRITE : 0) + | (P(flags) & PF_X ? PROT_EXEC : 0)); + + /* "If the segment's memory size p_memsz is larger than the + * file size p_filesz, the "extra" bytes are defined to hold + * the value 0 and to follow the segment's initialized area." + * -- man 7 elf. */ + if (P(memsz) > P(filesz)) { + /* How many extra bytes in the current page? */ + load_info->mappings[index].clear_length = end_address - P(vaddr) - P(filesz); + + /* Create new pages for the remaining extra bytes. */ + start_address = end_address; + end_address = (P(vaddr) + P(memsz) + page_size) & page_mask; + if (end_address > start_address) { + index++; + load_info->mappings = talloc_realloc(load_info, load_info->mappings, + Mapping, index + 1); + if (load_info->mappings == NULL) + return -ENOMEM; + + load_info->mappings[index].fd = -1; /* Anonymous. */ + load_info->mappings[index].offset = 0; + load_info->mappings[index].addr = start_address; + load_info->mappings[index].length = end_address - start_address; + load_info->mappings[index].clear_length = 0; + load_info->mappings[index].flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + load_info->mappings[index].prot = load_info->mappings[index - 1].prot; + } + } + else + load_info->mappings[index].clear_length = 0; + + return 0; +} + +/** + * Translate @user_path into @host_path and check if this latter exists, is + * executable and is a regular file. This function returns -errno if + * an error occured, 0 otherwise. + */ +int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path) +{ + struct stat statl; + int status; + + if (user_path[0] == '\0') + return -ENOEXEC; + + status = translate_path(tracee, host_path, AT_FDCWD, user_path, true); + if (status < 0) + return status; + + status = access(host_path, F_OK); + if (status < 0) + return -ENOENT; + + status = access(host_path, X_OK); + if (status < 0) + return -EACCES; + + status = lstat(host_path, &statl); + if (status < 0) + return -EPERM; + + return 0; +} + +/** + * Add @program_header (type PT_INTERP) to @load_info->interp. This + * function returns -errno if an error occured, otherwise it returns + * 0. + */ +static int add_interp(Tracee *tracee, int fd, LoadInfo *load_info, + const ProgramHeader *program_header) +{ + char host_path[PATH_MAX]; + char *user_path; + int status; + + /* Only one PT_INTERP segment is allowed. */ + if (load_info->interp != NULL) + return -EINVAL; + + load_info->interp = talloc_zero(load_info, LoadInfo); + if (load_info->interp == NULL) + return -ENOMEM; + + user_path = talloc_size(tracee->ctx, P(filesz) + 1); + if (user_path == NULL) + return -ENOMEM; + + /* Remember pread(2) doesn't change the + * current position in the file. */ + status = pread(fd, user_path, P(filesz), P(offset)); + if ((size_t) status != P(filesz)) /* Unexpected size. */ + status = -EACCES; + if (status < 0) + return status; + + user_path[P(filesz)] = '\0'; + + /* When a QEMU command was specified: + * + * - if it's a foreign binary we are reading the ELF + * interpreter of QEMU instead. + * + * - if it's a host binary, we are reading its ELF + * interpreter. + * + * In both case, it lies in "/host-rootfs" from a guest + * point-of-view. */ + if (tracee->qemu != NULL && user_path[0] == '/') { + user_path = talloc_asprintf(tracee->ctx, "%s%s", HOST_ROOTFS, user_path); + if (user_path == NULL) + return -ENOMEM; + } + + status = translate_and_check_exec(tracee, host_path, user_path); + if (status < 0) + return status; + + load_info->interp->host_path = talloc_strdup(load_info->interp, host_path); + if (load_info->interp->host_path == NULL) + return -ENOMEM; + + load_info->interp->user_path = talloc_strdup(load_info->interp, user_path); + if (load_info->interp->user_path == NULL) + return -ENOMEM; + + return 0; +} + +#undef P + +struct add_load_info_data { + LoadInfo *load_info; + Tracee *tracee; + int fd; +}; + +/** + * This function is a program header iterator. It invokes + * add_mapping() or add_interp(), according to the type of + * @program_header. This function returns -errno if an error + * occurred, otherwise 0. + */ +static int add_load_info(const ElfHeader *elf_header, + const ProgramHeader *program_header, void *data_) +{ + struct add_load_info_data *data = data_; + int status; + + switch (PROGRAM_FIELD(*elf_header, *program_header, type)) { + case PT_LOAD: + status = add_mapping(data->tracee, data->load_info, program_header); + if (status < 0) + return status; + break; + + case PT_INTERP: + status = add_interp(data->tracee, data->fd, data->load_info, program_header); + if (status < 0) + return status; + break; + + default: + break; + } + + return 0; +} + +/** + * Extract the load info from @load->host_path. This function returns + * -errno if an error occured, otherwise it returns 0. + */ +static int extract_load_info(Tracee *tracee, LoadInfo *load_info) +{ + struct add_load_info_data data; + int fd = -1; + int status; + + assert(load_info != NULL); + assert(load_info->host_path != NULL); + + fd = open_elf(load_info->host_path, &load_info->elf_header); + if (fd < 0) + return fd; + + /* Sanity check. */ + switch (ELF_FIELD(load_info->elf_header, type)) { + case ET_EXEC: + case ET_DYN: + break; + + default: + status = -EINVAL; + goto end; + } + + data.load_info = load_info; + data.tracee = tracee; + data.fd = fd; + + status = iterate_program_headers(tracee, fd, &load_info->elf_header, add_load_info, &data); +end: + if (fd >= 0) + close(fd); + + return status; +} + +/** + * Add @load_base to each adresses of @load_info. + */ +static void add_load_base(LoadInfo *load_info, word_t load_base) +{ + size_t nb_mappings; + size_t i; + + nb_mappings = talloc_array_length(load_info->mappings); + for (i = 0; i < nb_mappings; i++) + load_info->mappings[i].addr += load_base; + + if (IS_CLASS64(load_info->elf_header)) + load_info->elf_header.class64.e_entry += load_base; + else + load_info->elf_header.class32.e_entry += load_base; +} + +/** + * Compute the final load address for each position independant + * objects of @tracee. + * + * TODO: support for ASLR. + */ +static void compute_load_addresses(Tracee *tracee) +{ + if (IS_POSITION_INDENPENDANT(tracee->load_info->elf_header) + && tracee->load_info->mappings[0].addr == 0) { +#if defined(HAS_LOADER_32BIT) + if (IS_CLASS32(tracee->load_info->elf_header)) + add_load_base(tracee->load_info, EXEC_PIC_ADDRESS_32); + else +#endif + add_load_base(tracee->load_info, EXEC_PIC_ADDRESS); + } + + /* Nothing more to do? */ + if (tracee->load_info->interp == NULL) + return; + + if (IS_POSITION_INDENPENDANT(tracee->load_info->interp->elf_header) + && tracee->load_info->interp->mappings[0].addr == 0) { +#if defined(HAS_LOADER_32BIT) + if (IS_CLASS32(tracee->load_info->elf_header)) + add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS_32); + else +#endif + add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS); + } +} + +/** + * Expand in argv[] and envp[] the runner for @user_path, if needed. + * This function returns -errno if an error occurred, otherwise 0. On + * success, both @host_path and @user_path point to the program to + * execute (respectively from host and guest point-of-views), and both + * @tracee's argv[] (pointed to by SYSARG_2) @tracee's envp[] (pointed + * to by SYSARG_3) are correctly updated. + */ +static int expand_runner(Tracee* tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]) +{ + ArrayOfXPointers *envp; + char *argv0; + int status; + + /* Execution of host programs when QEMU is in use relies on + * LD_ environment variables. */ + status = fetch_array_of_xpointers(tracee, &envp, SYSARG_3, 0); + if (status < 0) + return status; + + /* Environment variables should be compared with the "name" + * part of the "name=value" string format. */ + envp->compare_xpointee = (compare_xpointee_t) compare_xpointee_env; + + /* No need to adjust argv[] if it's a host binary (a.k.a + * mixed-mode). */ + if (!is_host_elf(tracee, host_path)) { + ArrayOfXPointers *argv; + size_t nb_qemu_args; + size_t i; + + status = fetch_array_of_xpointers(tracee, &argv, SYSARG_2, 0); + if (status < 0) + return status; + + status = read_xpointee_as_string(argv, 0, &argv0); + if (status < 0) + return status; + + /* Assuming PRoot was invoked this way: + * + * proot -q 'qemu-arm -cpu cortex-a9' ... + * + * a call to: + * + * execve("/bin/true", { "true", NULL }, ...) + * + * becomes: + * + * execve("/usr/bin/qemu", + * { "qemu", "-cpu", "cortex-a9", "-0", "true", "/bin/true", NULL }, ...) + */ + + nb_qemu_args = talloc_array_length(tracee->qemu) - 1; + status = resize_array_of_xpointers(argv, 1, nb_qemu_args + 2); + if (status < 0) + return status; + + for (i = 0; i < nb_qemu_args; i++) { + status = write_xpointee(argv, i, tracee->qemu[i]); + if (status < 0) + return status; + } + + status = write_xpointees(argv, i, 3, "-0", argv0, user_path); + if (status < 0) + return status; + + /* Ensure LD_ features should not be applied to QEMU + * iteself. */ + status = ldso_env_passthru(tracee, envp, argv, "-E", "-U", i); + if (status < 0) + return status; + + status = push_array_of_xpointers(argv, SYSARG_2); + if (status < 0) + return status; + + /* Launch the runner in lieu of the initial + * program. */ + assert(strlen(tracee->qemu[0]) + strlen(HOST_ROOTFS) < PATH_MAX); + assert(tracee->qemu[0][0] == '/'); + + strcpy(host_path, tracee->qemu[0]); + + strcpy(user_path, HOST_ROOTFS); + strcat(user_path, host_path); + } + + /* 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, host_path, envp); + if (status < 0) + return status; + + status = push_array_of_xpointers(envp, SYSARG_3); + if (status < 0) + return status; + + return 0; +} + +extern unsigned char _binary_loader_exe_start; +extern unsigned char _binary_loader_exe_end; + +extern unsigned char WEAK _binary_loader_m32_exe_start; +extern unsigned char WEAK _binary_loader_m32_exe_end; + +/** + * Extract the built-in loader. This function returns NULL if an + * error occurred, otherwise it returns the path to the extracted + * loader. Note: @tracee is only used for notification purpose. + */ +static char *extract_loader(const Tracee *tracee, bool wants_32bit_version) +{ + char path[PATH_MAX]; + size_t status2; + void *start; + size_t size; + int status; + int fd; + + char *loader_path = NULL; + FILE *file = NULL; + + file = open_temp_file(NULL, "prooted"); + if (file == NULL) + goto end; + fd = fileno(file); + + if (wants_32bit_version) { + start = (void *) &_binary_loader_m32_exe_start; + size = (size_t)(&_binary_loader_m32_exe_end-&_binary_loader_m32_exe_start); + } + else { + start = (void *) &_binary_loader_exe_start; + size = (size_t) (&_binary_loader_exe_end-&_binary_loader_exe_start); + } + + status2 = write(fd, start, size); + if (status2 != size) { + note(tracee, ERROR, SYSTEM, "can't write the loader"); + goto end; + } + + status = fchmod(fd, S_IRUSR|S_IXUSR); + if (status < 0) { + note(tracee, ERROR, SYSTEM, "can't change loader permissions (u+rx)"); + goto end; + } + + status = readlink_proc_pid_fd(getpid(), fd, path); + if (status < 0) { + note(tracee, ERROR, INTERNAL, "can't retrieve loader path (/proc/self/fd/)"); + goto end; + } + + loader_path = talloc_strdup(talloc_autofree_context(), path); + if (loader_path == NULL) { + note(tracee, ERROR, INTERNAL, "can't allocate memory"); + goto end; + } + + if (tracee->verbose >= 2) + note(tracee, INFO, INTERNAL, "loader: %s", loader_path); + +end: + if (file != NULL) { + status = fclose(file); + if (status < 0) + note(tracee, WARNING, SYSTEM, "can't close loader file"); + } + + return loader_path; +} + +/** + * Get the path to the loader for the given @tracee. This function + * returns NULL if an error occurred. + */ +static inline const char *get_loader_path(const Tracee *tracee) +{ + static char *loader_path = NULL; + +#if defined(HAS_LOADER_32BIT) + static char *loader32_path = NULL; + + if (IS_CLASS32(tracee->load_info->elf_header)) { + loader32_path = loader32_path ?: getenv("PROOT_LOADER_32") ?: extract_loader(tracee, true); + return loader32_path; + } + else +#endif + { + loader_path = loader_path ?: getenv("PROOT_LOADER") ?: extract_loader(tracee, false); + return loader_path; + } +} + +/** + * Extract all the information that will be required by + * translate_load_*(). This function returns -errno if an error + * occured, otherwise 0. + */ +int translate_execve_enter(Tracee *tracee) +{ + char user_path[PATH_MAX]; + char host_path[PATH_MAX]; + char new_exe[PATH_MAX]; + char *raw_path; + const char *loader_path; + int status; + + if (IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee)) { + /* Syscalls can now be reported to its ptracer. */ + tracee->as_ptracee.ignore_loader_syscalls = false; + + /* Cancel this spurious execve, it was only used as a + * notification. */ + set_sysnum(tracee, PR_void); + return 0; + } + + status = get_sysarg_path(tracee, user_path, SYSARG_1); + if (status < 0) + return status; + + /* Remember the user path before it is overwritten by + * expand_shebang(). This "raw" path is useful to fix the + * value of AT_EXECFN and /proc/{@tracee->pid}/comm. */ + raw_path = talloc_strdup(tracee->ctx, user_path); + if (raw_path == NULL) + return -ENOMEM; + + status = expand_shebang(tracee, host_path, user_path); + if (status < 0) + /* The Linux kernel actually returns -EACCES when + * trying to execute a directory. */ + return status == -EISDIR ? -EACCES : status; + + /* user_path is modified only if there's an interpreter + * (ie. for a script or with qemu). */ + if (status == 0 && tracee->qemu == NULL) + TALLOC_FREE(raw_path); + + /* Remember the new value for "/proc/self/exe". It points to + * a canonicalized guest path, hence detranslate_path() + * instead of using user_path directly. */ + strcpy(new_exe, host_path); + status = detranslate_path(tracee, new_exe, NULL); + if (status >= 0) { + talloc_unlink(tracee, tracee->new_exe); + tracee->new_exe = talloc_strdup(tracee, new_exe); + } + else + tracee->new_exe = NULL; + + if (tracee->qemu != NULL) { + status = expand_runner(tracee, host_path, user_path); + if (status < 0) + return status; + } + + TALLOC_FREE(tracee->load_info); + + tracee->load_info = talloc_zero(tracee, LoadInfo); + if (tracee->load_info == NULL) + return -ENOMEM; + + tracee->load_info->host_path = talloc_strdup(tracee->load_info, host_path); + if (tracee->load_info->host_path == NULL) + return -ENOMEM; + + tracee->load_info->user_path = talloc_strdup(tracee->load_info, user_path); + if (tracee->load_info->user_path == NULL) + return -ENOMEM; + + tracee->load_info->raw_path = (raw_path != NULL + ? talloc_reparent(tracee->ctx, tracee->load_info, raw_path) + : talloc_reference(tracee->load_info, tracee->load_info->user_path)); + if (tracee->load_info->raw_path == NULL) + return -ENOMEM; + + status = extract_load_info(tracee, tracee->load_info); + if (status < 0) + return status; + + if (tracee->load_info->interp != NULL) { + status = extract_load_info(tracee, tracee->load_info->interp); + if (status < 0) + return status; + + /* An ELF interpreter is supposed to be + * standalone. */ + if (tracee->load_info->interp->interp != NULL) + return -EINVAL; + } + + compute_load_addresses(tracee); + + /* Execute the loader instead of the program. */ + loader_path = get_loader_path(tracee); + if (loader_path == NULL) + return -ENOENT; + + status = set_sysarg_path(tracee, loader_path, SYSARG_1); + if (status < 0) + return status; + + /* Mask to its ptracer syscalls performed by the loader. */ + tracee->as_ptracee.ignore_loader_syscalls = true; + + return 0; +}
diff --git a/5.1.0/src/execve/execve.h b/5.1.0/src/execve/execve.h new file mode 100644 index 0000000..2296094 --- /dev/null +++ b/5.1.0/src/execve/execve.h
@@ -0,0 +1,63 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <linux/limits.h> /* PATH_MAX, */ + +#include "tracee/tracee.h" +#include "execve/elf.h" +#include "arch.h" + +extern int translate_execve_enter(Tracee *tracee); +extern void translate_execve_exit(Tracee *tracee); +extern int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path); + +typedef struct mapping { + word_t addr; + word_t length; + word_t clear_length; + word_t prot; + word_t flags; + word_t fd; + word_t offset; +} Mapping; + +typedef struct load_info { + char *host_path; + char *user_path; + char *raw_path; + Mapping *mappings; + ElfHeader elf_header; + + struct load_info *interp; +} LoadInfo; + +#define IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee) ( \ + (tracee)->as_ptracee.ptracer != NULL \ + && peek_reg((tracee), CURRENT, SYSARG_1) == (word_t) 1 \ + && peek_reg((tracee), CURRENT, SYSARG_4) == (word_t) 2 \ + && peek_reg((tracee), CURRENT, SYSARG_5) == (word_t) 3 \ + && peek_reg((tracee), CURRENT, SYSARG_6) == (word_t) 4) + +#endif /* EXECVE_H */
diff --git a/5.1.0/src/execve/exit.c b/5.1.0/src/execve/exit.c new file mode 100644 index 0000000..576fb03 --- /dev/null +++ b/5.1.0/src/execve/exit.c
@@ -0,0 +1,445 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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/auxvec.h> /* AT_*, */ +#include <talloc.h> /* talloc*, */ +#include <sys/mman.h> /* MAP_*, */ +#include <assert.h> /* assert(3), */ +#include <string.h> /* strlen(3), strerror(3), */ +#include <strings.h> /* bzero(3), */ +#include <signal.h> /* kill(2), SIG*, */ +#include <unistd.h> /* write(2), */ +#include <errno.h> /* E*, */ + +#include "execve/execve.h" +#include "execve/elf.h" +#include "loader/script.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "tracee/mem.h" +#include "syscall/sysnum.h" +#include "execve/auxv.h" +#include "path/binding.h" +#include "path/temp.h" +#include "cli/note.h" + + +/** + * Fill @path with the content of @vectors, formatted according to + * @ptracee's current ABI. + */ +static int fill_file_with_auxv(const Tracee *ptracee, const char *path, + const ElfAuxVector *vectors) +{ + const ssize_t current_sizeof_word = sizeof_word(ptracee); + ssize_t status; + int fd = -1; + int i; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + + i = 0; + do { + status = write(fd, &vectors[i].type, current_sizeof_word); + if (status < current_sizeof_word) { + status = -1; + goto end; + } + + status = write(fd, &vectors[i].value, current_sizeof_word); + if (status < current_sizeof_word) { + status = -1; + goto end; + } + } while (vectors[i++].type != AT_NULL); + + status = 0; +end: + if (fd >= 0) + (void) close(fd); + + return status; +} + +/** + * Bind content of @vectors over /proc/{@ptracee->pid}/auxv. This + * function returns -1 if an error occurred, otherwise 0. + */ +static int bind_proc_pid_auxv(const Tracee *ptracee) +{ + word_t vectors_address; + ElfAuxVector *vectors; + + const char *guest_path; + const char *host_path; + Binding *binding; + int status; + + vectors_address = get_elf_aux_vectors_address(ptracee); + if (vectors_address == 0) + return -1; + + vectors = fetch_elf_aux_vectors(ptracee, vectors_address); + if (vectors == NULL) + return -1; + + /* Path to these ELF auxiliary vectors. */ + guest_path = talloc_asprintf(ptracee->ctx, "/proc/%d/auxv", ptracee->pid); + if (guest_path == NULL) + return -1; + + /* Remove binding to this path, if any. It contains ELF + * auxiliary vectors of the previous execve(2). */ + binding = get_binding(ptracee, GUEST, guest_path); + if (binding != NULL && compare_paths(binding->guest.path, guest_path) == PATHS_ARE_EQUAL) { + remove_binding_from_all_lists(ptracee, binding); + TALLOC_FREE(binding); + } + + host_path = create_temp_file(ptracee->ctx, "auxv"); + if (host_path == NULL) + return -1; + + status = fill_file_with_auxv(ptracee, host_path, vectors); + if (status < 0) + return -1; + + /* Note: this binding will be removed once ptracee gets freed. */ + binding = insort_binding3(ptracee, ptracee->life_context, host_path, guest_path); + if (binding == NULL) + return -1; + + /* This temporary file (host_path) will be removed once the + * binding is freed. */ + talloc_reparent(ptracee->ctx, binding, host_path); + + return 0; +} + +/** + * Convert @mappings into load @script statements at the given @cursor + * position. This function returns the new cursor position. + */ +static void *transcript_mappings(void *cursor, const Mapping *mappings) +{ + size_t nb_mappings; + size_t i; + + nb_mappings = talloc_array_length(mappings); + for (i = 0; i < nb_mappings; i++) { + LoadStatement *statement = cursor; + + if ((mappings[i].flags & MAP_ANONYMOUS) != 0) + statement->action = LOAD_ACTION_MMAP_ANON; + else + statement->action = LOAD_ACTION_MMAP_FILE; + + statement->mmap.addr = mappings[i].addr; + statement->mmap.length = mappings[i].length; + statement->mmap.prot = mappings[i].prot; + statement->mmap.offset = mappings[i].offset; + statement->mmap.clear_length = mappings[i].clear_length; + + cursor += LOAD_STATEMENT_SIZE(*statement, mmap); + } + + return cursor; +} + +/** + * Convert @tracee->load_info into a load script, then transfer this + * latter into @tracee's memory. + */ +static int transfer_load_script(Tracee *tracee) +{ + const word_t stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER); + word_t entry_point; + + size_t script_size; + size_t strings_size; + size_t string1_size; + size_t string2_size; + size_t string3_size; + size_t padding_size; + + word_t string1_address; + word_t string2_address; + word_t string3_address; + + void *buffer; + size_t buffer_size; + + LoadStatement *statement; + void *cursor; + int status; + + /* Strings addresses are required to generate the load script, + * for "open" actions. Since I want to generate it in one + * pass, these strings will be put right below the current + * stack pointer -- the only known adresses so far -- in the + * "strings area". */ + string1_size = strlen(tracee->load_info->user_path) + 1; + + string2_size = (tracee->load_info->interp == NULL ? 0 + : strlen(tracee->load_info->interp->user_path) + 1); + + string3_size = (tracee->load_info->raw_path == tracee->load_info->user_path ? 0 + : strlen(tracee->load_info->raw_path) + 1); + + /* A padding will be appended at the end of the load script + * (a.k.a "strings area") to ensure this latter is aligned on + * a word boundary, for sake of performance. */ + padding_size = (stack_pointer - string1_size - string2_size - string3_size) + % sizeof_word(tracee); + + strings_size = string1_size + string2_size + string3_size + padding_size; + string1_address = stack_pointer - strings_size; + string2_address = stack_pointer - strings_size + string1_size; + string3_address = (string3_size == 0 + ? string1_address + : stack_pointer - strings_size + string1_size + string2_size); + + /* Compute the size of the load script. */ + script_size = + LOAD_STATEMENT_SIZE(*statement, open) + + (LOAD_STATEMENT_SIZE(*statement, mmap) + * talloc_array_length(tracee->load_info->mappings)) + + (tracee->load_info->interp == NULL ? 0 + : LOAD_STATEMENT_SIZE(*statement, open) + + (LOAD_STATEMENT_SIZE(*statement, mmap) + * talloc_array_length(tracee->load_info->interp->mappings))) + + LOAD_STATEMENT_SIZE(*statement, start); + + /* Allocate enough room for both the load script and the + * strings area. */ + buffer_size = script_size + strings_size; + buffer = talloc_zero_size(tracee->ctx, buffer_size); + if (buffer == NULL) + return -ENOMEM; + + cursor = buffer; + + /* Load script statement: open. */ + statement = cursor; + statement->action = LOAD_ACTION_OPEN; + statement->open.string_address = string1_address; + + cursor += LOAD_STATEMENT_SIZE(*statement, open); + + /* Load script statements: mmap. */ + cursor = transcript_mappings(cursor, tracee->load_info->mappings); + + if (tracee->load_info->interp != NULL) { + /* Load script statement: open. */ + statement = cursor; + statement->action = LOAD_ACTION_OPEN_NEXT; + statement->open.string_address = string2_address; + + cursor += LOAD_STATEMENT_SIZE(*statement, open); + + /* Load script statements: mmap. */ + cursor = transcript_mappings(cursor, tracee->load_info->interp->mappings); + + entry_point = ELF_FIELD(tracee->load_info->interp->elf_header, entry); + } + else + entry_point = ELF_FIELD(tracee->load_info->elf_header, entry); + + /* Load script statement: start. */ + statement = cursor; + + /* Start of the program slightly differs when ptraced. */ + if (tracee->as_ptracee.ptracer != NULL) + statement->action = LOAD_ACTION_START_TRACED; + else + statement->action = LOAD_ACTION_START; + + statement->start.stack_pointer = stack_pointer; + statement->start.entry_point = entry_point; + statement->start.at_phent = ELF_FIELD(tracee->load_info->elf_header, phentsize); + statement->start.at_phnum = ELF_FIELD(tracee->load_info->elf_header, phnum); + statement->start.at_entry = ELF_FIELD(tracee->load_info->elf_header, entry); + statement->start.at_phdr = ELF_FIELD(tracee->load_info->elf_header, phoff) + + tracee->load_info->mappings[0].addr; + statement->start.at_execfn = string3_address; + + cursor += LOAD_STATEMENT_SIZE(*statement, start); + + /* Sanity check. */ + assert((uintptr_t) cursor - (uintptr_t) buffer == script_size); + + /* Convert the load script to the expected format. */ + if (is_32on64_mode(tracee)) { + int i; + for (i = 0; buffer + i * sizeof(uint64_t) < cursor; i++) + ((uint32_t *) buffer)[i] = ((uint64_t *) buffer)[i]; + } + + /* Concatenate the load script and the strings. */ + memcpy(cursor, tracee->load_info->user_path, string1_size); + cursor += string1_size; + + if (string2_size != 0) { + memcpy(cursor, tracee->load_info->interp->user_path, string2_size); + cursor += string2_size; + } + + if (string3_size != 0) { + memcpy(cursor, tracee->load_info->raw_path, string3_size); + cursor += string3_size; + } + + /* Sanity check. */ + cursor += padding_size; + assert((uintptr_t) cursor - (uintptr_t) buffer == buffer_size); + + /* Allocate enough room in tracee's memory for the load + * script, and make the first user argument points to this + * location. Note that it is safe to update the stack pointer + * manually since we are in execve sysexit. However it should + * be done before transfering data since the kernel might not + * allow page faults below the stack pointer. */ + poke_reg(tracee, STACK_POINTER, stack_pointer - buffer_size); + poke_reg(tracee, USERARG_1, stack_pointer - buffer_size); + + /* Copy everything in the tracee's memory at once. */ + status = write_data(tracee, stack_pointer - buffer_size, buffer, buffer_size); + if (status < 0) + return status; + + /* Tracee's stack content is now as follow: + * + * +------------+ <- initial stack pointer (higher address) + * | padding | + * +------------+ + * | string3 | + * +------------+ + * | string2 | + * +------------+ + * | string1 | + * +------------+ + * | start | + * +------------+ + * | mmap anon | + * +------------+ + * | mmap file | + * +------------+ + * | open next | + * +------------+ + * | mmap anon. | + * +------------+ + * | mmap file | + * +------------+ + * | open | + * +------------+ <- stack pointer, sysarg1 (word aligned) + */ + + /* Remember we are in the sysexit stage, so be sure the + * current register values will be used as-is at the end. */ + save_current_regs(tracee, ORIGINAL); + tracee->_regs_were_changed = true; + + return 0; +} + +/** + * Start the loading of @tracee. This function returns no error since + * it's either too late to do anything useful (the calling process is + * already replaced) or the error reported by the kernel + * (syscall_result < 0) will be propagated as-is. + */ +void translate_execve_exit(Tracee *tracee) +{ + word_t syscall_result; + int status; + + if (IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee)) { + /* Be sure not to confuse the ptracer with an + * unexpected syscall/returned value. */ + poke_reg(tracee, SYSARG_RESULT, 0); + set_sysnum(tracee, PR_execve); + + /* According to most ABIs, all registers have + * undefined values at program startup except: + * + * - the stack pointer + * - the instruction pointer + * - the rtld_fini pointer + * - the state flags + */ + poke_reg(tracee, STACK_POINTER, peek_reg(tracee, ORIGINAL, SYSARG_2)); + poke_reg(tracee, INSTR_POINTER, peek_reg(tracee, ORIGINAL, SYSARG_3)); + poke_reg(tracee, RTLD_FINI, 0); + poke_reg(tracee, STATE_FLAGS, 0); + + /* Restore registers with their current values. */ + save_current_regs(tracee, ORIGINAL); + tracee->_regs_were_changed = true; + + /* This is is required to make GDB work correctly + * under PRoot, however it deserves to be used + * unconditionally. */ + (void) bind_proc_pid_auxv(tracee); + + /* If the PTRACE_O_TRACEEXEC option is *not* in effect + * for the execing tracee, the kernel delivers an + * extra SIGTRAP to the tracee after execve(2) + * *returns*. This is an ordinary signal (similar to + * one which can be generated by "kill -TRAP"), not a + * special kind of ptrace-stop. Employing + * PTRACE_GETSIGINFO for this signal returns si_code + * set to 0 (SI_USER). This signal may be blocked by + * signal mask, and thus may be delivered (much) + * later. -- man 2 ptrace + * + * This signal is delayed so far since the program was + * not fully loaded yet; GDB would get "invalid + * adress" errors otherwise. */ + if ((tracee->as_ptracee.options & PTRACE_O_TRACEEXEC) == 0) + kill(tracee->pid, SIGTRAP); + + return; + } + + syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + if ((int) syscall_result < 0) + return; + + /* Execve happened; commit the new "/proc/self/exe". */ + if (tracee->new_exe != NULL) { + (void) talloc_unlink(tracee, tracee->exe); + tracee->exe = talloc_reference(tracee, tracee->new_exe); + talloc_set_name_const(tracee->exe, "$exe"); + } + + /* New processes have no heap. */ + bzero(tracee->heap, sizeof(Heap)); + + /* Transfer the load script to the loader. */ + status = transfer_load_script(tracee); + if (status < 0) + note(tracee, ERROR, INTERNAL, "can't transfer load script: %s", strerror(-status)); + + return; +}
diff --git a/5.1.0/src/execve/ldso.c b/5.1.0/src/execve/ldso.c new file mode 100644 index 0000000..7285786 --- /dev/null +++ b/5.1.0/src/execve/ldso.c
@@ -0,0 +1,569 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdbool.h> /* bool, true, false, */ +#include <string.h> /* strlen(3), strcpy(3), strcat(3), strcmp(3), */ +#include <stdlib.h> /* getenv(3), */ +#include <assert.h> /* assert(3), */ +#include <errno.h> /* ENOMEM, */ +#include <unistd.h> /* close(2), */ +#include <linux/limits.h> /* PATH_MAX, ARG_MAX, */ + +#include "execve/ldso.h" +#include "execve/elf.h" +#include "execve/aoxp.h" +#include "tracee/tracee.h" +#include "cli/note.h" + +/** + * Check if the environment @variable has the given @name. + */ +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 @envp at the given @index, otherwise it returns -errno when an + * error occured. + */ +int compare_xpointee_env(ArrayOfXPointers *envp, size_t index, const char *reference) +{ + char *value; + int status; + + assert(index < envp->length); + + status = read_xpointee_as_string(envp, 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(const Tracee *tracee, ArrayOfXPointers *envp, ArrayOfXPointers *argv, + const char *define, const char *undefine, size_t offset) +{ + 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_xpointee_as_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; + + /* When a host program executes a guest program, use + * the value of LD_LIBRARY_PATH as it was before being + * swapped by the mixed-mode support. */ + if ( tracee->host_ldso_paths != NULL + && tracee->guest_ldso_paths != NULL + && is_env_name(env, "LD_LIBRARY_PATH") + && strcmp(env, tracee->host_ldso_paths) == 0) + env = (char *) tracee->guest_ldso_paths; + +#define PASSTHRU(check, name) \ + if (is_env_name(env, name)) { \ + check |= true; \ + /* Errors are not fatal here. */ \ + status = resize_array_of_xpointers(argv, offset, 2); \ + if (status >= 0) { \ + status = write_xpointees(argv, offset, 2, define, env); \ + if (status < 0) \ + return status; \ + } \ + write_xpointee(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_of_xpointers(argv, offset, 2); + if (status >= 0) { + status = write_xpointees(argv, offset, 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; +} + +struct find_program_header_data { + ProgramHeader *program_header; + SegmentType type; + uint64_t address; +}; + +/** + * This function is a program header iterator. It stops the iteration + * (by returning 1) once it has found a program header that matches + * @data. This function returns -errno if an error occurred, + * otherwise 0 or 1. + */ +static int find_program_header(const ElfHeader *elf_header, + const ProgramHeader *program_header, void *data_) +{ + struct find_program_header_data *data = data_; + + if (PROGRAM_FIELD(*elf_header, *program_header, type) == data->type) { + uint64_t start; + uint64_t end; + + memcpy(data->program_header, program_header, sizeof(ProgramHeader)); + + if (data->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 + && data->address >= start + && data->address <= end) + return 1; + } + + 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. + */ +static int read_ldso_rpaths(const Tracee* tracee, int fd, const ElfHeader *elf_header, + char **rpaths, char **runpaths) +{ + ProgramHeader dynamic_segment; + ProgramHeader strtab_segment; + struct find_program_header_data data; + uint64_t strtab_address = (uint64_t) -1; + off_t strtab_offset; + int status; + size_t i; + + uint64_t offsetof_dynamic_segment; + uint64_t sizeof_dynamic_segment; + size_t sizeof_dynamic_entry; + + data.program_header = &dynamic_segment; + data.type = PT_DYNAMIC; + data.address = (uint64_t) -1; + + status = iterate_program_headers(tracee, fd, elf_header, find_program_header, &data); + if (status <= 0) + return status; + + offsetof_dynamic_segment = PROGRAM_FIELD(*elf_header, dynamic_segment, offset); + sizeof_dynamic_segment = PROGRAM_FIELD(*elf_header, dynamic_segment, filesz); + + if (IS_CLASS32(*elf_header)) + sizeof_dynamic_entry = sizeof(DynamicEntry32); + else + sizeof_dynamic_entry = sizeof(DynamicEntry64); + + if (sizeof_dynamic_segment % sizeof_dynamic_entry != 0) + return -ENOEXEC; + +/** + * Invoke @embedded_code on each dynamic entry of the given @type. + */ +#define FOREACH_DYNAMIC_ENTRY(type, embedded_code) \ + for (i = 0; i < sizeof_dynamic_segment / sizeof_dynamic_entry; i++) { \ + DynamicEntry dynamic_entry; \ + uint64_t value; \ + \ + /* embedded_code may change the file offset. */ \ + status = (int) lseek(fd, offsetof_dynamic_segment + 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; \ + \ + value = DYNAMIC_FIELD(*elf_header, dynamic_entry, val); \ + \ + embedded_code \ + } + + /* Get the address of the *first* string table. The ELF + * specification doesn't mention if it may have several string + * table references. */ + FOREACH_DYNAMIC_ENTRY(DT_STRTAB, { + strtab_address = value; + break; + }) + + if (strtab_address == (uint64_t) -1) + return 0; + + data.program_header = &strtab_segment; + data.type = PT_LOAD; + data.address = strtab_address; + + /* Search the program header that contains the given string table. */ + status = iterate_program_headers(tracee, fd, elf_header, find_program_header, &data); + if (status < 0) + return status; + + strtab_offset = PROGRAM_FIELD(*elf_header, strtab_segment, offset) + + (strtab_address - PROGRAM_FIELD(*elf_header, strtab_segment, vaddr)); + + FOREACH_DYNAMIC_ENTRY(DT_RPATH, { + if (strtab_offset < 0 || (uint64_t) strtab_offset > UINT64_MAX - value) + return -ENOEXEC; + + status = add_xpaths(tracee, fd, strtab_offset + value, rpaths); + if (status < 0) + return status; + }) + + FOREACH_DYNAMIC_ENTRY(DT_RUNPATH, { + if (strtab_offset < 0 || (uint64_t) strtab_offset > UINT64_MAX - value) + return -ENOEXEC; + + status = add_xpaths(tracee, fd, strtab_offset + value, runpaths); + if (status < 0) + return status; + }) + +#undef FOREACH_DYNAMIC_ENTRY + + return 0; +} + +/** + * Rebuild the variable LD_LIBRARY_PATH in @envp for the program + * @host_path 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(Tracee *tracee, const char host_path[PATH_MAX], ArrayOfXPointers *envp) +{ + static char *initial_ldso_paths = NULL; + ElfHeader elf_header; + + char host_ldso_paths[ARG_MAX] = ""; + bool rpath_found = false; + + char *rpaths = NULL; + char *runpaths = NULL; + + size_t length1; + size_t length2; + + size_t index; + int status; + int fd; + + fd = open_elf(host_path, &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. */ + rpath_found = 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. */ + rpath_found = 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_xpointee(envp, "LD_LIBRARY_PATH"); + if (status < 0) + return 0; /* Not fatal. */ + index = (size_t) status; + + if (index == envp->length) { + /* Allocate a new entry at the end of envp[] when + * LD_LIBRARY_PATH was not found. */ + + index = (envp->length > 0 ? envp->length - 1 : 0); + status = resize_array_of_xpointers(envp, index, 1); + if (status < 0) + return 0; /* Not fatal. */ + } + else if (tracee->guest_ldso_paths == NULL) { + /* Remember guest LD_LIBRARY_PATH in order to restore + * it when a host program will execute a guest + * program. */ + char *env; + + /* Errors are not fatal here. */ + status = read_xpointee_as_string(envp, index, &env); + if (status >= 0) + tracee->guest_ldso_paths = talloc_strdup(tracee, env); + } + + /* 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_xpointee(envp, index, host_ldso_paths); + + /* The guest LD_LIBRARY_PATH will be restored only if the host + * program didn't change it explicitly, so remember its + * initial value. */ + if (tracee->host_ldso_paths == NULL) + tracee->host_ldso_paths = talloc_strdup(tracee, host_ldso_paths); + + return (int) rpath_found; +}
diff --git a/5.1.0/src/execve/ldso.h b/5.1.0/src/execve/ldso.h new file mode 100644 index 0000000..b2406da --- /dev/null +++ b/5.1.0/src/execve/ldso.h
@@ -0,0 +1,42 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <linux/limits.h> +#include <stdbool.h> + +#include "execve/aoxp.h" +#include "execve/elf.h" + +extern int ldso_env_passthru(const Tracee *tracee, ArrayOfXPointers *envp, ArrayOfXPointers *argv, + const char *define, const char *undefine, size_t offset); + +extern int rebuild_host_ldso_paths(Tracee *tracee, const char t_program[PATH_MAX], + ArrayOfXPointers *envp); + +extern int compare_xpointee_env(ArrayOfXPointers *envp, size_t index, const char *name); + +extern bool is_env_name(const char *variable, const char *name); + +#endif /* LDSO_H */
diff --git a/5.1.0/src/execve/shebang.c b/5.1.0/src/execve/shebang.c new file mode 100644 index 0000000..bbffac8 --- /dev/null +++ b/5.1.0/src/execve/shebang.c
@@ -0,0 +1,307 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* open(2), */ +#include <sys/stat.h> /* open(2), */ +#include <fcntl.h> /* open(2), */ +#include <linux/limits.h> /* PATH_MAX, */ +#include <linux/binfmts.h> /* BINPRM_BUF_SIZE, */ +#include <unistd.h> /* read(2), close(2), */ +#include <errno.h> /* -E*, */ +#include <sys/param.h> /* MAXSYMLINKS, */ +#include <stdbool.h> /* bool, */ +#include <assert.h> /* assert(3), */ + +#include "execve/shebang.h" +#include "execve/execve.h" +#include "execve/aoxp.h" +#include "tracee/tracee.h" +#include "attribute.h" + +/** + * Extract into @user_path and @argument the shebang from @host_path. + * 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. + */ +static int extract_shebang(const Tracee *tracee UNUSED, const char *host_path, + char user_path[PATH_MAX], char argument[BINPRM_BUF_SIZE]) +{ + char tmp2[2]; + char tmp; + + size_t current_length; + size_t i; + + int status; + int fd; + + /* Assumption. */ + assert(BINPRM_BUF_SIZE < PATH_MAX); + + argument[0] = '\0'; + + /* Inspect the executable. */ + fd = open(host_path, O_RDONLY); + if (fd < 0) + return -errno; + + status = read(fd, tmp2, 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 (tmp2[0] != '#' || tmp2[1] != '!') { + status = 0; + goto end; + } + current_length = 2; + user_path[0] = '\0'; + + /* 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 = -ENOEXEC; + goto end; + } + + current_length++; + } while ((tmp == ' ' || tmp == '\t') && current_length < BINPRM_BUF_SIZE); + + /* Slurp the interpreter path until the first space or end-of-line. */ + for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, i++) { + switch (tmp) { + case ' ': + case '\t': + /* Remove spaces in between the interpreter + * and the hypothetical argument. */ + user_path[i] = '\0'; + break; + + case '\n': + case '\r': + /* There is no argument. */ + user_path[i] = '\0'; + argument[0] = '\0'; + status = 1; + goto end; + + default: + /* There is an argument if the previous + * character in user_path[] is '\0'. */ + if (i > 1 && user_path[i - 1] == '\0') + goto argument; + else + user_path[i] = tmp; + break; + } + + status = read(fd, &tmp, sizeof(char)); + if (status < 0) { + status = -errno; + goto end; + } + if ((size_t) status < sizeof(char)) { /* EOF */ + user_path[i] = '\0'; + argument[0] = '\0'; + status = 1; + goto end; + } + } + + /* The interpreter path is too long, truncate it. */ + user_path[i] = '\0'; + argument[0] = '\0'; + status = 1; + goto end; + +argument: + + /* Slurp the argument until the end-of-line. */ + for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, 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, truncate it. */ + argument[i] = '\0'; + status = 1; + +end: + close(fd); + + /* Did an error occur or isn't a script? */ + if (status <= 0) + return status; + + return 1; +} + +/** + * Expand in argv[] the shebang of @user_path, if any. This function + * returns -errno if an error occurred, 1 if a shebang was found and + * extracted, otherwise 0. On success, both @host_path and @user_path + * point to the program to execute (respectively from host + * point-of-view and as-is), and @tracee's argv[] (pointed to by + * SYSARG_2) is correctly updated. + */ +int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]) +{ + ArrayOfXPointers *argv = NULL; + bool has_shebang = false; + + char argument[BINPRM_BUF_SIZE]; + int status; + size_t i; + + /* "The interpreter must be a valid pathname for an executable + * which is not itself a script [1]. 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()." -- man 2 execve + * + * [1]: as of this writing (3.10.17) this is true only for the + * ELF interpreter; ie. a script can use a script as + * interpreter. + */ + for (i = 0; i < MAXSYMLINKS; i++) { + char *old_user_path; + + /* Translate this path (user -> host), then check it is executable. */ + status = translate_and_check_exec(tracee, host_path, user_path); + if (status < 0) + return status; + + /* Remember the initial user path. */ + old_user_path = talloc_strdup(tracee->ctx, user_path); + if (old_user_path == NULL) + return -ENOMEM; + + /* Extract into user_path and argument the shebang from host_path. */ + status = extract_shebang(tracee, host_path, user_path, argument); + if (status < 0) + return status; + + /* No more shebang. */ + if (status == 0) + break; + has_shebang = true; + + /* Translate new path (user -> host), then check it is executable. */ + status = translate_and_check_exec(tracee, host_path, user_path); + if (status < 0) + return status; + + /* Fetch argv[] only on demand. */ + if (argv == NULL) { + status = fetch_array_of_xpointers(tracee, &argv, SYSARG_2, 0); + if (status < 0) + return status; + } + + /* Assuming the shebang of "script" is "#!/bin/sh -x", + * a call to: + * + * execve("./script", { "script.sh", NULL }, ...) + * + * becomes: + * + * execve("/bin/sh", { "/bin/sh", "-x", "./script", NULL }, ...) + * + * See commit 8c8fbe85 about "argv->length == 1". */ + if (argument[0] != '\0') { + status = resize_array_of_xpointers(argv, 0, 2 + (argv->length == 1)); + if (status < 0) + return status; + + status = write_xpointees(argv, 0, 3, user_path, argument, old_user_path); + if (status < 0) + return status; + } + else { + status = resize_array_of_xpointers(argv, 0, 1 + (argv->length == 1)); + if (status < 0) + return status; + + status = write_xpointees(argv, 0, 2, user_path, old_user_path); + if (status < 0) + return status; + } + } + + if (i == MAXSYMLINKS) + return -ELOOP; + + /* Push argv[] only on demand. */ + if (argv != NULL) { + status = push_array_of_xpointers(argv, SYSARG_2); + if (status < 0) + return status; + } + + return (has_shebang ? 1 : 0); +}
diff --git a/5.1.0/src/execve/shebang.h b/5.1.0/src/execve/shebang.h new file mode 100644 index 0000000..1834a98 --- /dev/null +++ b/5.1.0/src/execve/shebang.h
@@ -0,0 +1,32 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 SHEBANG_H +#define SHEBANG_H + +#include <linux/limits.h> /* PATH_MAX, ARG_MAX, */ + +#include "tracee/tracee.h" + +extern int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]); + +#endif /* SHEBANG_H */
diff --git a/5.1.0/src/extension/care/archive.c b/5.1.0/src/extension/care/archive.c new file mode 100644 index 0000000..414d05c --- /dev/null +++ b/5.1.0/src/extension/care/archive.c
@@ -0,0 +1,540 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* open(2), lseek(2), */ +#include <sys/stat.h> /* open(2), */ +#include <fcntl.h> /* open(2), */ +#include <unistd.h> /* read(2), readlink(2), close(2), lseek(2), */ +#include <errno.h> /* errno, EACCES, */ +#include <assert.h> /* assert(3), */ +#include <linux/limits.h> /* PATH_MAX, */ +#include <string.h> /* strlen(3), strcmp(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <talloc.h> /* talloc(3), */ +#include <archive.h> /* archive_*(3), */ +#include <archive_entry.h> /* archive_entry*(3), */ + +#include "extension/care/archive.h" +#include "tracee/tracee.h" +#include "cli/note.h" + +typedef struct { + int (*set_format)(struct archive *); + int (*add_filter)(struct archive *); + int hardlink_resolver_strategy; + const char *options; + enum { NOT_SPECIAL = 0, SELF_EXTRACTING, RAW } special; +} Format; + +/** + * Move *@cursor backward -- within in the given @string -- if it + * reads @suffix once moved. + */ +static bool slurp_suffix(const char *string, const char **cursor, const char *suffix) +{ + size_t length; + + length = strlen(suffix); + if (*cursor - length < string || strncmp(*cursor - length, suffix, length) != 0) + return false; + + *cursor -= length; + return true; +} + +/** + * Detect the expected format for the given @string. This function + * returns -1 if an error occurred, otherwise it returns 0 and updates + * the @format structure and @suffix_length with the number of + * characters that describes the parsed format. + */ +static int parse_suffix(const Tracee* tracee, Format *format, + const char *string, size_t *suffix_length) +{ + const char *cursor; + bool found; + + bool no_wrapper_found = false; + bool no_filter_found = false; + bool no_format_found = false; + + cursor = string + strlen(string); + bzero(format, sizeof(Format)); + +/* parse_special: */ + + found = slurp_suffix(string, &cursor, "/"); + if (found) + goto end; + + found = slurp_suffix(string, &cursor, ".raw"); + if (found) { + format->special = SELF_EXTRACTING; + goto parse_filter; + } + + found = slurp_suffix(string, &cursor, ".bin"); + if (found) { +#if defined(CARE_BINARY_IS_PORTABLE) + format->special = SELF_EXTRACTING; + goto parse_filter; +#else + note(tracee, ERROR, USER, "This version of CARE was built " + "without self-extracting (.bin) support"); + return -1; +#endif + } + + no_wrapper_found = true; + +parse_filter: + + found = slurp_suffix(string, &cursor, ".gz"); + if (found) { + format->add_filter = archive_write_add_filter_gzip; + format->options = "gzip:compression-level=1"; + goto parse_format; + } + + found = slurp_suffix(string, &cursor, ".lzo"); + if (found) { + format->add_filter = archive_write_add_filter_lzop; + format->options = "lzop:compression-level=1"; + goto parse_format; + } + + found = slurp_suffix(string, &cursor, ".tgz"); + if (found) { + format->add_filter = archive_write_add_filter_gzip; + format->options = "gzip:compression-level=1"; + format->set_format = archive_write_set_format_gnutar; + format->hardlink_resolver_strategy = ARCHIVE_FORMAT_TAR_GNUTAR; + goto sanity_checks; + } + + found = slurp_suffix(string, &cursor, ".tzo"); + if (found) { + format->add_filter = archive_write_add_filter_lzop; + format->options = "lzop:compression-level=1"; + format->set_format = archive_write_set_format_gnutar; + format->hardlink_resolver_strategy = ARCHIVE_FORMAT_TAR_GNUTAR; + goto sanity_checks; + } + + no_filter_found = true; + +parse_format: + + found = slurp_suffix(string, &cursor, ".cpio"); + if (found) { + format->set_format = archive_write_set_format_cpio; + format->hardlink_resolver_strategy = ARCHIVE_FORMAT_CPIO_POSIX; + goto sanity_checks; + } + + found = slurp_suffix(string, &cursor, ".tar"); + if (found) { + format->set_format = archive_write_set_format_gnutar; + format->hardlink_resolver_strategy = ARCHIVE_FORMAT_TAR_GNUTAR; + goto sanity_checks; + } + + no_format_found = true; + +sanity_checks: + + if (no_filter_found && no_format_found) { + format->add_filter = archive_write_add_filter_lzop; + format->options = "lzop:compression-level=1"; + format->set_format = archive_write_set_format_gnutar; + format->hardlink_resolver_strategy = ARCHIVE_FORMAT_TAR_GNUTAR; + +#if defined(CARE_BINARY_IS_PORTABLE) + format->special = SELF_EXTRACTING; + if (no_wrapper_found) + note(tracee, WARNING, USER, + "unknown suffix, assuming self-extracting format."); +#else + format->special = RAW; + if (no_wrapper_found) + note(tracee, WARNING, USER, + "unknown suffix, assuming raw format."); +#endif + + no_wrapper_found = false; + no_filter_found = false; + no_format_found = false; + } + + if (no_format_found) { + note(tracee, WARNING, USER, "unknown format, assuming tar format."); + format->set_format = archive_write_set_format_gnutar; + format->hardlink_resolver_strategy = ARCHIVE_FORMAT_TAR_GNUTAR; + + no_format_found = false; + } + +end: + *suffix_length = strlen(cursor); + return 0; +} + +/** + * Copy "/proc/self/exe" into @destination. This function returns -1 + * if an error occured, otherwise the file descriptor of the + * destination. + */ +static int copy_self_exe(const Tracee *tracee, const char *destination) +{ + int output_fd; + int input_fd; + int status; + + input_fd = open("/proc/self/exe", O_RDONLY); + if (input_fd < 0) { + note(tracee, ERROR, SYSTEM, "can't open '/proc/self/exe'"); + return -1; + } + + output_fd = open(destination, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU|S_IRGRP|S_IXGRP); + if (output_fd < 0) { + note(tracee, ERROR, SYSTEM, "can't open/create '%s'", destination); + status = -1; + goto end; + } + + while (1) { + uint8_t buffer[4 * 1024]; + ssize_t size; + + status = read(input_fd, buffer, sizeof(buffer)); + if (status < 0) { + note(tracee, ERROR, SYSTEM, "can't read '/proc/self/exe'"); + goto end; + } + + if (status == 0) + break; + + size = status; + status = write(output_fd, buffer, size); + if (status < 0) { + note(tracee, ERROR, SYSTEM, "can't write '%s'", destination); + goto end; + } + if (status != size) + note(tracee, WARNING, INTERNAL, + "wrote %zd bytes instead of %zd", (size_t) status, size); + } + +end: + (void) close(input_fd); + + if (status < 0) { + (void) close(output_fd); + return -1; + } + + return output_fd; +} + +/** + * Create a new archive structure (memory allocation attached to + * @context) for the given @output file. This function returns NULL + * on error, otherwise the newly allocated archive structure. See + * parse_suffix() for the meaning of @suffix_length. + */ +Archive *new_archive(TALLOC_CTX *context, const Tracee* tracee, + const char *output, size_t *suffix_length) +{ + Format format; + Archive *archive; + int status; + + assert(output != NULL); + + status = parse_suffix(tracee, &format, output, suffix_length); + if (status < 0) + return NULL; + + archive = talloc_zero(context, Archive); + if (archive == NULL) { + note(tracee, ERROR, INTERNAL, "can't allocate archive structure"); + return NULL; + } + archive->fd = -1; + + /* No format was set, content will be copied into a directory + * instead of being archived. */ + if (format.set_format == NULL) { + int flags = ARCHIVE_EXTRACT_PERM + | ARCHIVE_EXTRACT_TIME + | ARCHIVE_EXTRACT_ACL + | ARCHIVE_EXTRACT_FFLAGS + | ARCHIVE_EXTRACT_XATTR + | (geteuid() == 0 ? ARCHIVE_EXTRACT_OWNER : 0); + + archive->handle = archive_write_disk_new(); + if (archive->handle == NULL) { + note(tracee, WARNING, INTERNAL, "can't initialize archive structure"); + return NULL; + } + + status = archive_write_disk_set_options(archive->handle, flags); + if (status != ARCHIVE_OK) { + note(tracee, ERROR, INTERNAL, "can't set archive options: %s", + archive_error_string(archive->handle)); + return NULL; + } + + status = archive_write_disk_set_standard_lookup(archive->handle); + if (status != ARCHIVE_OK) { + note(tracee, ERROR, INTERNAL, "can't set archive lookup: %s", + archive_error_string(archive->handle)); + return NULL; + } + + archive->hardlink_resolver = archive_entry_linkresolver_new(); + if (archive->hardlink_resolver != NULL) + archive_entry_linkresolver_set_strategy(archive->hardlink_resolver, + ARCHIVE_FORMAT_TAR); + + return archive; + } + + archive->handle = archive_write_new(); + if (archive->handle == NULL) { + note(tracee, WARNING, INTERNAL, "can't initialize archive structure"); + return NULL; + } + + assert(format.set_format != NULL); + status = format.set_format(archive->handle); + if (status != ARCHIVE_OK) { + note(tracee, ERROR, INTERNAL, "can't set archive format: %s", + archive_error_string(archive->handle)); + return NULL; + } + + if (format.hardlink_resolver_strategy != 0) { + archive->hardlink_resolver = archive_entry_linkresolver_new(); + if (archive->hardlink_resolver != NULL) + archive_entry_linkresolver_set_strategy(archive->hardlink_resolver, + format.hardlink_resolver_strategy); + } + + if (format.add_filter != NULL) { + status = format.add_filter(archive->handle); + if (status != ARCHIVE_OK) { + note(tracee, ERROR, INTERNAL, "can't add archive filter: %s", + archive_error_string(archive->handle)); + return NULL; + } + } + + if (format.options != NULL) { + status = archive_write_set_options(archive->handle, format.options); + if (status != ARCHIVE_OK) { + note(tracee, ERROR, INTERNAL, "can't set archive options: %s", + archive_error_string(archive->handle)); + return NULL; + } + } + + switch (format.special) { + case SELF_EXTRACTING: + archive->fd = copy_self_exe(tracee, output); + if (archive->fd < 0) + return NULL; + + /* Remember where the CARE binary ends. */ + archive->offset = lseek(archive->fd, 0, SEEK_CUR); + + status = archive_write_open_fd(archive->handle, archive->fd); + break; + + case RAW: + archive->fd = open(output, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP); + if (archive->fd < 0) { + note(tracee, ERROR, SYSTEM, "can't open/create '%s'", output); + return NULL; + } + + status = write(archive->fd, "RAW", strlen("RAW")); + if (status != strlen("RAW")) { + note(tracee, ERROR, SYSTEM, "can't write '%s'", output); + (void) close(archive->fd); + return NULL; + } + + /* Remember where the "RAW" string ends. */ + archive->offset = lseek(archive->fd, 0, SEEK_CUR); + + status = archive_write_open_fd(archive->handle, archive->fd); + break; + + default: + status = archive_write_open_filename(archive->handle, output); + break; + } + if (status != ARCHIVE_OK) { + note(tracee, ERROR, INTERNAL, "can't open archive '%s': %s", + output, archive_error_string(archive->handle)); + return NULL; + } + + return archive; +} + +/** + * Finalize the given @archive. This function returns -1 if an error + * occurred, otherwise 0. + */ +int finalize_archive(Archive *archive) +{ + int status; + + if (archive == NULL || archive->handle == NULL) + return -1; + + if (archive->hardlink_resolver != NULL) + archive_entry_linkresolver_free(archive->hardlink_resolver); + + status = archive_write_close(archive->handle); + if (status != ARCHIVE_OK) + return -1; + + status = archive_write_free(archive->handle); + if (status != ARCHIVE_OK) + return -1; + + return 0; +} + +/** + * Put the content of @path into @archive, with the specified @statl + * status, at the given @alternate_path (NULL if unchanged). This + * function returns -1 if an error occurred, otherwise 0. Note: this + * function can be called with @tracee == NULL. + */ +int archive(const Tracee* tracee, Archive *archive, + const char *path, const char *alternate_path, const struct stat *statl) +{ + struct archive_entry *entry = NULL; + ssize_t status; + mode_t type; + size_t size; + int fd = -1; + + if (archive == NULL || archive->handle == NULL) + return -1; + + entry = archive_entry_new(); + if (entry == NULL) { + note(tracee, WARNING, INTERNAL, "can't create archive entry for '%s': %s", + path, archive_error_string(archive->handle)); + status = -1; + goto end; + } + + archive_entry_set_pathname(entry, alternate_path ?: path); + archive_entry_copy_stat(entry, statl); + + if (archive->hardlink_resolver != NULL) { + struct archive_entry *unused; + archive_entry_linkify(archive->hardlink_resolver, &entry, &unused); + } + + /* Get status only once hardlinks were resolved. */ + size = archive_entry_size(entry); + type = archive_entry_filetype(entry); + + if (type == AE_IFLNK) { + char target[PATH_MAX]; + status = readlink(path, target, PATH_MAX); + if (status >= PATH_MAX) { + status = -1; + errno = ENAMETOOLONG; + } + if (status < 0) { + note(tracee, WARNING, SYSTEM, "can't readlink '%s'", path); + status = -1; + goto end; + } + target[status] = '\0'; + + /* Must be done before archive_write_header(). */ + archive_entry_set_symlink(entry, target); + } + + status = archive_write_header(archive->handle, entry); + if (status != ARCHIVE_OK) { + note(tracee, WARNING, INTERNAL, "can't write header for '%s': %s", + path, archive_error_string(archive->handle)); + status = -1; + goto end; + } + + /* No content to archive? */ + if (type != AE_IFREG || size == 0) { + status = 0; + goto end; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + if (errno != EACCES) + note(tracee, WARNING, SYSTEM, "can't open '%s'", path); + status = -1; + goto end; + } + + /* Copy the content from the file into the archive. */ + do { + uint8_t buffer[4096]; + + status = read(fd, buffer, sizeof(buffer)); + if (status < 0) { + note(tracee, WARNING, SYSTEM, "can't read '%s'", path); + status = -1; + goto end; + } + + size = archive_write_data(archive->handle, buffer, status); + if ((size_t) status != size) { + note(tracee, WARNING, INTERNAL, "can't archive '%s' content: %s", + path, archive_error_string(archive->handle)); + status = -1; + goto end; + } + } while (status > 0); + status = 0; + +end: + if (fd >= 0) + (void) close(fd); + + if (entry != NULL) + archive_entry_free(entry); + + return status; +}
diff --git a/5.1.0/src/extension/care/archive.h b/5.1.0/src/extension/care/archive.h new file mode 100644 index 0000000..3b588b0 --- /dev/null +++ b/5.1.0/src/extension/care/archive.h
@@ -0,0 +1,47 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 ARCHIVE_H +#define ARCHIVE_H + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> + +#include "tracee/tracee.h" + +typedef struct { + struct archive *handle; + struct archive_entry_linkresolver *hardlink_resolver; + + /* Information used to create an self-extracting archive. */ + off_t offset; + int fd; +} Archive; + +extern Archive *new_archive(TALLOC_CTX *context, const Tracee* tracee, + const char *output, size_t *prefix_length); +extern int finalize_archive(Archive *archive); +extern int archive(const Tracee* tracee, Archive *archive, + const char *path, const char *alternate_path, const struct stat *statl); + +#endif /* ARCHIVE_H */
diff --git a/5.1.0/src/extension/care/care.c b/5.1.0/src/extension/care/care.c new file mode 100644 index 0000000..6587149 --- /dev/null +++ b/5.1.0/src/extension/care/care.c
@@ -0,0 +1,604 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* struct stat, */ +#include <sys/stat.h> /* struct stat, */ +#include <unistd.h> /* lstat(2), */ +#include <linux/limits.h> /* PATH_MAX, */ +#include <string.h> /* strlen(3), */ +#include <assert.h> /* assert(3), */ +#include <time.h> /* time(2), localtime(3), */ +#include <stddef.h> /* offsetof(3), */ +#include <talloc.h> /* talloc*, */ +#include <uthash.h> /* ut*, UT*, HASH*, */ +#include <sys/queue.h> /* STAILQ_*, */ +#include <inttypes.h> /* PRI*, */ +#include <linux/auxvec.h> /* AT_*, */ + +#include "extension/care/care.h" +#include "extension/care/final.h" +#include "extension/care/archive.h" +#include "extension/extension.h" +#include "tracee/tracee.h" +#include "tracee/mem.h" +#include "execve/auxv.h" +#include "path/canon.h" +#include "path/path.h" +#include "path/binding.h" +#include "cli/note.h" + +/* Make uthash use talloc. */ +#undef uthash_malloc +#undef uthash_free +#define uthash_malloc(size) talloc_size(care, size) +#define uthash_free(pointer, size) TALLOC_FREE(pointer) + +/* Hash entry. */ +typedef struct Entry { + UT_hash_handle hh; + char *path; +} Entry; + +/** + * Add a copy of @value at the end if the given @list. All the newly + * talloc'ed elements (duplicated value, item, list head) are attached + * to the given @context. This function returns NULL if an error + * occurred, otherwise the newly talloc'ed item. + */ +Item *queue_item(TALLOC_CTX *context, List **list, const char *value) +{ + Item *item; + + if (*list == NULL) { + *list = talloc_zero(context, List); + if (*list == NULL) + return NULL; + + STAILQ_INIT(*list); + } + + item = talloc_zero(*list, Item); + if (item == NULL) + return NULL; + + item->load = talloc_strdup(item, value); + if (item->load == NULL) + return NULL; + + STAILQ_INSERT_TAIL(*list, item, link); + return item; +} + +/** + * Generate a valid archive @care->output from @care. + */ +static void generate_output_name(const Tracee *tracee, Care *care) +{ + struct tm *splitted_time; + time_t flat_time; + + flat_time = time(NULL); + splitted_time = localtime(&flat_time); + if (splitted_time == NULL) { + note(tracee, ERROR, INTERNAL, + "can't generate a valid output name from the current time, " + "please specify an ouput name explicitly"); + return; + } + + care->output = talloc_asprintf(care, "care-%02d%02d%02d%02d%02d%02d.%s", + splitted_time->tm_year - 100, splitted_time->tm_mon + 1, + splitted_time->tm_mday, splitted_time->tm_hour, + splitted_time->tm_min, splitted_time->tm_sec, +#if defined(CARE_BINARY_IS_PORTABLE) + "bin" +#else + "raw" +#endif + ); + if (care->output == NULL) { + note(tracee, ERROR, INTERNAL, + "can't generate a valid output name from the current time, " + "please specify an ouput name explicitly"); + return; + } +} + +/** + * Genereate @extension->config from @options. This function returns + * -1 if an error ocurred, otherwise 0. + */ +static int generate_care(Extension *extension, const Options *options) +{ + size_t suffix_length; + const char *cursor; + Tracee *tracee; + Item *item2; + Item *item; + Care *care; + + tracee = TRACEE(extension); + + extension->config = talloc_zero(extension, Care); + if (extension->config == NULL) + return -1; + care = extension->config; + + care->command = options->command; + care->ipc_are_volatile = !options->ignore_default_config; + + if (options->output != NULL) + care->output = talloc_strdup(care, options->output); + else + generate_output_name(tracee, care); + if (care->output == NULL) { + note(tracee, WARNING, INTERNAL, "can't get output name"); + return -1; + } + + care->initial_cwd = talloc_strdup(care, tracee->fs->cwd); + if (care->initial_cwd == NULL) { + note(tracee, WARNING, INTERNAL, "can't allocate cwd"); + return -1; + } + + care->archive = new_archive(care, tracee, care->output, &suffix_length); + if (care->archive == NULL) + return -1; + + cursor = strrchr(care->output, '/'); + if (cursor == NULL || strlen(cursor) == 1) + cursor = care->output; + else + cursor++; + + care->prefix = talloc_strndup(care, cursor, strlen(cursor) - suffix_length); + if (care->prefix == NULL) { + note(tracee, WARNING, INTERNAL, "can't allocate archive prefix"); + return -1; + } + + /* Copy & canonicalize volatile paths. */ + if (options->volatile_paths != NULL) { + char path[PATH_MAX]; + int status; + + STAILQ_FOREACH(item, options->volatile_paths, link) { + /* Initial state before canonicalization. */ + strcpy(path, "/"); + + status = canonicalize(tracee, (const char *) item->load, false, path, 0); + if (status < 0) + continue; + + /* Sanity check. */ + if (strcmp(path, "/") == 0) { + const char *string; + const char *name; + + name = talloc_get_name(item); + string = name == NULL || name[0] != '$' + ? talloc_asprintf(tracee->ctx, "'%s'", + (const char *) item->load) + : talloc_asprintf(tracee->ctx, "'%s' (%s)", + (const char *) item->load, name); + + note(tracee, WARNING, USER, + "path %s was declared volatile but it leads to '/', " + "as a consequence it will *not* be considered volatile.", + string); + continue; + } + + item2 = queue_item(care, &care->volatile_paths, path); + if (item2 == NULL) + continue; + + /* Preserve the non expanded form. */ + talloc_set_name_const(item2, talloc_get_name(item)); + + VERBOSE(tracee, 1, "volatile path: %s", (const char *) item2->load); + } + } + + /* Copy volatile env. variables. */ + if (options->volatile_envars != NULL) { + STAILQ_FOREACH(item, options->volatile_envars, link) { + item2 = queue_item(care, &care->volatile_envars, item->load); + if (item2 == NULL) + continue; + + VERBOSE(tracee, 1, "volatile envar: %s", (const char *) item2->load); + } + } + + /* Convert the limit from megabytes to bytes, as expected by + * handle_host_path(). */ + care->max_size = options->max_size * 1024 * 1024; + + /* handle_host_path() can now be safely used. */ + care->is_ready = true; + + talloc_set_destructor(care, finalize_care); + return 0; +} + +/** + * Add @path_ to the list of @care->concealed_accesses. This function + * does *not* check for duplicated entries. + */ +static void register_concealed_access(const Tracee *tracee, Care *care, const char *path_) +{ + char path[PATH_MAX]; + size_t length; + int status; + + length = strlen(path_); + if (length >= PATH_MAX) + return; + memcpy(path, path_, length + 1); + + /* It was a concealed access if, and only if, the path was + * part of a asymmetric binding. */ + status = substitute_binding(tracee, HOST, path); + if (status != 1) + return; + + /* Do not register accesses that would not succeed even if the + * path was revealed, i.e. the path does not exist at all. */ + status = access(path, F_OK); + if (status < 0) + return; + + queue_item(care, &care->concealed_accesses, path); + VERBOSE(tracee, 1, "concealed: %s", path); +} + +/** + * Archive @path if needed. + */ +static void handle_host_path(Extension *extension, const char *path) +{ + struct stat statl; + bool as_dentries; + char *location; + Tracee *tracee; + Entry *entry; + Care *care; + int status; + + care = talloc_get_type_abort(extension->config, Care); + tracee = TRACEE(extension); + + if (!care->is_ready) + return; + + /* Don't archive if the path was already seen before. + * This ensures the rootfs is re-created as it was + * before any file creation or modification. */ + HASH_FIND_STR(care->entries, path, entry); + if (entry != NULL) + return; + + switch (get_sysnum(tracee, ORIGINAL)) { + case PR_getdents: + case PR_getdents64: + /* Don't archive if the dentry was already seen + * before, it would be useless. */ + HASH_FIND_STR(care->dentries, path, entry); + if (entry != NULL) + return; + as_dentries = true; + break; + + default: + as_dentries = false; + break; + } + + entry = talloc_zero(care, Entry); + if (entry == NULL) { + note(tracee, WARNING, INTERNAL, "can't allocate entry for '%s'", path); + return; + } + + entry->path = talloc_strdup(entry, path); + if (entry->path == NULL) { + note(tracee, WARNING, INTERNAL, "can't allocate name for '%s'", path); + return; + } + + /* Remember this new entry. */ + if (as_dentries) + HASH_ADD_KEYPTR(hh, care->dentries, entry->path, strlen(entry->path), entry); + else + HASH_ADD_KEYPTR(hh, care->entries, entry->path, strlen(entry->path), entry); + + /* Don't use faccessat(2) here since it would require Linux >= + * 2.6.16 and Glibc >= 2.4, whereas CARE is supposed to work + * on any Linux 2.6 systems. */ + status = lstat(path, &statl); + if (status < 0) { + register_concealed_access(tracee, care, path); + return; + } + + /* FIFOs and Unix domain sockets should be volatile. */ + if (S_ISFIFO(statl.st_mode) || S_ISSOCK(statl.st_mode)) { + if (care->ipc_are_volatile) { + Item *item = queue_item(care, &care->volatile_paths, path); + if (item != NULL) + VERBOSE(tracee, 0, "volatile path: %s", path); + else + note(tracee, WARNING, USER, + "can't declare '%s' (fifo or socket) as volatile", path); + return; + } + else + note(tracee, WARNING, USER, + "'%1$s' might be explicitely declared volatile (-p %1$s)", path); + } + + /* Don't archive the content of dentries, this save a lot of + * space! */ + if (as_dentries) + statl.st_size = 0; + + if (care->volatile_paths != NULL) { + Item *item; + + STAILQ_FOREACH(item, care->volatile_paths, link) { + switch (compare_paths(item->load, path)) { + case PATHS_ARE_EQUAL: + /* It's a volatile path, archive it as + * empty to preserve its dentry. */ + statl.st_size = 0; + break; + + case PATH1_IS_PREFIX: + /* Don't archive it's a sub-part of a + * volatile path. */ + return; + + default: + continue; + } + break; + } + } + + if (care->max_size >= 0 && statl.st_size > care->max_size) { + note(tracee, WARNING, USER, + "file '%s' is archived with a null size since it is bigger than %" + PRIi64 "MB, you can specify an alternate limit with the option -m.", + path, care->max_size / 1024 / 1024); + statl.st_size = 0; + } + + /* Format the location within the archive. */ + location = NULL; + assert(path[0] == '/'); + location = talloc_asprintf(tracee->ctx, "%s/rootfs%s", care->prefix, path); + if (location == NULL) { + note(tracee, WARNING, INTERNAL, "can't allocate location for '%s'", path); + return; + } + + status = archive(tracee, care->archive, path, location, &statl); + if (status == 0) + VERBOSE(tracee, 1, "archived: %s", path); +} + +typedef struct { + uint32_t d_ino; + uint32_t next; + uint16_t size; + char name[]; +} Dirent32; + +typedef struct { + uint64_t d_ino; + uint64_t next; + uint16_t size; + char name[]; +} Dirent64; + +typedef struct { + uint64_t inode; + int64_t next; + uint16_t size; + uint8_t type; + char name[]; +} NewDirent; + +/** + * Archive all the entries returned by getdents syscalls. + */ +static void handle_getdents(Tracee *tracee, bool is_new_getdents) +{ + char component[PATH_MAX]; + char path[PATH_MAX]; + uint64_t offset; + int status; + + word_t result; + word_t buffer; + word_t fd; + + Dirent32 dirent32; + Dirent64 dirent64; + NewDirent new_dirent; + + result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + if ((int) result < 0) + return; + + fd = peek_reg(tracee, ORIGINAL, SYSARG_1); + buffer = peek_reg(tracee, ORIGINAL, SYSARG_2); + + offset = 0; + while (offset < result) { + word_t name_offset; + word_t address; + size_t size; + + address = buffer + offset; + + if (!is_new_getdents) { +#if defined(ARCH_X86_64) + const bool is_32bit = is_32on64_mode(tracee); +#else + const bool is_32bit = true; +#endif + if (is_32bit) { + name_offset = offsetof(Dirent32, name); + status = read_data(tracee, &dirent32, address, sizeof(dirent32)); + size = dirent32.size; + } + else { + name_offset = offsetof(Dirent64, name); + status = read_data(tracee, &dirent64, address, sizeof(dirent64)); + size = dirent64.size; + } + } else { + name_offset = offsetof(NewDirent, name); + status = read_data(tracee, &new_dirent, address, sizeof(new_dirent)); + size = new_dirent.size; + } + if (status < 0) { + note(tracee, WARNING, INTERNAL, "can't read dentry"); + break; + } + + status = read_string(tracee, component, address + name_offset, PATH_MAX); + if (status < 0 || status >= PATH_MAX) { + note(tracee, WARNING, INTERNAL, "can't read dentry" ); + goto next; + } + + /* Archive through the host_path notification. */ + strcpy(path, "/"); + translate_path(tracee, path, fd, component, false); + next: + offset += size; + } + + if (offset != result) + note(tracee, WARNING, INTERNAL, "dentry table out of sync."); +} + +/** + * Set AT_HWCAP to 0 to ensure no processor specific extensions will + * be used, for the sake of reproducibility across different CPUs. + * This function assumes the "argv, envp, auxv" stuff is pointed to by + * @tracee's stack pointer, as expected right after a successful call + * to execve(2). + */ +static int adjust_elf_auxv(Tracee *tracee) +{ + ElfAuxVector *vectors; + ElfAuxVector *vector; + word_t vectors_address; + + vectors_address = get_elf_aux_vectors_address(tracee); + if (vectors_address == 0) + return 0; + + vectors = fetch_elf_aux_vectors(tracee, vectors_address); + if (vectors == NULL) + return 0; + + for (vector = vectors; vector->type != AT_NULL; vector++) { + if (vector->type == AT_HWCAP) + vector->value = 0; + } + + push_elf_aux_vectors(tracee, vectors, vectors_address); + + return 0; +} + +/* List of syscalls handled by this extensions. */ +static FilteredSysnum filtered_sysnums[] = { + { PR_getdents, FILTER_SYSEXIT }, + { PR_getdents64, FILTER_SYSEXIT }, + FILTERED_SYSNUM_END, +}; + +/** + * Handler for this @extension. It is triggered each time an @event + * occurred. See ExtensionEvent for the meaning of @data1 and @data2. + */ +int care_callback(Extension *extension, ExtensionEvent event, + intptr_t data1, intptr_t data2 UNUSED) +{ + Tracee *tracee; + + switch (event) { + case INITIALIZATION: + extension->filtered_sysnums = filtered_sysnums; + return generate_care(extension, (Options *) data1); + + case NEW_STATUS: { + int status = (int) data1; + if (WIFEXITED(status)) { + Care *care = talloc_get_type_abort(extension->config, Care); + care->last_exit_status = WEXITSTATUS(status); + } + return 0; + } + + case HOST_PATH: + handle_host_path(extension, (const char *) data1); + return 0; + + case SYSCALL_EXIT_START: + tracee = TRACEE(extension); + + switch (get_sysnum(tracee, ORIGINAL)) { + case PR_getdents: + handle_getdents(tracee, false); + break; + + case PR_getdents64: + handle_getdents(tracee, true); + break; + + case PR_execve: { + word_t result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + + /* Note: this can be done only before PRoot pushes the + * load script into tracee's stack. */ + if ((int) result >= 0) + adjust_elf_auxv(tracee); + break; + } + + default: + break; + } + return 0; + + default: + return 0; + } +}
diff --git a/5.1.0/src/extension/care/care.h b/5.1.0/src/extension/care/care.h new file mode 100644 index 0000000..aae86a7 --- /dev/null +++ b/5.1.0/src/extension/care/care.h
@@ -0,0 +1,80 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 CARE_H +#define CARE_H + +#include <stdbool.h> +#include <sys/queue.h> /* STAILQ_*, */ + +#include "extension/care/archive.h" + +/* Generic item for a STAILQ list. */ +typedef struct item { + const void *load; + STAILQ_ENTRY(item) link; +} Item; + +typedef STAILQ_HEAD(list, item) List; + +/* CARE CLI configuration. */ +typedef struct { + const char *output; + char *const *command; + + List *concealed_paths; + List *revealed_paths; + List *volatile_paths; + List *volatile_envars; + + bool ignore_default_config; + + int max_size; +} Options; + +/* CARE internal configuration. */ +typedef struct { + struct Entry *entries; + struct Entry *dentries; + + char *const *command; + List *volatile_paths; + List *volatile_envars; + List *concealed_accesses; + + const char *prefix; + const char *output; + const char *initial_cwd; + bool ipc_are_volatile; + + Archive *archive; + int64_t max_size; + + int last_exit_status; + + bool is_ready; +} Care; + +extern Item *queue_item(TALLOC_CTX *context, List **list, const char *value); + +#endif /* CARE_H */ +
diff --git a/5.1.0/src/extension/care/extract.c b/5.1.0/src/extension/care/extract.c new file mode 100644 index 0000000..7f3c326 --- /dev/null +++ b/5.1.0/src/extension/care/extract.c
@@ -0,0 +1,315 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* open(2), fstat(2), lseek(2), */ +#include <sys/stat.h> /* open(2), fstat(2), */ +#include <sys/stat.h> /* open(2), */ +#include <fcntl.h> /* open(2), */ +#include <stdint.h> /* *int*_t, *INT*_MAX, */ +#include <unistd.h> /* fstat(2), read(2), lseek(2), */ +#include <sys/mman.h> /* mmap(2), MAP_*, */ +#include <stdbool.h> /* bool, true, false, */ +#include <assert.h> /* assert(3), */ +#include <errno.h> /* errno(3), */ +#include <string.h> /* strerror(3), */ +#include <inttypes.h> /* PRI*, */ +#include <endian.h> /* be64toh(3), */ +#include <archive.h> /* archive_*(3), */ +#include <archive_entry.h> /* archive_entry*(3), */ + +#include "extension/care/extract.h" +#include "cli/note.h" + +/** + * Extract the given @archive into the current working directory. + * This function returns -1 if an error occured, otherwise 0. + */ +static int extract_archive(struct archive *archive) +{ + struct archive_entry *entry; + int result = 0; + int status; + + int flags = ARCHIVE_EXTRACT_PERM + | ARCHIVE_EXTRACT_TIME + | ARCHIVE_EXTRACT_ACL + | ARCHIVE_EXTRACT_FFLAGS + | ARCHIVE_EXTRACT_XATTR; + + /* Avoid spurious warnings. One should test for the CAP_CHOWN + * capability instead but libarchive only does this test: */ + if (geteuid() == 0) + flags |= ARCHIVE_EXTRACT_OWNER; + + while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + status = archive_read_extract(archive, entry, flags); + switch (status) { + case ARCHIVE_OK: + note(NULL, INFO, USER, "extracted: %s", archive_entry_pathname(entry)); + break; + + default: + result = -1; + note(NULL, ERROR, INTERNAL, "%s: %s", + archive_error_string(archive), + strerror(archive_errno(archive))); + break; + } + } + + return result; +} + +/* Data used by archive_[open/read/close] callbacks. */ +typedef struct +{ + uint8_t buffer[4096]; + const char *path; + size_t size; + int fd; +} CallbackData; + +/** + * This callback is invoked by archive_open(). It returns ARCHIVE_OK + * if the underlying file or data source is successfully opened. If + * the open fails, it calls archive_set_error() to register an error + * code and message and returns ARCHIVE_FATAL. + * + * -- man 3 archive_read_open. + */ +static int open_callback(struct archive *archive, void *data_) +{ + CallbackData *data = talloc_get_type_abort(data_, CallbackData); + AutoExtractInfo info; + struct stat statf; + off_t offset; + int status; + + /* Note: data->fd will be closed by close_callback(). */ + data->fd = open(data->path, O_RDONLY); + if (data->fd < 0) { + archive_set_error(archive, errno, "can't open archive"); + return ARCHIVE_FATAL; + } + + status = fstat(data->fd, &statf); + if (status < 0) { + archive_set_error(archive, errno, "can't stat archive"); + return ARCHIVE_FATAL; + } + + /* Assume it's a regular archive if it physically can't be a + * self-extracting one. */ + if (statf.st_size < (off_t) sizeof(AutoExtractInfo)) + return ARCHIVE_OK; + + offset = lseek(data->fd, statf.st_size - sizeof(AutoExtractInfo), SEEK_SET); + if (offset == (off_t) -1) { + archive_set_error(archive, errno, "can't seek in archive"); + return ARCHIVE_FATAL; + } + + status = read(data->fd, &info, sizeof(AutoExtractInfo)); + if (status < 0) { + archive_set_error(archive, errno, "can't read archive"); + return ARCHIVE_FATAL; + } + + if ( status == sizeof(AutoExtractInfo) + && strcmp(info.signature, AUTOEXTRACT_SIGNATURE) == 0) { + /* This is a self-extracting archive, retrive it's + * offset and size. */ + + data->size = be64toh(info.size); + offset = statf.st_size - data->size - sizeof(AutoExtractInfo); + + note(NULL, INFO, USER, + "archive found: offset = %" PRIu64 ", size = %" PRIu64 "", + (uint64_t) offset, data->size); + } + else { + /* This is not a self-extracting archive, assume it's + * a regular one... */ + offset = 0; + + /* ... unless a self-extracting archive really was + * expected. */ + if (strcmp(data->path, "/proc/self/exe") == 0) + return ARCHIVE_FATAL; + } + + offset = lseek(data->fd, offset, SEEK_SET); + if (offset == (off_t) -1) { + archive_set_error(archive, errno, "can't seek in archive"); + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +/** + * This callback is invoked whenever the library requires raw bytes + * from the archive. The read callback reads data into a buffer, set + * the @buffer argument to point to the available data, and return a + * count of the number of bytes available. The library will invoke + * the read callback again only after it has consumed this data. The + * library imposes no constraints on the size of the data blocks + * returned. On end-of-file, the read callback returns zero. On + * error, the read callback should invoke archive_set_error() to + * register an error code and message and returns -1. + * + * -- man 3 archive_read_open. + */ +static ssize_t read_callback(struct archive *archive, void *data_, const void **buffer) +{ + CallbackData *data = talloc_get_type_abort(data_, CallbackData); + ssize_t size; + + size = read(data->fd, data->buffer, sizeof(data->buffer)); + if (size < 0) { + archive_set_error(archive, errno, "can't read archive"); + return -1; + } + + *buffer = data->buffer; + return size; +} + +/** + * This callback is invoked by archive_close() when the archive + * processing is complete. The callback returns ARCHIVE_OK on + * success. On failure, the callback invokes archive_set_error() to + * register an error code and message and returns ARCHIVE_FATAL. + * + * -- man 3 archive_read_open + */ +static int close_callback(struct archive *archive, void *data_) +{ + CallbackData *data = talloc_get_type_abort(data_, CallbackData); + int status; + + status = close(data->fd); + if (status < 0) { + archive_set_error(archive, errno, "can't close archive"); + return ARCHIVE_WARN; + } + + return ARCHIVE_OK; +} + +/** + * Extract the archive stored at the given @path. This function + * returns -1 if an error occurred, otherwise 0. + */ +int extract_archive_from_file(const char *path) +{ + struct archive *archive = NULL; + CallbackData *data = NULL; + int status2; + int status; + + archive = archive_read_new(); + if (archive == NULL) { + note(NULL, ERROR, INTERNAL, "can't initialize archive structure"); + status = -1; + goto end; + } + + status = archive_read_support_format_cpio(archive); + if (status != ARCHIVE_OK) { + note(NULL, ERROR, INTERNAL, "can't set archive format: %s", + archive_error_string(archive)); + status = -1; + goto end; + } + + status = archive_read_support_format_gnutar(archive); + if (status != ARCHIVE_OK) { + note(NULL, ERROR, INTERNAL, "can't set archive format: %s", + archive_error_string(archive)); + status = -1; + goto end; + } + + status = archive_read_support_filter_gzip(archive); + if (status != ARCHIVE_OK) { + note(NULL, ERROR, INTERNAL, "can't add archive filter: %s", + archive_error_string(archive)); + status = -1; + goto end; + } + + status = archive_read_support_filter_lzop(archive); + if (status != ARCHIVE_OK) { + note(NULL, ERROR, INTERNAL, "can't add archive filter: %s", + archive_error_string(archive)); + status = -1; + goto end; + } + + data = talloc_zero(NULL, CallbackData); + if (data == NULL) { + note(NULL, ERROR, INTERNAL, "can't allocate callback data"); + status = -1; + goto end; + + } + + data->path = talloc_strdup(data, path); + if (data->path == NULL) { + note(NULL, ERROR, INTERNAL, "can't allocate callback data path"); + status = -1; + goto end; + + } + + status = archive_read_open(archive, data, open_callback, read_callback, close_callback); + if (status != ARCHIVE_OK) { + /* Don't complain if no error message were registered, + * ie. when testing for a self-extracting archive. */ + if (archive_error_string(archive) != NULL) + note(NULL, ERROR, INTERNAL, "can't read archive: %s", + archive_error_string(archive)); + status = -1; + goto end; + } + + status = extract_archive(archive); +end: + if (archive != NULL) { + status2 = archive_read_close(archive); + if (status2 != ARCHIVE_OK) { + note(NULL, WARNING, INTERNAL, "can't close archive: %s", + archive_error_string(archive)); + } + + status2 = archive_read_free(archive); + if (status2 != ARCHIVE_OK) { + note(NULL, WARNING, INTERNAL, "can't free archive: %s", + archive_error_string(archive)); + } + } + + TALLOC_FREE(data); + + return status; +}
diff --git a/5.1.0/src/extension/care/extract.h b/5.1.0/src/extension/care/extract.h new file mode 100644 index 0000000..b79f624 --- /dev/null +++ b/5.1.0/src/extension/care/extract.h
@@ -0,0 +1,38 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 EXTRACT_H +#define EXTRACT_H + +#include <stdint.h> +#include "attribute.h" + +#define AUTOEXTRACT_SIGNATURE "I_LOVE_PIZZA" + +typedef struct { + char signature[sizeof(AUTOEXTRACT_SIGNATURE)]; + uint64_t size; +} PACKED AutoExtractInfo; + +extern int WEAK extract_archive_from_file(const char *path); + +#endif /* EXTRACT_H */
diff --git a/5.1.0/src/extension/care/final.c b/5.1.0/src/extension/care/final.c new file mode 100644 index 0000000..0a5f9fd --- /dev/null +++ b/5.1.0/src/extension/care/final.c
@@ -0,0 +1,476 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <unistd.h> /* lstat(2), readlink(2), getpid(2), wirte(2), lseek(2), get*id(2), */ +#include <sys/types.h> /* get*id(2), */ +#include <sys/stat.h> /* struct stat, fchmod(2), */ +#include <linux/limits.h> /* PATH_MAX, */ +#include <sys/utsname.h> /* uname(2), */ +#include <stdio.h> /* fprintf(3), fclose(3), */ +#include <errno.h> /* errno, ENAMETOOLONG, */ +#include <string.h> /* strcpy(3), */ +#include <endian.h> /* htobe64(3), */ +#include <assert.h> /* assert(3), */ + +#include "extension/care/final.h" +#include "extension/care/care.h" +#include "extension/care/extract.h" +#include "execve/ldso.h" +#include "path/path.h" +#include "path/temp.h" +#include "cli/note.h" + +/** + * Find in @care->volatile_envars the given @envar (format + * "name=value"). This function returns the name of the variable if + * found (format "name"), NULL otherwise. + */ +static const char *find_volatile_envar(const Care *care, const char *envar) +{ + const Item *volatile_envar; + + if (care->volatile_envars == NULL) + return NULL; + + STAILQ_FOREACH(volatile_envar, care->volatile_envars, link) { + if (is_env_name(envar, volatile_envar->load)) + return volatile_envar->load; + } + + return NULL; +} + +extern char **environ; + +/** + * Archive in @care->archive the content of @file with the given + * @name, then close it. This function returns < 0 if an error + * occured, otherwise 0. Note: this function is called in @care's + * destructor. + */ +static int archive_close_file(const Care *care, FILE *file, const char *name) +{ + char path[PATH_MAX]; + struct stat statl; + char *location; + int status; + int fd; + + /* Ensure everything is written into the file before archiving + * it. */ + fflush(file); + + fd = fileno(file); + + status = fstat(fd, &statl); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't get '%s' status", name); + goto end; + } + + location = talloc_asprintf(care, "%s/%s", care->prefix, name); + if (location == NULL) { + note(NULL, ERROR, INTERNAL, "can't allocate location for '%s'", name); + status = -1; + goto end; + } + + status = readlink_proc_pid_fd(getpid(), fd, path); + if (status < 0) { + note(NULL, ERROR, INTERNAL, "can't readlink(/proc/self/fd/%d)", fd); + goto end; + } + + status = archive(NULL, care->archive, path, location, &statl); +end: + (void) fclose(file); + return status; +} + +/** + * Return a copy -- attached to @context -- of @input with all ' + * (single quote) characters escaped. + */ +static const char *escape_quote(TALLOC_CTX *context, const char *input) +{ + char *output; + size_t length; + size_t i; + + output = talloc_strdup(context, ""); + if (output == NULL) + return NULL; + + length = strlen(input); + for (i = 0; i < length; i++) { + char buffer[2] = { input[i], '\0' }; + + if (buffer[0] == '\'') + output = talloc_strdup_append_buffer(output, "'\\''"); + else + output = talloc_strdup_append_buffer(output, buffer); + if (output == NULL) + return NULL; + } + + return output; +} + +/* Helpers for archive_* functions. */ +#define N(format, ...) \ + do { \ + if (fprintf(file, format "\n", ##__VA_ARGS__) < 0) { \ + note(NULL, ERROR, INTERNAL, "can't write file"); \ + (void) fclose(file); \ + return -1; \ + } \ + } while (0) + +#define C(format, ...) N(format " \\", ##__VA_ARGS__) + +/** + * Archive the "re-execute.sh" file, according to the given @care. + * This function returns < 0 if an error occured, 0 otherwise. Note: + * this function is called in @care's destructor. + */ +static int archive_re_execute_sh(Care *care) +{ + struct utsname utsname; + const Item *item; + FILE *file; + int status; + int i; + + file = open_temp_file(NULL, "care"); + if (file == NULL) { + note(NULL, ERROR, INTERNAL, "can't create temporary file for 're-execute.sh'"); + return -1; + } + + status = fchmod(fileno(file), 0755); + if (status < 0) + note(NULL, WARNING, SYSTEM, "can't make 're-execute.sh' executable"); + + N("#! /bin/sh"); + N(""); + N("export XAUTHORITY=\"${XAUTHORITY:-$HOME/.Xauthority}\""); + N("export ICEAUTHORITY=\"${ICEAUTHORITY:-$HOME/.ICEauthority}\""); + N(""); + + N("nbargs=$#"); + C("[ $nbargs -ne 0 ] || set --"); + for (i = 0; care->command != NULL && care->command[i] != NULL; i++) + C("'%s'", care->command[i]); + N(""); + + N("PROOT=\"${PROOT-$(dirname $0)/proot}\""); + N(""); + + N("if [ ! -e ${PROOT} ]; then"); + N(" PROOT=$(which proot)"); + N("fi"); + N(""); + + N("if [ -z ${PROOT} ]; then"); + N(" echo '**********************************************************************'"); + N(" echo '\"proot\" command not found, please get it from http://proot.me'"); + N(" echo '**********************************************************************'"); + N(" exit 1"); + N("fi"); + N(""); + + N("if [ x$PROOT_NO_SECCOMP != x ]; then"); + N(" PROOT_NO_SECCOMP=\"PROOT_NO_SECCOMP=$PROOT_NO_SECCOMP\""); + N("fi"); + N(""); + + C("env --ignore-environment"); + C("PROOT_IGNORE_MISSING_BINDINGS=1"); + C("$PROOT_NO_SECCOMP"); + + for (i = 0; environ[i] != NULL; i++) { + const char *volatile_envar; + + volatile_envar = find_volatile_envar(care, environ[i]); + if (volatile_envar != NULL) + C("'%1$s'=\"$%1$s\" ", volatile_envar); + else { + const char *string = escape_quote(care, environ[i]); + C("'%s' ", string ?: environ[i]); + } + } + + C("\"${PROOT-$(dirname $0)/proot}\""); + + if (care->volatile_paths != NULL) { + /* If a volatile path is relative to $HOME, use an + * asymmetric binding. For instance: + * + * -b $HOME/.Xauthority:/home/user/.Xauthority + * + * where "/home/user" was the $HOME during the + * original execution. */ + STAILQ_FOREACH(item, care->volatile_paths, link) { + const char *name = talloc_get_name(item); + if (name[0] == '$') + C("-b \"%s:%s\" ", name, (char *) item->load); + else + C("-b \"%s\" ", (char *) item->load); + } + } + + status = uname(&utsname); + if (status < 0) { + note(NULL, WARNING, SYSTEM, "can't get kernel release"); + C("-k 3.17.0"); + } + else { + C("-k '\\%s\\%s\\%s\\%s\\%s\\%s\\0\\' ", + utsname.sysname, + utsname.nodename, + utsname.release, + utsname.version, + utsname.machine, + utsname.domainname); + } + + C("-i %d:%d", getuid(), getgid()); + C("-w '%s' ", care->initial_cwd); + C("-r \"$(dirname $0)/rootfs\""); + + /* In case the program retrieves its DSOs from /proc/self/maps + * (eg. VLC). */ + C("-b \"$(dirname $0)/rootfs\""); + N("${1+\"$@\"}"); + N(""); + + N("status=$?"); + N("if [ $status -ne %d ] && [ $nbargs -eq 0 ]; then", care->last_exit_status); + N("echo \"care: The reproduced execution didn't return the same exit status as the\""); + N("echo \"care: original execution. If it is unexpected, please report this bug\""); + N("echo \"care: to CARE/PRoot developers:\""); + N("echo \"care: * mailing list: reproducible@googlegroups.com; or\""); + N("echo \"care: * forum: https://groups.google.com/forum/?fromgroups#!forum/reproducible; or\""); + N("echo \"care: * issue tracker: https://github.com/cedric-vincent/PRoot/issues/\""); + N("fi"); + N(""); + N("exit $status"); + + return archive_close_file(care, file, "re-execute.sh"); +} + +/** + * Archive the "concealed-accesses.txt" file in @care->archive, + * according to the content of @care->concealed_accesses. This + * function returns < 0 if an error occured, 0 otherwise. Note: this + * function is called in @care's destructor. + */ +static int archive_concealed_accesses_txt(const Care *care) +{ + const Item *item; + FILE *file; + + if (care->concealed_accesses == NULL) + return 0; + + file = open_temp_file(NULL, "care"); + if (file == NULL) { + note(NULL, WARNING, INTERNAL, + "can't create temporary file for 'concealed-accesses.txt'"); + return -1; + } + + STAILQ_FOREACH(item, care->concealed_accesses, link) + N("%s", (char *) item->load); + + return archive_close_file(care, file, "concealed-accesses.txt"); +} + +/** + * Archive the "README.txt" file in @care->archive. This function + * returns < 0 if an error occured, 0 otherwise. Note: this function + * is called in @care's destructor. + */ +static int archive_readme_txt(const Care *care) +{ + FILE *file; + + file = open_temp_file(NULL, "care"); + if (file == NULL) { + note(NULL, WARNING, INTERNAL, "can't create temporary file for 'README.txt'"); + return -1; + } + + N("This archive was created with CARE: http://reproducible.io. It contains:"); + N(""); + N("re-execute.sh"); + N(" start the re-execution of the initial command as originally"); + N(" specified. It is also possible to specify an alternate command."); + N(" For example, assuming gcc was archived, it can be re-invoked"); + N(" differently:"); + N(""); + N(" $ ./re-execute.sh gcc --version"); + N(" gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"); + N(""); + N(" $ echo 'int main(void) { return puts(\"OK\"); }' > rootfs/foo.c"); + N(" $ ./re-execute.sh gcc -Wall /foo.c"); + N(" $ foo.c: In function \"main\":"); + N(" $ foo.c:1:1: warning: implicit declaration of function \"puts\""); + N(""); + N("rootfs/"); + N(" directory where all the files used during the original execution"); + N(" were archived, they will be required for the reproduced execution."); + N(""); + N("proot"); + N(" virtualization tool invoked by re-execute.sh to confine the"); + N(" reproduced execution into the rootfs. It also emulates the"); + N(" missing kernel features if needed."); + N(""); + N("concealed-accesses.txt"); + N(" list of accessed paths that were concealed during the original"); + N(" execution. Its main purpose is to know what are the paths that"); + N(" should be revealed if the the original execution didn't go as"); + N(" expected. It is absolutely useless for the reproduced execution."); + N(""); + + return archive_close_file(care, file, "README.txt"); +} + +#undef N +#undef C + +#if !defined(CARE_BINARY_IS_PORTABLE) +static int archive_myself(const Care *care) UNUSED; +#endif + +/** + * Archive the content pointed to by "/proc/self/exe" in + * "@care->archive:@care->prefix/proot". Note: this function is + * called in @care's destructor. + */ +static int archive_myself(const Care *care) +{ + char path[PATH_MAX]; + struct stat statl; + char *location; + int status; + + status = readlink("/proc/self/exe", path, PATH_MAX); + if (status >= PATH_MAX) { + status = -1; + errno = ENAMETOOLONG; + } + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't readlink '/proc/self/exe'"); + return status; + } + path[status] = '\0'; + + status = lstat(path, &statl); + if (status < 0) { + note(NULL, ERROR, INTERNAL, "can't lstat '%s'", path); + return status; + } + + location = talloc_asprintf(care, "%s/proot", care->prefix); + if (location == NULL) { + note(NULL, ERROR, INTERNAL, "can't allocate location for 'proot'"); + return -1; + } + + return archive(NULL, care->archive, path, location, &statl); +} + +/** + * Archive "re-execute.sh" & "proot" from @care. This function + * always returns 0. Note: this is a Talloc destructor. + */ +int finalize_care(Care *care) +{ + char *extractor; + int status; + + /* Generate & archive the "re-execute.sh" script. */ + status = archive_re_execute_sh(care); + if (status < 0) + note(NULL, WARNING, INTERNAL, "can't archive 're-execute.sh'"); + + /* Generate & archive the "concealed-accesses.txt" file. */ + status = archive_concealed_accesses_txt(care); + if (status < 0) + note(NULL, WARNING, INTERNAL, "can't archive 'concealed-accesses.txt'"); + + /* Generate & archive the "README.txt" file. */ + status = archive_readme_txt(care); + if (status < 0) + note(NULL, WARNING, INTERNAL, "can't archive 'README.txt'"); + +#if defined(CARE_BINARY_IS_PORTABLE) + /* Archive "care" as "proot", these are the same binary. */ + status = archive_myself(care); + if (status < 0) + note(NULL, WARNING, INTERNAL, "can't archive 'proot'"); +#endif + + finalize_archive(care->archive); + + /* Append self/raw extracting information if needed. */ + if (care->archive->fd >= 0 && care->archive->offset > 0) { + AutoExtractInfo info; + off_t position; + + strcpy(info.signature, AUTOEXTRACT_SIGNATURE); + + /* Compute the size of the archive. */ + position = lseek(care->archive->fd, 0, SEEK_CUR); + assert(position > care->archive->offset); + info.size = htobe64(position - care->archive->offset); + + status = write(care->archive->fd, &info, sizeof(info)); + if (status != sizeof(info)) + note(NULL, WARNING, SYSTEM, "can't write extracting information"); + + (void) close(care->archive->fd); + care->archive->fd = -1; + + if (care->archive->offset == strlen("RAW")) + extractor = talloc_asprintf(care, "`care -x %s`", care->output); + else + extractor = talloc_asprintf(care, "`%2$s%1$s` or `care -x %1$s`", + care->output, care->output[0] == '/' ? "" : "./"); + } + else if (care->output[strlen(care->output) - 1] != '/') + extractor = talloc_asprintf(care, "`care -x %s`", care->output); + else + extractor = NULL; + + note(NULL, INFO, USER, + "----------------------------------------------------------------------"); + note(NULL, INFO, USER, "Hints:"); + note(NULL, INFO, USER, + " - search for \"conceal\" in `care -h` if the execution didn't go as expected."); + + if (extractor != NULL) + note(NULL, INFO, USER, " - run %s to extract the output archive correctly.", extractor); + + return 0; +}
diff --git a/5.1.0/src/extension/care/final.h b/5.1.0/src/extension/care/final.h new file mode 100644 index 0000000..9ea1bbb --- /dev/null +++ b/5.1.0/src/extension/care/final.h
@@ -0,0 +1,30 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 CARE_FINAL_H +#define CARE_FINAL_H + +#include "care.h" + +extern int finalize_care(Care *care); + +#endif /* CARE_FINAL_H */
diff --git a/5.1.0/src/extension/extension.c b/5.1.0/src/extension/extension.c new file mode 100644 index 0000000..997104b --- /dev/null +++ b/5.1.0/src/extension/extension.c
@@ -0,0 +1,150 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* assert(3), */ +#include <talloc.h> /* talloc_*, */ +#include <sys/queue.h> /* LIST_*, */ +#include <strings.h> /* bzero(3), */ + +#include "extension/extension.h" +#include "cli/note.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) { + note(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; + } + + 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, word_t clone_flags) +{ + Extension *parent_extension; + Extension *child_extension; + int status; + + if (parent->extensions == NULL) + return; + + /* Sanity check. */ + assert(child->extensions == NULL || clone_flags == CLONE_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, clone_flags); + + /* Not inheritable. */ + if (status < 0) + continue; + + /* Inheritable... */ + child_extension = new_extension(child, parent_extension->callback); + if (child_extension == NULL) { + note(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, clone_flags); + } + } +}
diff --git a/5.1.0/src/extension/extension.h b/5.1.0/src/extension/extension.h new file mode 100644 index 0000000..3edafa2 --- /dev/null +++ b/5.1.0/src/extension/extension.h
@@ -0,0 +1,183 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/queue.h> /* LIST_, */ +#include <stdint.h> /* intptr_t, */ +#include <stdbool.h> /* 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 + * cancels the syscall and 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 cancels the syscall and 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" is the clone(2) + * flags (CLONE_RECONF for 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 "(word_t) data2" is the clone(2) + * flags (CLONE_RECONF for sub-reconfiguration). For instance + * the extension for the child could use a configuration + * different from the parent's configuration. */ + INHERIT_CHILD, + + /* The tracee enters a "chained" syscall, that is, an + * unrequested syscall inserted by PRoot after an actual + * syscall. If the extension returns < 0, then PRoot cancels + * the syscall and reports this errno to the tracee. */ + SYSCALL_CHAINED_ENTER, + + /* The tracee exists a "chained" syscall, that is, an + * unrequested syscall inserted by PRoot after an actual + * syscall. */ + SYSCALL_CHAINED_EXIT, + + /* 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; + +#define CLONE_RECONF ((word_t) -1) + +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 sysnum handled by this extension. */ + const FilteredSysnum *filtered_sysnums; + + /* 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, word_t clone_flags); + +/** + * 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); +extern int care_callback(Extension *extension, ExtensionEvent event, intptr_t d1, intptr_t d2); + +#endif /* EXTENSION_H */
diff --git a/5.1.0/src/extension/fake_id0/fake_id0.c b/5.1.0/src/extension/fake_id0/fake_id0.c new file mode 100644 index 0000000..346f4dc --- /dev/null +++ b/5.1.0/src/extension/fake_id0/fake_id0.c
@@ -0,0 +1,868 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* assert(3), */ +#include <stdint.h> /* intptr_t, */ +#include <errno.h> /* E*, */ +#include <sys/stat.h> /* chmod(2), stat(2) */ +#include <sys/types.h> /* uid_t, gid_t, get*id(2), */ +#include <unistd.h> /* get*id(2), */ +#include <sys/ptrace.h> /* linux.git:c0a3a20b */ +#include <linux/audit.h> /* AUDIT_ARCH_*, */ +#include <string.h> /* memcpy(3), */ +#include <stdlib.h> /* strtol(3), */ +#include <linux/auxvec.h>/* AT_, */ + +#include "extension/extension.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "syscall/seccomp.h" +#include "tracee/tracee.h" +#include "tracee/abi.h" +#include "tracee/mem.h" +#include "execve/auxv.h" +#include "path/binding.h" +#include "arch.h" + +typedef struct { + uid_t ruid; + uid_t euid; + uid_t suid; + uid_t fsuid; + + gid_t rgid; + gid_t egid; + gid_t sgid; + gid_t fsgid; +} Config; + +typedef struct { + char *path; + mode_t mode; +} ModifiedNode; + +/* List of syscalls handled by this extensions. */ +static FilteredSysnum filtered_sysnums[] = { + { PR_capset, FILTER_SYSEXIT }, + { PR_chmod, FILTER_SYSEXIT }, + { PR_chown, FILTER_SYSEXIT }, + { PR_chown32, FILTER_SYSEXIT }, + { PR_chroot, FILTER_SYSEXIT }, + { PR_execve, FILTER_SYSEXIT }, + { PR_fchmod, FILTER_SYSEXIT }, + { PR_fchmodat, FILTER_SYSEXIT }, + { PR_fchown, FILTER_SYSEXIT }, + { PR_fchown32, FILTER_SYSEXIT }, + { PR_fchownat, FILTER_SYSEXIT }, + { PR_fstat, FILTER_SYSEXIT }, + { PR_fstat, FILTER_SYSEXIT }, + { PR_fstat64, FILTER_SYSEXIT }, + { PR_fstatat64, FILTER_SYSEXIT }, + { PR_getegid, FILTER_SYSEXIT }, + { PR_getegid32, FILTER_SYSEXIT }, + { PR_geteuid, FILTER_SYSEXIT }, + { PR_geteuid32, FILTER_SYSEXIT }, + { PR_getgid, FILTER_SYSEXIT }, + { PR_getgid32, FILTER_SYSEXIT }, + { PR_getgroups, FILTER_SYSEXIT }, + { PR_getgroups32, FILTER_SYSEXIT }, + { PR_getresgid, FILTER_SYSEXIT }, + { PR_getresgid32, FILTER_SYSEXIT }, + { PR_getresuid, FILTER_SYSEXIT }, + { PR_getresuid32, FILTER_SYSEXIT }, + { PR_getuid, FILTER_SYSEXIT }, + { PR_getuid32, FILTER_SYSEXIT }, + { PR_lchown, FILTER_SYSEXIT }, + { PR_lchown32, FILTER_SYSEXIT }, + { PR_lstat, FILTER_SYSEXIT }, + { PR_lstat64, FILTER_SYSEXIT }, + { PR_mknod, FILTER_SYSEXIT }, + { PR_mknodat, FILTER_SYSEXIT }, + { PR_newfstatat, FILTER_SYSEXIT }, + { PR_oldlstat, FILTER_SYSEXIT }, + { PR_oldstat, FILTER_SYSEXIT }, + { PR_setfsgid, FILTER_SYSEXIT }, + { PR_setfsgid32, FILTER_SYSEXIT }, + { PR_setfsuid, FILTER_SYSEXIT }, + { PR_setfsuid32, FILTER_SYSEXIT }, + { PR_setgid, FILTER_SYSEXIT }, + { PR_setgid32, FILTER_SYSEXIT }, + { PR_setgroups, FILTER_SYSEXIT }, + { PR_setgroups32, FILTER_SYSEXIT }, + { PR_setregid, FILTER_SYSEXIT }, + { PR_setregid32, FILTER_SYSEXIT }, + { PR_setreuid, FILTER_SYSEXIT }, + { PR_setreuid32, FILTER_SYSEXIT }, + { PR_setresgid, FILTER_SYSEXIT }, + { PR_setresgid32, FILTER_SYSEXIT }, + { PR_setresuid, FILTER_SYSEXIT }, + { PR_setresuid32, FILTER_SYSEXIT }, + { PR_setuid, FILTER_SYSEXIT }, + { PR_setuid32, FILTER_SYSEXIT }, + { PR_setxattr, FILTER_SYSEXIT }, + { PR_setdomainname, FILTER_SYSEXIT }, + { PR_sethostname, FILTER_SYSEXIT }, + { PR_lsetxattr, FILTER_SYSEXIT }, + { PR_fsetxattr, FILTER_SYSEXIT }, + { PR_stat, FILTER_SYSEXIT }, + { PR_stat64, FILTER_SYSEXIT }, + { PR_statfs, FILTER_SYSEXIT }, + { PR_statfs64, FILTER_SYSEXIT }, + FILTERED_SYSNUM_END, +}; + +/** + * 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; +} + +/** + * Force permissions of @path to "rwx" during the path translation of + * current @tracee's syscall, in order to simulate CAP_DAC_OVERRIDE. + * The original permissions are restored through talloc destructors. + * See canonicalize() for the meaning of @is_final. + */ +static void override_permissions(const Tracee *tracee, const char *path, bool is_final) +{ + ModifiedNode *node; + struct stat perms; + mode_t new_mode; + int status; + + /* Get the meta-data */ + status = stat(path, &perms); + if (status < 0) + return; + + /* 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; + + node = talloc_zero(tracee->ctx, ModifiedNode); + if (node == NULL) + return; + + if (!is_final) { + /* Restore the previous mode of any non final components. */ + node->mode = perms.st_mode; + } + else { + switch (get_sysnum(tracee, ORIGINAL)) { + /* 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; + + /* 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; + } + + /* The mode restoration works because Talloc destructors are + * called in reverse order. */ + talloc_set_destructor(node, restore_mode); + + (void) chmod(path, new_mode); + + return; +} + +/** + * Adjust current @tracee's syscall parameters according to @config. + * This function always returns 0. + */ +static int handle_sysenter_end(Tracee *tracee, const Config *config) +{ + word_t sysnum; + + sysnum = get_sysnum(tracee, ORIGINAL); + switch (sysnum) { + case PR_setuid: + case PR_setuid32: + case PR_setgid: + case PR_setgid32: + case PR_setreuid: + case PR_setreuid32: + case PR_setregid: + case PR_setregid32: + case PR_setresuid: + case PR_setresuid32: + case PR_setresgid: + case PR_setresgid32: + case PR_setfsuid: + case PR_setfsuid32: + case PR_setfsgid: + case PR_setfsgid32: + /* These syscalls are fully emulated. */ + set_sysnum(tracee, PR_void); + return 0; + + case PR_chown: + case PR_chown32: + case PR_lchown: + case PR_lchown32: + case PR_fchown: + case PR_fchown32: + case PR_fchownat: { + Reg uid_sysarg; + Reg gid_sysarg; + uid_t uid; + gid_t gid; + + if (sysnum == PR_fchownat) { + uid_sysarg = SYSARG_3; + gid_sysarg = SYSARG_4; + } + else { + uid_sysarg = SYSARG_2; + gid_sysarg = SYSARG_3; + } + + uid = peek_reg(tracee, ORIGINAL, uid_sysarg); + gid = peek_reg(tracee, ORIGINAL, gid_sysarg); + + /* Swap actual and emulated ids to get a chance of + * success. */ + if (uid == config->ruid) + poke_reg(tracee, uid_sysarg, getuid()); + if (gid == config->rgid) + poke_reg(tracee, gid_sysarg, getgid()); + + return 0; + } + + case PR_setgroups: + case PR_setgroups32: + case PR_getgroups: + case PR_getgroups32: + /* TODO */ + + default: + return 0; + } + + /* Never reached */ + assert(0); + return 0; + +} + +/** + * Copy config->@field to the tracee's memory location pointed to by @sysarg. + */ +#define POKE_MEM_ID(sysarg, field) do { \ + poke_uint16(tracee, peek_reg(tracee, ORIGINAL, sysarg), config->field); \ + if (errno != 0) \ + return -errno; \ +} while (0) + +/** + * Emulate setuid(2) and setgid(2). + */ +#define SETXID(id) do { \ + id ## _t id = peek_reg(tracee, ORIGINAL, SYSARG_1); \ + bool allowed; \ + \ + /* "EPERM: The user is not privileged (does not have the \ + * CAP_SETUID capability) and uid does not match the real UID \ + * or saved set-user-ID of the calling process." -- man \ + * setuid */ \ + allowed = (config->euid == 0 /* TODO: || HAS_CAP(SETUID) */ \ + || id == config->r ## id \ + || id == config->e ## id \ + || id == config->s ## id); \ + if (!allowed) \ + return -EPERM; \ + \ + /* "If the effective UID of the caller is root, the real UID \ + * and saved set-user-ID are also set." -- man setuid */ \ + if (config->e ## id == 0) { \ + config->r ## id = id; \ + config->s ## id = id; \ + } \ + \ + /* "whenever the effective user ID is changed, fsuid will also \ + * be changed to the new value of the effective user ID." -- \ + * man setfsuid */ \ + config->e ## id = id; \ + config->fs ## id = id; \ + \ + poke_reg(tracee, SYSARG_RESULT, 0); \ + return 0; \ +} while (0) + +/** + * Check whether @id is set or not. + */ +#define UNSET_ID(id) (id == (uid_t) -1) + +/** + * Check whether @id is change or not. + */ +#define UNCHANGED_ID(id) (UNSET_ID(id) || id == config->id) + +/** + * Emulate setreuid(2) and setregid(2). + */ +#define SETREXID(id) do { \ + id ## _t r ## id = peek_reg(tracee, ORIGINAL, SYSARG_1); \ + id ## _t e ## id = peek_reg(tracee, ORIGINAL, SYSARG_2); \ + bool allowed; \ + \ + /* "Unprivileged processes may only set the effective user ID \ + * to the real user ID, the effective user ID, or the saved \ + * set-user-ID. \ + * \ + * Unprivileged users may only set the real user ID to the \ + * real user ID or the effective user ID." \ + * + * "EPERM: The calling process is not privileged (does not \ + * have the CAP_SETUID) and a change other than: \ + * 1. swapping the effective user ID with the real user ID, \ + * or; \ + * 2. setting one to the value of the other, or ; \ + * 3. setting the effective user ID to the value of the saved \ + * set-user-ID \ + * was specified." -- man setreuid \ + * \ + * Is it possible to "ruid <- euid" and "euid <- suid" at the \ + * same time? */ \ + allowed = (config->euid == 0 /* TODO: || HAS_CAP(SETUID) */ \ + || (UNCHANGED_ID(e ## id) && UNCHANGED_ID(r ## id)) \ + || (r ## id == config->e ## id && (e ## id == config->r ## id || UNCHANGED_ID(e ## id))) \ + || (e ## id == config->r ## id && (r ## id == config->e ## id || UNCHANGED_ID(r ## id))) \ + || (e ## id == config->s ## id && UNCHANGED_ID(r ## id))); \ + if (!allowed) \ + return -EPERM; \ + \ + /* "Supplying a value of -1 for either the real or effective \ + * user ID forces the system to leave that ID unchanged. \ + * [...] If the real user ID is set or the effective user ID \ + * is set to a value not equal to the previous real user ID, \ + * the saved set-user-ID will be set to the new effective user \ + * ID." -- man setreuid */ \ + if (!UNSET_ID(e ## id)) { \ + if (e ## id != config->r ## id) \ + config->s ## id = e ## id; \ + \ + config->e ## id = e ## id; \ + config->fs ## id = e ## id; \ + } \ + \ + /* Since it changes the current ruid value, this has to be \ + * done after euid handling. */ \ + if (!UNSET_ID(r ## id)) { \ + if (!UNSET_ID(e ## id)) \ + config->s ## id = e ## id; \ + config->r ## id = r ## id; \ + } \ + \ + poke_reg(tracee, SYSARG_RESULT, 0); \ + return 0; \ +} while (0) + +/** + * Check if @var is equal to any config->r{@type}id's. + */ +#define EQUALS_ANY_ID(var, type) (var == config->r ## type ## id \ + || var == config->e ## type ## id \ + || var == config->s ## type ## id) + +/** + * Emulate setresuid(2) and setresgid(2). + */ +#define SETRESXID(type) do { \ + type ## id_t r ## type ## id = peek_reg(tracee, ORIGINAL, SYSARG_1); \ + type ## id_t e ## type ## id = peek_reg(tracee, ORIGINAL, SYSARG_2); \ + type ## id_t s ## type ## id = peek_reg(tracee, ORIGINAL, SYSARG_3); \ + bool allowed; \ + \ + /* "Unprivileged user processes may change the real UID, \ + * effective UID, and saved set-user-ID, each to one of: the \ + * current real UID, the current effective UID or the current \ + * saved set-user-ID. \ + * \ + * Privileged processes (on Linux, those having the CAP_SETUID \ + * capability) may set the real UID, effective UID, and saved \ + * set-user-ID to arbitrary values." -- man setresuid */ \ + allowed = (config->euid == 0 /* || HAS_CAP(SETUID) */ \ + || ((UNSET_ID(r ## type ## id) || EQUALS_ANY_ID(r ## type ## id, type)) \ + && (UNSET_ID(e ## type ## id) || EQUALS_ANY_ID(e ## type ## id, type)) \ + && (UNSET_ID(s ## type ## id) || EQUALS_ANY_ID(s ## type ## id, type)))); \ + if (!allowed) \ + return -EPERM; \ + \ + /* "If one of the arguments equals -1, the corresponding value \ + * is not changed." -- man setresuid */ \ + if (!UNSET_ID(r ## type ## id)) \ + config->r ## type ## id = r ## type ## id; \ + \ + if (!UNSET_ID(e ## type ## id)) { \ + /* "the file system UID is always set to the same \ + * value as the (possibly new) effective UID." -- man \ + * setresuid */ \ + config->e ## type ## id = e ## type ## id; \ + config->fs ## type ## id = e ## type ## id; \ + } \ + \ + if (!UNSET_ID(s ## type ## id)) \ + config->s ## type ## id = s ## type ## id; \ + \ + poke_reg(tracee, SYSARG_RESULT, 0); \ + return 0; \ +} while (0) + +/** + * Emulate setfsuid(2) and setfsgid(2). + */ +#define SETFSXID(type) do { \ + uid_t fs ## type ## id = peek_reg(tracee, ORIGINAL, SYSARG_1); \ + uid_t old_fs ## type ## id = config->fs ## type ## id; \ + bool allowed; \ + \ + /* "setfsuid() will succeed only if the caller is the \ + * superuser or if fsuid matches either the real user ID, \ + * effective user ID, saved set-user-ID, or the current value \ + * of fsuid." -- man setfsuid */ \ + allowed = (config->euid == 0 /* TODO: || HAS_CAP(SETUID) */ \ + || fs ## type ## id == config->fs ## type ## id \ + || EQUALS_ANY_ID(fs ## type ## id, type)); \ + if (allowed) \ + config->fs ## type ## id = fs ## type ## id; \ + \ + /* "On success, the previous value of fsuid is returned. On \ + * error, the current value of fsuid is returned." -- man \ + * setfsuid */ \ + poke_reg(tracee, SYSARG_RESULT, old_fs ## type ## id); \ + return 0; \ +} while (0) + +/** + * Adjust current @tracee's syscall result according to @config. This + * function returns -errno if an error occured, otherwise 0. + */ +static int handle_sysexit_end(Tracee *tracee, Config *config) +{ + word_t sysnum; + word_t result; + + sysnum = get_sysnum(tracee, ORIGINAL); + switch (sysnum) { + + case PR_setuid: + case PR_setuid32: + SETXID(uid); + + case PR_setgid: + case PR_setgid32: + SETXID(gid); + + case PR_setreuid: + case PR_setreuid32: + SETREXID(uid); + + case PR_setregid: + case PR_setregid32: + SETREXID(gid); + + case PR_setresuid: + case PR_setresuid32: + SETRESXID(u); + + case PR_setresgid: + case PR_setresgid32: + SETRESXID(g); + + case PR_setfsuid: + case PR_setfsuid32: + SETFSXID(u); + + case PR_setfsgid: + case PR_setfsgid32: + SETFSXID(g); + + case PR_getuid: + case PR_getuid32: + poke_reg(tracee, SYSARG_RESULT, config->ruid); + return 0; + + case PR_getgid: + case PR_getgid32: + poke_reg(tracee, SYSARG_RESULT, config->rgid); + return 0; + + case PR_geteuid: + case PR_geteuid32: + poke_reg(tracee, SYSARG_RESULT, config->euid); + return 0; + + case PR_getegid: + case PR_getegid32: + poke_reg(tracee, SYSARG_RESULT, config->egid); + return 0; + + case PR_getresuid: + case PR_getresuid32: + POKE_MEM_ID(SYSARG_1, ruid); + POKE_MEM_ID(SYSARG_2, euid); + POKE_MEM_ID(SYSARG_3, suid); + return 0; + + case PR_getresgid: + case PR_getresgid32: + POKE_MEM_ID(SYSARG_1, rgid); + POKE_MEM_ID(SYSARG_2, egid); + POKE_MEM_ID(SYSARG_3, sgid); + return 0; + + case PR_setdomainname: + case PR_sethostname: + case PR_setgroups: + case PR_setgroups32: + case PR_mknod: + case PR_mknodat: + case PR_capset: + case PR_setxattr: + case PR_lsetxattr: + case PR_fsetxattr: + 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 if the tracee was supposed to have + * the capability. */ + if (config->euid == 0) /* TODO: || HAS_CAP(...) */ + 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 address; + Reg sysarg; + uid_t uid; + gid_t gid; + + /* 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 (sysnum == PR_fstatat64 || sysnum == PR_newfstatat) + sysarg = SYSARG_3; + else + sysarg = SYSARG_2; + + address = peek_reg(tracee, ORIGINAL, sysarg); + + /* Sanity checks. */ + assert(__builtin_types_compatible_p(uid_t, uint32_t)); + assert(__builtin_types_compatible_p(gid_t, uint32_t)); + + /* Get the uid & gid values from the 'stat' structure. */ + uid = peek_uint32(tracee, address + offsetof_stat_uid(tracee)); + if (errno != 0) + uid = 0; /* Not fatal. */ + + gid = peek_uint32(tracee, address + offsetof_stat_gid(tracee)); + if (errno != 0) + gid = 0; /* Not fatal. */ + + /* Override only if the file is owned by the current user. + * Errors are not fatal here. */ + if (uid == getuid()) + poke_uint32(tracee, address + offsetof_stat_uid(tracee), config->ruid); + + if (gid == getgid()) + poke_uint32(tracee, address + offsetof_stat_gid(tracee), config->rgid); + + return 0; + } + + case PR_chroot: { + char path[PATH_MAX]; + word_t input; + int status; + + if (config->euid != 0) /* TODO: && !HAS_CAP(SYS_CHROOT) */ + return 0; + + /* 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_path(tracee, path, input); + 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; + } + + default: + return 0; + } +} + +#undef POKE_MEM_ID +#undef SETXID +#undef UNSET_ID +#undef UNCHANGED_ID +#undef SETREXID +#undef EQUALS_ANY_ID +#undef SETRESXID +#undef SETFSXID + +/** + * Adjust some ELF auxiliary vectors. This function assumes the + * "argv, envp, auxv" stuff is pointed to by @tracee's stack pointer, + * as expected right after a successful call to execve(2). + */ +static int adjust_elf_auxv(Tracee *tracee, Config *config) +{ + ElfAuxVector *vectors; + ElfAuxVector *vector; + word_t vectors_address; + + vectors_address = get_elf_aux_vectors_address(tracee); + if (vectors_address == 0) + return 0; + + vectors = fetch_elf_aux_vectors(tracee, vectors_address); + if (vectors == NULL) + return 0; + + for (vector = vectors; vector->type != AT_NULL; vector++) { + switch (vector->type) { + case AT_UID: + vector->value = config->ruid; + break; + + case AT_EUID: + vector->value = config->euid; + break; + + case AT_GID: + vector->value = config->rgid; + break; + + case AT_EGID: + vector->value = config->egid; + break; + + default: + break; + } + } + + push_elf_aux_vectors(tracee, vectors, vectors_address); + + return 0; +} + +/** + * 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: { + const char *uid_string = (const char *) data1; + const char *gid_string; + Config *config; + int uid, gid; + + errno = 0; + uid = strtol(uid_string, NULL, 10); + if (errno != 0) + uid = getuid(); + + gid_string = strchr(uid_string, ':'); + if (gid_string == NULL) { + errno = EINVAL; + } + else { + errno = 0; + gid = strtol(gid_string + 1, NULL, 10); + } + /* Fallback to the current gid if an error occured. */ + if (errno != 0) + gid = getgid(); + + extension->config = talloc(extension, Config); + if (extension->config == NULL) + return -1; + + config = talloc_get_type_abort(extension->config, Config); + config->ruid = uid; + config->euid = uid; + config->suid = uid; + config->fsuid = uid; + config->rgid = gid; + config->egid = gid; + config->sgid = gid; + config->fsgid = gid; + + extension->filtered_sysnums = filtered_sysnums; + return 0; + } + + case INHERIT_PARENT: /* Inheritable for sub reconfiguration ... */ + return 1; + + case INHERIT_CHILD: { + /* Copy the parent configuration to the child. The + * structure should not be shared as uid/gid changes + * in one process should not affect other processes. + * This assertion is not true for POSIX threads + * sharing the same group, however Linux threads never + * share uid/gid information. As a consequence, the + * GlibC emulates the POSIX behavior on Linux by + * sending a signal to all group threads to cause them + * to invoke the system call too. Finally, PRoot + * doesn't have to worry about clone flags. + */ + + Extension *parent = (Extension *) data1; + extension->config = talloc_zero(extension, Config); + if (extension->config == NULL) + return -1; + + memcpy(extension->config, parent->config, sizeof(Config)); + return 0; + } + + case HOST_PATH: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + + /* Force permissions if the tracee was supposed to + * have the capability. */ + if (config->euid == 0) /* TODO: || HAS_CAP(DAC_OVERRIDE) */ + override_permissions(tracee, (char*) data1, (bool) data2); + return 0; + } + + case SYSCALL_ENTER_END: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + + return handle_sysenter_end(tracee, config); + } + + case SYSCALL_EXIT_END: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + + return handle_sysexit_end(tracee, config); + } + + case SYSCALL_EXIT_START: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + word_t result = peek_reg(tracee, CURRENT, SYSARG_RESULT);; + word_t sysnum = get_sysnum(tracee, ORIGINAL); + + /* Note: this can be done only before PRoot pushes the + * load script into tracee's stack. */ + if ((int) result >= 0 && sysnum == PR_execve) + adjust_elf_auxv(tracee, config); + return 0; + } + + default: + return 0; + } +}
diff --git a/5.1.0/src/extension/kompat/kompat.c b/5.1.0/src/extension/kompat/kompat.c new file mode 100644 index 0000000..2bbadb8 --- /dev/null +++ b/5.1.0/src/extension/kompat/kompat.c
@@ -0,0 +1,1063 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdint.h> /* intptr_t, */ +#include <stdlib.h> /* strtoul(3), */ +#include <linux/version.h> /* KERNEL_VERSION, */ +#include <assert.h> /* assert(3), */ +#include <sys/utsname.h> /* uname(2), utsname, */ +#include <string.h> /* str*(3), memcpy(3), */ +#include <talloc.h> /* talloc_*, */ +#include <fcntl.h> /* AT_*, */ +#include <sys/ptrace.h> /* linux.git:c0a3a20b */ +#include <errno.h> /* errno, */ +#include <linux/auxvec.h> /* AT_, */ +#include <linux/futex.h> /* FUTEX_PRIVATE_FLAG */ +#include <sys/param.h> /* MIN, */ + +#include "extension/extension.h" +#include "syscall/seccomp.h" +#include "syscall/sysnum.h" +#include "syscall/chain.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "tracee/mem.h" +#include "execve/auxv.h" +#include "cli/note.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; + +#define NONE {{0, 0, 0}} + +typedef struct { + int actual_release; + int virtual_release; + struct utsname utsname; + word_t hwcap; +} Config; + +/** + * Return whether the @expected_release is newer than + * @config->actual_release and older than @config->virtual_release. + */ +static bool needs_kompat(const Config *config, int expected_release) +{ + return (expected_release > config->actual_release + && expected_release <= config->virtual_release); +} + +/** + * Modify the current syscall of @tracee as described by @modif + * regarding the given @config. This function returns whether the + * syscall was modified or not. + */ +static bool modify_syscall(Tracee *tracee, const Config *config, const Modif *modif) +{ + size_t i, j; + word_t syscall; + + assert(config != NULL); + + if (!needs_kompat(config, modif->expected_release)) + return false; + + /* Check if this syscall is supported on this architecture. */ + syscall = detranslate_sysnum(get_abi(tracee), modif->new_sysarg_num); + if (syscall == SYSCALL_AVOIDER) + return false; + + set_sysnum(tracee, 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 true; +} + +/** + * 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); +} + +/** + * Remove @discarded_flags from the given @tracee's @sysarg register + * if the actual kernel release is not compatible with the + * @expected_release. + */ +static void discard_fd_flags(Tracee *tracee, const Config *config, + int discarded_flags, int expected_release, Reg sysarg) +{ + word_t flags; + + if (!needs_kompat(config, expected_release)) + return; + + flags = peek_reg(tracee, CURRENT, sysarg); + poke_reg(tracee, sysarg, flags & ~discarded_flags); +} + +/** + * Replace current @tracee's syscall with an older and compatible one + * whenever it's required, i.e. when the syscall is supported by the + * kernel as specified by @config->virtual_release but it isn't + * supported by the actual kernel. + */ +static int handle_sysenter_end(Tracee *tracee, Config *config) +{ + /* Note: syscalls like "openat" can be replaced by "open" since PRoot + * has canonicalized "fd + path" into "path". */ + switch (get_sysnum(tracee, ORIGINAL)) { + case PR_accept4: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,28), + .new_sysarg_num = PR_accept, + .shifts = NONE + }; + 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 = NONE + }; + + /* "If oldfd equals newfd, then dup3() fails with the + * error EINVAL" -- man dup3 */ + if (peek_reg(tracee, CURRENT, SYSARG_1) == peek_reg(tracee, CURRENT, SYSARG_2)) + return -EINVAL; + + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_epoll_create1: { + bool modified; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_epoll_create, + .shifts = NONE + }; + + /* "the size argument is ignored, but must be greater + * than zero" -- man epoll_create */ + modified = modify_syscall(tracee, config, &modif); + if (modified) + poke_reg(tracee, SYSARG_1, 1); + return 0; + } + + case PR_epoll_pwait: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,19), + .new_sysarg_num = PR_epoll_wait, + .shifts = NONE + }; + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_eventfd2: { + bool modified; + word_t flags; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_eventfd, + .shifts = NONE + }; + + modified = modify_syscall(tracee, config, &modif); + if (modified) { + /* EFD_SEMAPHORE can't be emulated with eventfd. */ + flags = peek_reg(tracee, CURRENT, SYSARG_2); + if ((flags & EFD_SEMAPHORE) != 0) + return -EINVAL; + } + 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 } + } + }; + 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 } + } + }; + 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); + + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_fcntl: { + word_t command; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,24))) + return 0; + + command = peek_reg(tracee, ORIGINAL, SYSARG_2); + if (command == F_DUPFD_CLOEXEC) + poke_reg(tracee, SYSARG_2, F_DUPFD); + + 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); + if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0) + return -EINVAL; /* Exposed by LTP. */ + +#if defined(ARCH_X86_64) + if ((flags & AT_SYMLINK_NOFOLLOW) != 0) + modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_lstat : PR_lstat64); + else + modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_stat : PR_stat64); +#else + if ((flags & AT_SYMLINK_NOFOLLOW) != 0) + modif.new_sysarg_num = PR_lstat64; + else + modif.new_sysarg_num = PR_stat64; +#endif + + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_futex: { + word_t operation; + static bool warned = false; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,22)) || config->actual_release == 0) + return 0; + + operation = peek_reg(tracee, CURRENT, SYSARG_2); + if ((operation & FUTEX_PRIVATE_FLAG) == 0) + return 0; + + if (!warned) { + warned = true; + note(tracee, WARNING, USER, + "kompat: this kernel doesn't support private futexes " + "and PRoot can't emulate them. Expect some troubles..."); + } + + poke_reg(tracee, SYSARG_2, operation & ~FUTEX_PRIVATE_FLAG); + 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 = NONE + }; + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_linkat: { + word_t flags; + 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 } + } + }; + + flags = peek_reg(tracee, CURRENT, SYSARG_5); + if ((flags & ~AT_SYMLINK_FOLLOW) != 0) + return -EINVAL; /* Exposed by LTP. */ + + 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: { + bool modified; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,16), + .new_sysarg_num = PR_open, + .shifts = { [0] = { + .sysarg = SYSARG_2, + .nb_args = 3, + .offset = -1 } + } + }; + modified = modify_syscall(tracee, config, &modif); + discard_fd_flags(tracee, config, O_CLOEXEC, KERNEL_VERSION(2,6,23), + modified ? SYSARG_2 : SYSARG_3); + return 0; + } + + case PR_open: + discard_fd_flags(tracee, config, O_CLOEXEC, KERNEL_VERSION(2,6,23), SYSARG_2); + return 0; + + case PR_pipe2: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_pipe, + .shifts = NONE + }; + modify_syscall(tracee, config, &modif); + return 0; + } + + case PR_pselect6: { + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,16), + .shifts = NONE + }; +#if defined(ARCH_X86_64) + modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_select : PR__newselect); +#else + modif.new_sysarg_num = PR__newselect; +#endif + + 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_renameat2: { + Modif modif = { + .expected_release = KERNEL_VERSION(3,15,0), + .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: { + bool modified; + Modif modif = { + .expected_release = KERNEL_VERSION(2,6,27), + .new_sysarg_num = PR_signalfd, + .shifts = NONE + }; + + /* "In Linux up to version 2.6.26, the flags argument + * is unused, and must be specified as zero." -- man + * signalfd */ + modified = modify_syscall(tracee, config, &modif); + if (modified) + poke_reg(tracee, SYSARG_4, 0); + return 0; + } + + case PR_socket: + case PR_socketpair: + case PR_timerfd_create: + discard_fd_flags(tracee, config, O_CLOEXEC | O_NONBLOCK, + KERNEL_VERSION(2,6,27), SYSARG_2); + 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); + + modify_syscall(tracee, config, &modif); + return 0; + } + + default: + return 0; + } +} + +/** + * Adjust some ELF auxiliary vectors to improve the compatibility. + * This function assumes the "argv, envp, auxv" stuff is pointed to by + * @tracee's stack pointer, as expected right after a successful call + * to execve(2). + */ +static void adjust_elf_auxv(Tracee *tracee, Config *config) +{ + ElfAuxVector *vectors; + ElfAuxVector *vector; + word_t vectors_address; + word_t stack_pointer; + void *argv_envp; + size_t size; + int status; + + vectors_address = get_elf_aux_vectors_address(tracee); + if (vectors_address == 0) + return; + + vectors = fetch_elf_aux_vectors(tracee, vectors_address); + if (vectors == NULL) + return; + + for (vector = vectors; vector->type != AT_NULL; vector++) { + switch (vector->type) { + /* Discard AT_SYSINFO* vectors: they can be used to + * get the OS release number from memory instead of + * from the uname syscall, and only this latter is + * currently hooked by PRoot. */ + case AT_SYSINFO_EHDR: + case AT_SYSINFO: + vector->type = AT_IGNORE; + vector->value = 0; + break; + + case AT_HWCAP: + if (config->hwcap != (word_t) -1) + vector->value = config->hwcap; + break; + + case AT_RANDOM: + /* Skip only if not in forced mode. */ + if (config->actual_release != 0) + goto end; + break; + + default: + break; + } + } + + /* Add the AT_RANDOM vector only if needed. */ + if (!needs_kompat(config, KERNEL_VERSION(2,6,29))) + goto end; + + status = add_elf_aux_vector(&vectors, AT_RANDOM, vectors_address); + if (status < 0) + goto end; /* Not fatal. */ + + /* Since a new vector needs to be added, the ELF auxiliary + * vectors array can't be pushed in place. As a consequence, + * argv[] and envp[] arrays are moved one vector downward to + * make room for the new ELF auxiliary vectors array. + * Remember, the stack layout is as follow right after execve: + * + * argv[], envp[], auxv[] + */ + stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER); + size = vectors_address - stack_pointer; + argv_envp = talloc_size(tracee->ctx, size); + if (argv_envp == NULL) + goto end; + + status = read_data(tracee, argv_envp, stack_pointer, size); + if (status < 0) + goto end; + + /* Allocate enough room in tracee's stack for the new ELF + * auxiliary vector. */ + stack_pointer -= 2 * sizeof_word(tracee); + vectors_address -= 2 * sizeof_word(tracee); + + /* Note that it is safe to update the stack pointer manually + * since we are in execve sysexit. However it should be done + * before transfering data since the kernel might not allow + * page faults below the stack pointer. */ + poke_reg(tracee, STACK_POINTER, stack_pointer); + + status = write_data(tracee, stack_pointer, argv_envp, size); + if (status < 0) + return; + +end: + push_elf_aux_vectors(tracee, vectors, vectors_address); + return; +} + +/** + * Append to the @tracee's current syscall enough calls to fcntl(@fd) + * in order to set the flags from the original @sysarg register, if + * there are also set in @emulated_flags. + */ +static void emulate_fd_flags(Tracee *tracee, word_t fd, Reg sysarg, int emulated_flags) +{ + word_t flags; + + flags = peek_reg(tracee, ORIGINAL, sysarg); + if (flags == 0) + return; + + if ((emulated_flags & flags & O_CLOEXEC) != 0) + register_chained_syscall(tracee, PR_fcntl, fd, F_SETFD, FD_CLOEXEC, 0, 0, 0); + + if ((emulated_flags & flags & O_NONBLOCK) != 0) + register_chained_syscall(tracee, PR_fcntl, fd, F_SETFL, O_NONBLOCK, 0, 0, 0); + + force_chain_final_result(tracee, peek_reg(tracee, CURRENT, SYSARG_RESULT)); +} + +/** + * Adjust the results/output parameters for syscalls that were + * modified in handle_sysenter_end(). This function returns -errno if + * an error occured, otherwise 0. + */ +static int handle_sysexit_end(Tracee *tracee, Config *config) +{ + word_t result; + word_t sysnum; + int status; + + result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + sysnum = get_sysnum(tracee, ORIGINAL); + + /* Error reported by the kernel. */ + status = (int) result; + if (status < 0) + return 0; + + switch (sysnum) { + case PR_uname: { + word_t address; + + address = peek_reg(tracee, ORIGINAL, SYSARG_1); + + /* The layout of struct utsname does not depend on the + * architecture, it only depends on the kernel + * version. In this regards, this structure is stable + * since < 2.6.0. */ + status = write_data(tracee, address, &config->utsname, sizeof(config->utsname)); + if (status < 0) + return status; + return 0; + } + + case PR_setdomainname: + case PR_sethostname: { + word_t address; + word_t length; + char *name; + + name = (sysnum == PR_setdomainname + ? config->utsname.domainname + : config->utsname.nodename); + + length = peek_reg(tracee, ORIGINAL, SYSARG_2); + if (length > sizeof(config->utsname.domainname) - 1) + return -EINVAL; + + /* Because of the test above. */ + assert(sizeof(config->utsname.domainname) == sizeof(config->utsname.nodename)); + + address = peek_reg(tracee, ORIGINAL, SYSARG_1); + status = read_data(tracee, name, address, length); + if (status < 0) + return status; + + /* "name does not require a terminating null byte." -- + * man 2 set{domain,host}name. */ + name[length] = '\0'; + + return 0; + } + + case PR_accept4: + if (get_sysnum(tracee, MODIFIED) == PR_accept) + emulate_fd_flags(tracee, result, SYSARG_4, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_dup3: + if (get_sysnum(tracee, MODIFIED) == PR_dup2) + emulate_fd_flags(tracee, peek_reg(tracee, ORIGINAL, SYSARG_2), + SYSARG_3, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_epoll_create1: + if (get_sysnum(tracee, MODIFIED) == PR_epoll_create) + emulate_fd_flags(tracee, result, SYSARG_1, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_eventfd2: + if (get_sysnum(tracee, MODIFIED) == PR_eventfd) + emulate_fd_flags(tracee, result, SYSARG_2, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_fcntl: { + word_t command; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,24))) + return 0; + + command = peek_reg(tracee, ORIGINAL, SYSARG_2); + if (command != F_DUPFD_CLOEXEC) + return 0; + + register_chained_syscall(tracee, PR_fcntl, result, F_SETFD, FD_CLOEXEC, 0, 0, 0); + force_chain_final_result(tracee, peek_reg(tracee, CURRENT, SYSARG_RESULT)); + return 0; + } + + case PR_inotify_init1: + if (get_sysnum(tracee, MODIFIED) == PR_inotify_init) + emulate_fd_flags(tracee, result, SYSARG_1, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_open: + if (needs_kompat(config, KERNEL_VERSION(2,6,23))) + emulate_fd_flags(tracee, result, SYSARG_2, O_CLOEXEC); + return 0; + + case PR_openat: + if (needs_kompat(config, KERNEL_VERSION(2,6,23))) + emulate_fd_flags(tracee, result, SYSARG_3, O_CLOEXEC); + return 0; + + case PR_pipe2: { + int fds[2]; + + if (get_sysnum(tracee, MODIFIED) != PR_pipe) + return 0; + + status = read_data(tracee, fds, peek_reg(tracee, MODIFIED, SYSARG_1), sizeof(fds)); + if (status < 0) + return 0; + + emulate_fd_flags(tracee, fds[0], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + emulate_fd_flags(tracee, fds[1], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + + return 0; + } + + case PR_signalfd4: + if (get_sysnum(tracee, MODIFIED) == PR_signalfd) + emulate_fd_flags(tracee, result, SYSARG_4, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_socket: + case PR_timerfd_create: + if (needs_kompat(config, KERNEL_VERSION(2,6,27))) + emulate_fd_flags(tracee, result, SYSARG_2, O_CLOEXEC | O_NONBLOCK); + return 0; + + case PR_socketpair: { + int fds[2]; + + if (!needs_kompat(config, KERNEL_VERSION(2,6,27))) + return 0; + + status = read_data(tracee, fds, peek_reg(tracee, MODIFIED, SYSARG_4), sizeof(fds)); + if (status < 0) + return 0; + + emulate_fd_flags(tracee, fds[0], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + emulate_fd_flags(tracee, fds[1], SYSARG_2, O_CLOEXEC | O_NONBLOCK); + + return 0; + } + + default: + return 0; + } + + return 0; +} + +/** + * Fill @config->utsname and @config->hwcap according to the content + * of @string. This function returns -1 if there is a parsing error, + * otherwise 0. + */ +static int parse_utsname(Config *config, const char *string) +{ + struct utsname utsname; + int status; + + assert(string != NULL); + + status = uname(&utsname); + if (status < 0 || getenv("PROOT_FORCE_KOMPAT") != NULL) + config->actual_release = 0; + else + config->actual_release = parse_kernel_release(utsname.release); + + /* Check whether it is the simple format (ie. release number), + * or the complex one: + * + * '\sysname\nodename\release\version\machine\domainname\hwcap\' + * + * This complex format is ugly on purpose: it ain't to be used + * directly by users. */ + if (string[0] == '\\') { + const char *start; + const char *end; + char *end2; + + /* Initial state of the parser. */ + end = string; + +#define PARSE(field) do { \ + size_t length; \ + \ + start = end + 1; \ + end = strchr(start, '\\'); \ + if (end == NULL) { \ + note(NULL, ERROR, USER, \ + "can't find %s field in '%s'", #field, string); \ + return -1; \ + } \ + \ + length = end - start; \ + length = MIN(length, sizeof(config->utsname.field) - 1); \ + strncpy(config->utsname.field, start, length); \ + config->utsname.field[length] = '\0'; \ + } while(0) + + PARSE(sysname); + PARSE(nodename); + PARSE(release); + PARSE(version); + PARSE(machine); + PARSE(domainname); + +#undef PARSE + + /* The hwcap field is parsed as an hexadecimal value. */ + errno = 0; + config->hwcap = strtol(end + 1, &end2, 16); + if (errno != 0 || end2[0] != '\\') { + note(NULL, ERROR, USER, "can't find hwcap field in '%s'", string); + return -1; + } + } + else { + size_t length; + + memcpy(&config->utsname, &utsname, sizeof(config->utsname)); + + length = MIN(strlen(string), sizeof(config->utsname.release) - 1); + strncpy(config->utsname.release, string, length); + config->utsname.release[length] = '\0'; + + config->hwcap = (word_t) -1; + } + + config->virtual_release = parse_kernel_release(config->utsname.release); + + return 0; +} + +/* List of syscalls handled by this extensions. */ +static FilteredSysnum filtered_sysnums[] = { + { PR_accept4, FILTER_SYSEXIT }, + { PR_dup3, FILTER_SYSEXIT }, + { PR_epoll_create1, FILTER_SYSEXIT }, + { PR_epoll_pwait, 0 }, + { PR_eventfd2, FILTER_SYSEXIT }, + { PR_execve, FILTER_SYSEXIT }, + { PR_faccessat, 0 }, + { PR_fchmodat, 0 }, + { PR_fchownat, 0 }, + { PR_fcntl, FILTER_SYSEXIT }, + { PR_fstatat64, 0 }, + { PR_futimesat, 0 }, + { PR_futex, 0 }, + { PR_inotify_init1, FILTER_SYSEXIT }, + { PR_linkat, 0 }, + { PR_mkdirat, 0 }, + { PR_mknodat, 0 }, + { PR_newfstatat, 0 }, + { PR_open, FILTER_SYSEXIT }, + { PR_openat, FILTER_SYSEXIT }, + { PR_pipe2, FILTER_SYSEXIT }, + { PR_pselect6, 0 }, + { PR_readlinkat, 0 }, + { PR_renameat, 0 }, + { PR_renameat2, 0 }, + { PR_setdomainname, FILTER_SYSEXIT }, + { PR_sethostname, FILTER_SYSEXIT }, + { PR_signalfd4, FILTER_SYSEXIT }, + { PR_socket, FILTER_SYSEXIT }, + { PR_socketpair, FILTER_SYSEXIT }, + { PR_symlinkat, 0 }, + { PR_timerfd_create, FILTER_SYSEXIT }, + { PR_uname, FILTER_SYSEXIT }, + { PR_unlinkat, 0 }, + FILTERED_SYSNUM_END, +}; + +/** + * 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: { + Config *config; + + extension->config = talloc_zero(extension, Config); + if (extension->config == NULL) + return -1; + config = extension->config; + + status = parse_utsname(config, (const char *) data1); + if (status < 0) + return -1; + + extension->filtered_sysnums = filtered_sysnums; + 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; + + return handle_sysenter_end(tracee, config); + } + + case SYSCALL_EXIT_END: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + + return handle_sysexit_end(tracee, config); + } + + case SYSCALL_EXIT_START: { + Tracee *tracee = TRACEE(extension); + Config *config = talloc_get_type_abort(extension->config, Config); + word_t result = peek_reg(tracee, CURRENT, SYSARG_RESULT);; + word_t sysnum = get_sysnum(tracee, ORIGINAL); + + /* Note: this can be done only before PRoot pushes the + * load script into tracee's stack. */ + if ((int) result >= 0 && sysnum == PR_execve) + adjust_elf_auxv(tracee, config); + return 0; + } + + default: + return 0; + } +}
diff --git a/5.1.0/src/loader/assembly-arm.h b/5.1.0/src/loader/assembly-arm.h new file mode 100644 index 0000000..365b822 --- /dev/null +++ b/5.1.0/src/loader/assembly-arm.h
@@ -0,0 +1,92 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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. + */ + +/* According to the ARM EABI, all registers have undefined values at + * program startup except: + * + * - the instruction pointer (r15) + * - the stack pointer (r13) + * - the rtld_fini pointer (r0) + */ +#define BRANCH(stack_pointer, destination) do { \ + asm volatile ( \ + "// Restore initial stack pointer. \n\t" \ + "mov sp, %0 \n\t" \ + " \n\t" \ + "// Clear rtld_fini. \n\t" \ + "mov r0, #0 \n\t" \ + " \n\t" \ + "// Start the program. \n\t" \ + "mov pc, %1 \n" \ + : /* no output */ \ + : "r" (stack_pointer), "r" (destination) \ + : "memory", "sp", "r0", "pc"); \ + __builtin_unreachable(); \ + } while (0) + +#define PREPARE_ARGS_1(arg1_) \ + register word_t arg1 asm("r0") = arg1_; \ + +#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + PREPARE_ARGS_1(arg1_) \ + register word_t arg2 asm("r1") = arg2_; \ + register word_t arg3 asm("r2") = arg3_; \ + +#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \ + PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + register word_t arg4 asm("r3") = arg4_; \ + register word_t arg5 asm("r4") = arg5_; \ + register word_t arg6 asm("r5") = arg6_; + +#define OUTPUT_CONTRAINTS_1 \ + "r" (arg1) + +#define OUTPUT_CONTRAINTS_3 \ + OUTPUT_CONTRAINTS_1, \ + "r" (arg2), "r" (arg3) + +#define OUTPUT_CONTRAINTS_6 \ + OUTPUT_CONTRAINTS_3, \ + "r" (arg4), "r" (arg5), "r" (arg6) + +#define SYSCALL(number_, nb_args, args...) \ + ({ \ + register word_t number asm("r7") = number_; \ + register word_t result asm("r0"); \ + PREPARE_ARGS_##nb_args(args) \ + asm volatile ( \ + "svc #0x00000000 \n\t" \ + : "=r" (result) \ + : "r" (number), \ + OUTPUT_CONTRAINTS_##nb_args \ + : "memory"); \ + result; \ + }) + +#define OPEN 5 +#define CLOSE 6 +#define MMAP 192 +#define MMAP_OFFSET_SHIFT 12 +#define EXECVE 11 +#define EXIT 1 +#define PRCTL 172 +
diff --git a/5.1.0/src/loader/assembly-arm64.h b/5.1.0/src/loader/assembly-arm64.h new file mode 100644 index 0000000..b9b1822 --- /dev/null +++ b/5.1.0/src/loader/assembly-arm64.h
@@ -0,0 +1,93 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 BRANCH(stack_pointer, destination) do { \ + asm volatile ( \ + "// Restore initial stack pointer. \n\t" \ + "mov sp, %0 \n\t" \ + " \n\t" \ + "// Clear rtld_fini. \n\t" \ + "mov x0, #0 \n\t" \ + " \n\t" \ + "// Start the program. \n\t" \ + "br %1 \n" \ + : /* no output */ \ + : "r" (stack_pointer), "r" (destination) \ + : "memory", "sp", "x0"); \ + __builtin_unreachable(); \ + } while (0) + +#define PREPARE_ARGS_1(arg1_) \ + register word_t arg1 asm("x0") = arg1_; \ + +#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + PREPARE_ARGS_1(arg1_) \ + register word_t arg2 asm("x1") = arg2_; \ + register word_t arg3 asm("x2") = arg3_; \ + +#define PREPARE_ARGS_4(arg1_, arg2_, arg3_, arg4_) \ + PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + register word_t arg4 asm("x3") = arg4_; + +#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \ + PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + register word_t arg4 asm("x3") = arg4_; \ + register word_t arg5 asm("x4") = arg5_; \ + register word_t arg6 asm("x5") = arg6_; + +#define OUTPUT_CONTRAINTS_1 \ + "r" (arg1) + +#define OUTPUT_CONTRAINTS_3 \ + OUTPUT_CONTRAINTS_1, \ + "r" (arg2), "r" (arg3) + +#define OUTPUT_CONTRAINTS_4 \ + OUTPUT_CONTRAINTS_3, \ + "r" (arg4) + +#define OUTPUT_CONTRAINTS_6 \ + OUTPUT_CONTRAINTS_3, \ + "r" (arg4), "r" (arg5), "r" (arg6) + +#define SYSCALL(number_, nb_args, args...) \ + ({ \ + register word_t number asm("w8") = number_; \ + register word_t result asm("x0"); \ + PREPARE_ARGS_##nb_args(args) \ + asm volatile ( \ + "svc #0x00000000 \n\t" \ + : "=r" (result) \ + : "r" (number), \ + OUTPUT_CONTRAINTS_##nb_args \ + : "memory"); \ + result; \ + }) + +#define OPENAT 56 +#define CLOSE 57 +#define MMAP 222 +#define MMAP_OFFSET_SHIFT 0 +#define EXECVE 221 +#define EXIT 93 +#define PRCTL 167 +
diff --git a/5.1.0/src/loader/assembly-x86.h b/5.1.0/src/loader/assembly-x86.h new file mode 100644 index 0000000..2357953 --- /dev/null +++ b/5.1.0/src/loader/assembly-x86.h
@@ -0,0 +1,67 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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. + */ + +/* According to the x86 ABI, all registers have undefined values at + * program startup except: + * + * - the instruction pointer (rip) + * - the stack pointer (rsp) + * - the rtld_fini pointer (rdx) + * - the system flags (eflags) + */ +#define BRANCH(stack_pointer, destination) do { \ + asm volatile ( \ + "// Restore initial stack pointer. \n\t" \ + "movl %0, %%esp \n\t" \ + " \n\t" \ + "// Clear state flags. \n\t" \ + "pushl $0 \n\t" \ + "popfl \n\t" \ + " \n\t" \ + "// Clear rtld_fini. \n\t" \ + "movl $0, %%edx \n\t" \ + " \n\t" \ + "// Start the program. \n\t" \ + "jmpl *%%eax \n" \ + : /* no output */ \ + : "irm" (stack_pointer), "a" (destination) \ + : "memory", "cc", "esp", "edx"); \ + __builtin_unreachable(); \ + } while (0) + +extern word_t syscall_6(word_t number, + word_t arg1, word_t arg2, word_t arg3, + word_t arg4, word_t arg5, word_t arg6); + +extern word_t syscall_3(word_t number, word_t arg1, word_t arg2, word_t arg3); + +extern word_t syscall_1(word_t number, word_t arg1); + +#define SYSCALL(number, nb_args, args...) syscall_##nb_args(number, args) + +#define OPEN 5 +#define CLOSE 6 +#define MMAP 192 +#define MMAP_OFFSET_SHIFT 12 +#define EXECVE 11 +#define EXIT 1 +#define PRCTL 172
diff --git a/5.1.0/src/loader/assembly-x86_64.h b/5.1.0/src/loader/assembly-x86_64.h new file mode 100644 index 0000000..1575aea --- /dev/null +++ b/5.1.0/src/loader/assembly-x86_64.h
@@ -0,0 +1,95 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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. + */ + +/* According to the x86_64 ABI, all registers have undefined values at + * program startup except: + * + * - the instruction pointer (rip) + * - the stack pointer (rsp) + * - the rtld_fini pointer (rdx) + * - the system flags (rflags) + */ +#define BRANCH(stack_pointer, destination) do { \ + asm volatile ( \ + "// Restore initial stack pointer. \n\t" \ + "movq %0, %%rsp \n\t" \ + " \n\t" \ + "// Clear state flags. \n\t" \ + "pushq $0 \n\t" \ + "popfq \n\t" \ + " \n\t" \ + "// Clear rtld_fini. \n\t" \ + "movq $0, %%rdx \n\t" \ + " \n\t" \ + "// Start the program. \n\t" \ + "jmpq *%%rax \n" \ + : /* no output */ \ + : "irm" (stack_pointer), "a" (destination) \ + : "memory", "cc", "rsp", "rdx"); \ + __builtin_unreachable(); \ + } while (0) + +#define PREPARE_ARGS_1(arg1_) \ + register word_t arg1 asm("rdi") = arg1_; \ + +#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + PREPARE_ARGS_1(arg1_) \ + register word_t arg2 asm("rsi") = arg2_; \ + register word_t arg3 asm("rdx") = arg3_; \ + +#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \ + PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + register word_t arg4 asm("r10") = arg4_; \ + register word_t arg5 asm("r8") = arg5_; \ + register word_t arg6 asm("r9") = arg6_; + +#define OUTPUT_CONTRAINTS_1 \ + "r" (arg1) + +#define OUTPUT_CONTRAINTS_3 \ + OUTPUT_CONTRAINTS_1, \ + "r" (arg2), "r" (arg3) + +#define OUTPUT_CONTRAINTS_6 \ + OUTPUT_CONTRAINTS_3, \ + "r" (arg4), "r" (arg5), "r" (arg6) + +#define SYSCALL(number_, nb_args, args...) \ + ({ \ + register word_t number asm("rax") = number_; \ + register word_t result asm("rax"); \ + PREPARE_ARGS_##nb_args(args) \ + asm volatile ( \ + "syscall \n\t" \ + : "=r" (result) \ + : "r" (number), \ + OUTPUT_CONTRAINTS_##nb_args \ + : "memory", "cc", "rcx", "r11"); \ + result; \ + }) + +#define OPEN 2 +#define CLOSE 3 +#define MMAP 9 +#define EXECVE 59 +#define EXIT 60 +#define PRCTL 157
diff --git a/5.1.0/src/loader/assembly.S b/5.1.0/src/loader/assembly.S new file mode 100644 index 0000000..ca46997 --- /dev/null +++ b/5.1.0/src/loader/assembly.S
@@ -0,0 +1,62 @@ +#if defined(__i386__) + .text + +/* + ABI user-land kernel-land + ====== ========= =========== + number %eax %eax + arg1 %edx %ebx + arg2 %ecx %ecx + arg3 16(%esp) %edx + arg4 12(%esp) %esi + arg5 8(%esp) %edi + arg6 4(%esp) %ebp + result N/A %eax +*/ +.globl syscall_6 +.type syscall_6, @function +syscall_6: + /* Callee-saved registers. */ + pushl %ebp // %esp -= 0x04 + pushl %edi // %esp -= 0x08 + pushl %esi // %esp -= 0x0c + pushl %ebx // %esp -= 0x10 + +// mov %eax, %eax // number + mov %edx, %ebx // arg1 +// mov %ecx, %ecx // arg2 + mov 0x14(%esp), %edx // arg3 + mov 0x18(%esp), %esi // arg4 + mov 0x1c(%esp), %edi // arg5 + mov 0x20(%esp), %ebp // arg6 + + int $0x80 + + popl %ebx + popl %esi + popl %edi + popl %ebp + +// mov %eax, %eax // result + ret + +.globl syscall_3 +.type syscall_3, @function +syscall_3: + pushl %ebx + mov %edx, %ebx + mov 0x8(%esp), %edx + int $0x80 + popl %ebx + ret + +.globl syscall_1 +.type syscall_1, @function +syscall_1: + pushl %ebx + mov %edx, %ebx + int $0x80 + popl %ebx + ret + +#endif /* defined(__i386__) */
diff --git a/5.1.0/src/loader/loader.c b/5.1.0/src/loader/loader.c new file mode 100644 index 0000000..88d37ea --- /dev/null +++ b/5.1.0/src/loader/loader.c
@@ -0,0 +1,263 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdbool.h> /* bool, true, false, */ + +#define NO_LIBC_HEADER +#include "loader/script.h" +#include "compat.h" +#include "arch.h" + +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) + +#if GCC_VERSION < 40500 +#define __builtin_unreachable() +#endif + +#if defined(ARCH_X86_64) +# include "loader/assembly-x86_64.h" +#elif defined(ARCH_ARM_EABI) +# include "loader/assembly-arm.h" +#elif defined(ARCH_ARM64) +# include "loader/assembly-arm64.h" +#elif defined(ARCH_X86) +# include "loader/assembly-x86.h" +#else +# error "Unsupported architecture" +#endif + +#if !defined(MMAP_OFFSET_SHIFT) +# define MMAP_OFFSET_SHIFT 0 +#endif + +#define FATAL() do { \ + SYSCALL(EXIT, 1, 182); \ + __builtin_unreachable(); \ + } while (0) + +#define unlikely(expr) __builtin_expect(!!(expr), 0) + +/** + * Clear the memory from @start (inclusive) to @end (exclusive). + */ +static inline void clear(word_t start, word_t end) +{ + byte_t *start_misaligned; + byte_t *end_misaligned; + + word_t *start_aligned; + word_t *end_aligned; + + /* Compute the number of mis-aligned bytes. */ + word_t start_bytes = start % sizeof(word_t); + word_t end_bytes = end % sizeof(word_t); + + /* Compute aligned addresses. */ + start_aligned = (word_t *) (start_bytes ? start + sizeof(word_t) - start_bytes : start); + end_aligned = (word_t *) (end - end_bytes); + + /* Clear leading mis-aligned bytes. */ + start_misaligned = (byte_t *) start; + while (start_misaligned < (byte_t *) start_aligned) + *start_misaligned++ = 0; + + /* Clear aligned bytes. */ + while (start_aligned < end_aligned) + *start_aligned++ = 0; + + /* Clear trailing mis-aligned bytes. */ + end_misaligned = (byte_t *) end_aligned; + while (end_misaligned < (byte_t *) end) + *end_misaligned++ = 0; +} + +/** + * Return the address of the last path component of @string_. Note + * that @string_ is not modified. + */ +static inline word_t basename(word_t string_) +{ + byte_t *string = (byte_t *) string_; + byte_t *cursor; + + for (cursor = string; *cursor != 0; cursor++) + ; + + for (; *cursor != (byte_t) '/' && cursor > string; cursor--) + ; + + if (cursor != string) + cursor++; + + return (word_t) cursor; +} + +/** + * Interpret the load script pointed to by @cursor. + */ +void _start(void *cursor) +{ + bool traced = false; + bool reset_at_base = true; + word_t at_base = 0; + + word_t fd = -1; + word_t status; + + while(1) { + LoadStatement *stmt = cursor; + + switch (stmt->action) { + case LOAD_ACTION_OPEN_NEXT: + status = SYSCALL(CLOSE, 1, fd); + if (unlikely((int) status < 0)) + FATAL(); + /* Fall through. */ + + case LOAD_ACTION_OPEN: +#ifdef OPENAT + fd = SYSCALL(OPENAT, 4, AT_FDCWD, stmt->open.string_address, O_RDONLY, 0); +#else + fd = SYSCALL(OPEN, 3, stmt->open.string_address, O_RDONLY, 0); +#endif + if (unlikely((int) fd < 0)) + FATAL(); + + reset_at_base = true; + + cursor += LOAD_STATEMENT_SIZE(*stmt, open); + break; + + case LOAD_ACTION_MMAP_FILE: + status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length, + stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED, fd, + stmt->mmap.offset >> MMAP_OFFSET_SHIFT); + if (unlikely(status != stmt->mmap.addr)) + FATAL(); + + if (stmt->mmap.clear_length != 0) + clear(stmt->mmap.addr + stmt->mmap.length - stmt->mmap.clear_length, + stmt->mmap.addr + stmt->mmap.length); + + if (reset_at_base) { + at_base = stmt->mmap.addr; + reset_at_base = false; + } + + cursor += LOAD_STATEMENT_SIZE(*stmt, mmap); + break; + + case LOAD_ACTION_MMAP_ANON: + status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length, + stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); + if (unlikely(status != stmt->mmap.addr)) + FATAL(); + + cursor += LOAD_STATEMENT_SIZE(*stmt, mmap); + break; + + case LOAD_ACTION_START_TRACED: + traced = true; + /* Fall through. */ + + case LOAD_ACTION_START: { + word_t *cursor2 = (word_t *) stmt->start.stack_pointer; + const word_t argc = cursor2[0]; + const word_t at_execfn = cursor2[1]; + word_t name; + + status = SYSCALL(CLOSE, 1, fd); + if (unlikely((int) status < 0)) + FATAL(); + + /* Right after execve, the stack content is as follow: + * + * +------+--------+--------+--------+ + * | argc | argv[] | envp[] | auxv[] | + * +------+--------+--------+--------+ + */ + + /* Skip argv[]. */ + cursor2 += argc + 1; + + /* Skip envp[]. */ + do cursor2++; while (cursor2[0] != 0); + cursor2++; + + /* Adjust auxv[]. */ + do { + switch (cursor2[0]) { + case AT_PHDR: + cursor2[1] = stmt->start.at_phdr; + break; + + case AT_PHENT: + cursor2[1] = stmt->start.at_phent; + break; + + case AT_PHNUM: + cursor2[1] = stmt->start.at_phnum; + break; + + case AT_ENTRY: + cursor2[1] = stmt->start.at_entry; + break; + + case AT_BASE: + cursor2[1] = at_base; + break; + + case AT_EXECFN: + /* stmt->start.at_execfn can't be used for now since it is + * currently stored in a location that will be scratched + * by the process (below the final stack pointer). */ + cursor2[1] = at_execfn; + break; + + default: + break; + } + cursor2 += 2; + } while (cursor2[0] != AT_NULL); + + /* Note that only 2 arguments are actually necessary... */ + name = basename(stmt->start.at_execfn); + SYSCALL(PRCTL, 3, PR_SET_NAME, name, 0); + + if (unlikely(traced)) + SYSCALL(EXECVE, 6, 1, + stmt->start.stack_pointer, + stmt->start.entry_point, 2, 3, 4); + else + BRANCH(stmt->start.stack_pointer, stmt->start.entry_point); + FATAL(); + } + + default: + FATAL(); + } + } + + FATAL(); +}
diff --git a/5.1.0/src/loader/script.h b/5.1.0/src/loader/script.h new file mode 100644 index 0000000..38f55f9 --- /dev/null +++ b/5.1.0/src/loader/script.h
@@ -0,0 +1,73 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 SCRIPT +#define SCRIPT + +#include "arch.h" +#include "attribute.h" + +struct load_statement { + word_t action; + + union { + struct { + word_t string_address; + } open; + + struct { + word_t addr; + word_t length; + word_t prot; + word_t offset; + word_t clear_length; + } mmap; + + struct { + word_t stack_pointer; + word_t entry_point; + word_t at_phdr; + word_t at_phent; + word_t at_phnum; + word_t at_entry; + word_t at_execfn; + } start; + }; +} PACKED; + +typedef struct load_statement LoadStatement; + +#define LOAD_STATEMENT_SIZE(statement, type) \ + (sizeof((statement).action) + sizeof((statement).type)) + +/* Don't use enum, since sizeof(enum) doesn't have to be equal to + * sizeof(word_t). Keep values in the same order as their respective + * actions appear in loader.c to get a change GCC produces a jump + * table. */ +#define LOAD_ACTION_OPEN_NEXT 0 +#define LOAD_ACTION_OPEN 1 +#define LOAD_ACTION_MMAP_FILE 2 +#define LOAD_ACTION_MMAP_ANON 3 +#define LOAD_ACTION_START_TRACED 4 +#define LOAD_ACTION_START 5 + +#endif /* SCRIPT */
diff --git a/5.1.0/src/path/binding.c b/5.1.0/src/path/binding.c new file mode 100644 index 0000000..f53c18c --- /dev/null +++ b/5.1.0/src/path/binding.c
@@ -0,0 +1,735 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/stat.h> /* lstat(2), */ +#include <unistd.h> /* getcwd(2), lstat(2), */ +#include <string.h> /* string(3), */ +#include <strings.h> /* bzero(3), */ +#include <assert.h> /* assert(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <errno.h> /* E* */ +#include <sys/queue.h> /* CIRCLEQ_*, */ +#include <talloc.h> /* talloc_*, */ + +#include "path/binding.h" +#include "path/path.h" +#include "path/canon.h" +#include "cli/note.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) + note(tracee, INFO, USER, "binding = %s", binding->host.path); + else + note(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). + */ +Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]) +{ + 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(const 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(const Tracee *tracee, Side side, char path[PATH_MAX]) +{ + const Path *reverse_ref; + const Path *ref; + const Binding *binding; + + binding = get_binding(tracee, side, path); + if (!binding) + return -ENOENT; + + /* Is it a "symetric" binding? */ + if (!binding->need_substitution) + return 0; + + 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_path_prefix(path, ref->length, reverse_ref->path, reverse_ref->length); + + return 1; +} + +/** + * Remove @binding from all the @tracee's lists of bindings it belongs to. + */ +void 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); +} + +/** + * 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 && getenv("PROOT_IGNORE_MISSING_BINDINGS") == NULL) { + note(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. + */ +static void insort_binding2(const 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); +} + +/** + * Create and insert a new binding (@host_path:@guest_path) into the + * list of @tracee's bindings. The Talloc parent of this new binding + * is @context. This function returns NULL if an error occurred, + * otherwise a pointer to the newly created binding. + */ +Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context, + const char host_path[PATH_MAX], + const char guest_path[PATH_MAX]) +{ + Binding *binding; + + binding = talloc_zero(context, Binding); + if (binding == NULL) + return NULL; + + strcpy(binding->host.path, host_path); + strcpy(binding->guest.path, guest_path); + + binding->host.length = strlen(binding->host.path); + binding->guest.length = strlen(binding->guest.path); + + insort_binding2(tracee, binding); + + return 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; + + /* 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 && getenv("PROOT_IGNORE_MISSING_BINDINGS") == NULL) + note(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) { + note(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) { + note(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. 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); + + /* Does 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) { + note(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(get_root(tracee) != NULL); + 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) { + note(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; +}
diff --git a/5.1.0/src/path/binding.h b/5.1.0/src/path/binding.h new file mode 100644 index 0000000..de96aa6 --- /dev/null +++ b/5.1.0/src/path/binding.h
@@ -0,0 +1,58 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <limits.h> /* PATH_MAX, */ +#include <stdbool.h> + +#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 Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context, + const char host_path[PATH_MAX], const char guest_path[PATH_MAX]); +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(const Tracee* tracee, Side side, const char path[PATH_MAX]); +extern Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]); +extern const char *get_root(const Tracee* tracee); +extern int substitute_binding(const Tracee* tracee, Side side, char path[PATH_MAX]); +extern void remove_binding_from_all_lists(const Tracee *tracee, Binding *binding); + +#endif /* BINDING_H */
diff --git a/5.1.0/src/path/canon.c b/5.1.0/src/path/canon.c new file mode 100644 index 0000000..addb98a --- /dev/null +++ b/5.1.0/src/path/canon.c
@@ -0,0 +1,364 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* pid_t */ +#include <limits.h> /* PATH_MAX, */ +#include <sys/param.h> /* MAXSYMLINKS, */ +#include <errno.h> /* E*, */ +#include <sys/stat.h> /* lstat(2), S_ISREG(), */ +#include <unistd.h> /* access(2), lstat(2), */ +#include <string.h> /* string(3), */ +#include <assert.h> /* assert(3), */ +#include <stdio.h> /* sscanf(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" + +/** + * Put an end-of-string ('\0') right before the last component of @path. + */ +static inline 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 @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. + */ +static inline 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; +} + +/** + * 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 finality, + 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, finality); + 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, finality); + 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(finality) && !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]; + Finality finality; + 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 (strnlen(guest_path, PATH_MAX) >= PATH_MAX) + return -ENAMETOOLONG; + + 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; + finality = NOT_FINAL; + while (!IS_FINAL(finality)) { + Comparison comparison; + char component[NAME_MAX]; + char host_path[PATH_MAX]; + + finality = next_component(component, &cursor); + status = (int) finality; + if (status < 0) + return status; + + if (strcmp(component, ".") == 0) { + if (IS_FINAL(finality)) + finality = FINAL_DOT; + continue; + } + + if (strcmp(component, "..") == 0) { + pop_component(guest_path); + if (IS_FINAL(finality)) + finality = 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, finality, 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 || (finality == 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 (finality == FINAL_NORMAL) { + 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, finality, guest_path, host_path); + if (status < 0) + return status; + + /* Here, 'guest_path' shouldn't be a symlink anymore, + * unless it is a named file descriptor. */ + assert(status != 1 || sscanf(guest_path, "/proc/%*d/fd/%d", &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 (finality) { + 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; +}
diff --git a/5.1.0/src/path/canon.h b/5.1.0/src/path/canon.h new file mode 100644 index 0000000..98a3dad --- /dev/null +++ b/5.1.0/src/path/canon.h
@@ -0,0 +1,34 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdbool.h> +#include <limits.h> + +#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 */
diff --git a/5.1.0/src/path/glue.c b/5.1.0/src/path/glue.c new file mode 100644 index 0000000..a21c548 --- /dev/null +++ b/5.1.0/src/path/glue.c
@@ -0,0 +1,192 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* mkdir(2), lstat(2), */ +#include <sys/stat.h> /* mkdir(2), lstat(2), */ +#include <fcntl.h> /* mknod(2), */ +#include <unistd.h> /* mknod(2), lstat(2), unlink(2), rmdir(2), */ +#include <string.h> /* string(3), */ +#include <assert.h> /* assert(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <errno.h> /* errno, E* */ +#include <talloc.h> /* talloc_*, */ + +#include "path/binding.h" +#include "path/path.h" +#include "path/temp.h" +#include "cli/note.h" + +#include "compat.h" + +/** + * Remove @path if it is empty only. + * + * Note: this is a Talloc destructor. + */ +static int remove_placeholder(char *path) +{ + struct stat statl; + int status; + + status = lstat(path, &statl); + if (status) + return 0; /* Not fatal. */ + + if (!S_ISDIR(statl.st_mode)) { + if (statl.st_size != 0) + return 0; /* Not fatal. */ + status = unlink(path); + } + else + status = rmdir(path); + if (status) + return 0; /* Not fatal. */ + + return 0; +} + +/** + * Attach a copy of @path to the autofree context, and set its + * destructor to remove_placeholder(). + */ +static void set_placeholder_destructor(const char *path) +{ + TALLOC_CTX *autofreed; + char *placeholder; + + autofreed = talloc_autofree_context(); + if (autofreed == NULL) + return; + + placeholder = talloc_strdup(autofreed, path); + if (placeholder == NULL) + return; + + talloc_set_destructor(placeholder, remove_placeholder); +} + +/** + * 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 finality) +{ + 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 = create_temp_directory(NULL, tracee->tool_name); + if (tracee->glue == NULL) { + note(tracee, ERROR, INTERNAL, "can't create glue rootfs"); + return 0; + } + talloc_set_name_const(tracee->glue, "$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(finality)) { + 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); + + /* Remove placeholders from the guest rootfs once PRoot is + * terminated. */ + if (status >= 0 && !belongs_to_gluefs) + set_placeholder_destructor(host_path); + + /* 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(finality)) + return type; + + /* mkdir/mknod are supposed to always succeed in + * tracee->glue. */ + if (belongs_to_gluefs) { + note(tracee, WARNING, SYSTEM, "mkdir/mknod"); + return 0; + } + +create_binding: + /* Sanity checks. */ + if ( strnlen(tracee->glue, PATH_MAX) >= PATH_MAX + || strnlen(guest_path, PATH_MAX) >= PATH_MAX) { + note(tracee, WARNING, INTERNAL, "installing the binding: guest path too long"); + return 0; + } + + /* From the example, create the binding "/black" -> + * "$GLUE/black". */ + binding = insort_binding3(tracee, tracee->glue, tracee->glue, guest_path); + if (binding == NULL) + return 0; + + /* TODO: emulation of getdents(parent(guest_path)) to finalize + * the glue, "black" in getdents("/") from the example. */ + + return type; +}
diff --git a/5.1.0/src/path/glue.h b/5.1.0/src/path/glue.h new file mode 100644 index 0000000..5c82f0d --- /dev/null +++ b/5.1.0/src/path/glue.h
@@ -0,0 +1,34 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <limits.h> /* 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 finality); + +#endif /* GLUE_H */
diff --git a/5.1.0/src/path/path.c b/5.1.0/src/path/path.c new file mode 100644 index 0000000..3656c11 --- /dev/null +++ b/5.1.0/src/path/path.c
@@ -0,0 +1,732 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <string.h> /* string(3), */ +#include <stdarg.h> /* va_*(3), */ +#include <assert.h> /* assert(3), */ +#include <fcntl.h> /* AT_*, */ +#include <unistd.h> /* readlink*(2), *stat(2), getpid(2), */ +#include <sys/types.h> /* pid_t, */ +#include <sys/stat.h> /* S_ISDIR, */ +#include <dirent.h> /* opendir(3), readdir(3), */ +#include <stdio.h> /* snprintf(3), */ +#include <errno.h> /* E*, */ +#include <stddef.h> /* ptrdiff_t, */ + +#include "path/path.h" +#include "path/binding.h" +#include "path/canon.h" +#include "path/proc.h" +#include "extension/extension.h" +#include "cli/note.h" +#include "build.h" + +#include "compat.h" + +/** + * 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 status; + int i; + + result[0] = '\0'; + length = 0; + status = 0; + + /* Parse the list of variadic arguments. */ + va_start(paths, result); + for (i = 0; i < number_paths; i++) { + const char *path; + size_t path_length; + size_t new_length; + + path = va_arg(paths, const char *); + if (path == NULL) + continue; + path_length = strlen(path); + + /* A new path separator is needed. */ + if (length > 0 && result[length - 1] != '/' && path[0] != '/') { + new_length = length + path_length + 1; + if (new_length + 1 >= PATH_MAX) { + status = -ENAMETOOLONG; + break; + } + strcat(result + length, "/"); + strcat(result + length, path); + length = new_length; + } + /* There are already two path separators. */ + else if (length > 0 && result[length - 1] == '/' && path[0] == '/') { + new_length = length + path_length - 1; + if (new_length + 1 >= PATH_MAX) { + status = -ENAMETOOLONG; + break; + } + strcat(result + length, path + 1); + length += path_length - 1; + } + /* There's already one path separator or result[] is empty. */ + else { + new_length = length + path_length; + if (new_length + 1 >= PATH_MAX) { + status = -ENAMETOOLONG; + break; + } + strcat(result + length, path); + length += path_length; + } + + status = 0; + } + va_end(paths); + + return status; +} + +/** + * 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], const char *command) +{ + char path[PATH_MAX]; + const char *cursor; + struct stat statr; + int status; + + bool is_explicit; + bool found; + + assert(command != NULL); + is_explicit = (strchr(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) { + if (is_explicit && !S_ISREG(statr.st_mode)) { + note(tracee, ERROR, USER, "'%s' is not a regular file", command); + return -EACCES; + } + + if (is_explicit && (statr.st_mode & S_IXUSR) == 0) { + note(tracee, ERROR, USER, "'%s' is not executable", command); + return -EACCES; + } + + found = true; + + /* Don't dereference the final component to preserve + * argv0 in case it is a symlink to script. */ + (void) realpath2(tracee, host_path, command, false); + } + else + found = false; + + /* Is the the explicit command was found? */ + if (is_explicit) { + if (found) + return 0; + else + goto not_found; + } + + /* Otherwise search the command in $PATH. */ + paths = paths ?: getenv("PATH"); + if (paths == NULL || strcmp(paths, "") == 0) + goto not_found; + + 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) { + /* Don't dereference the final component to preserve + * argv0 in case it is a symlink to script. */ + (void) realpath2(tracee, host_path, path, false); + return 0; + } + } while (*(cursor - 1) != '\0'); + +not_found: + status = getcwd2(tracee, path); + if (status < 0) + strcpy(path, "<unknown>"); + + note(tracee, ERROR, USER, "'%s' not found (root = %s, cwd = %s, $PATH=%s)", + command, get_root(tracee), path, paths); + + /* Check if the command was found without any $PATH look-up + * but it didn't contain "/". */ + if (found && !is_explicit) + note(tracee, ERROR, USER, + "to execute a local program, use the './' prefix, for example: ./%s", command); + + 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'; + } +} + +/** + * Put in @path the result of readlink(/proc/@pid/fd/@fd). This + * function returns -errno if an error occured, othrwise 0. + */ +int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX]) +{ + char link[32]; /* 32 > sizeof("/proc//cwd") + sizeof(#ULONG_MAX) */ + int status; + + /* Format the path to the "virtual" link. */ + status = snprintf(link, sizeof(link), "/proc/%d/fd/%d", pid, fd); + if (status < 0) + return -EBADF; + if ((size_t) status >= sizeof(link)) + return -EBADF; + + /* Read the value of this "virtual" link. */ + status = readlink(link, path, PATH_MAX); + if (status < 0) + return -EBADF; + if (status >= PATH_MAX) + return -ENAMETOOLONG; + path[status] = '\0'; + + return 0; +} + +/** + * Copy in @result the equivalent of "@tracee->root + canon(@dir_fd + + * @user_path)". If @user_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 result[PATH_MAX], int dir_fd, + const char *user_path, bool deref_final) +{ + char guest_path[PATH_MAX]; + int status; + + /* Use "/" as the base if it is an absolute guest path. */ + if (user_path[0] == '/') { + strcpy(result, "/"); + } + /* It is relative to a directory referred by a descriptor, see + * openat(2) for details. */ + else if (dir_fd != AT_FDCWD) { + /* /proc/@tracee->pid/fd/@dir_fd -> result. */ + status = readlink_proc_pid_fd(tracee->pid, dir_fd, result); + if (status < 0) + return status; + + /* Named file descriptors may reference special + * objects like pipes, sockets, inodes, ... Such + * objects do not belong to the file-system. */ + if (result[0] != '/') + return -ENOTDIR; + + /* Remove the leading "root" part of the base + * (required!). */ + status = detranslate_path(tracee, result, NULL); + if (status < 0) + return status; + } + /* It is relative to the current working directory. */ + else { + status = getcwd2(tracee, result); + if (status < 0) + return status; + } + + VERBOSE(tracee, 2, "pid %d: translate(\"%s\" + \"%s\")", + tracee != NULL ? tracee->pid : 0, result, user_path); + + status = notify_extensions(tracee, GUEST_PATH, (intptr_t) result, (intptr_t) user_path); + if (status < 0) + return status; + if (status > 0) + goto skip; + + /* So far "result" was used as a base path, it's time to join + * it to the user path. */ + assert(result[0] == '/'); + status = join_paths(2, guest_path, result, user_path); + if (status < 0) + return status; + strcpy(result, "/"); + + /* Canonicalize regarding the new root. */ + status = canonicalize(tracee, guest_path, deref_final, result, 0); + if (status < 0) + return status; + + /* Final binding substitution to convert "result" into a host + * path, since canonicalize() works from the guest + * point-of-view. */ + status = substitute_binding(tracee, GUEST, result); + if (status < 0) + return status; + +skip: + VERBOSE(tracee, 2, "pid %d: -> \"%s\"", + tracee != NULL ? tracee->pid : 0, result); + 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; + ssize_t new_length; + + bool sanity_check; + bool follow_binding; + + /* Sanity check. */ + if (strnlen(path, PATH_MAX) >= PATH_MAX) + return -ENAMETOOLONG; + + /* 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) + return new_length; + 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 @host_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. Don't use + * readlinkat(2) here since it would require Linux >= + * 2.6.16 and Glibc >= 2.4, whereas PRoot is supposed + * to work on any Linux 2.6 systems. */ + + 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); + 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; +} + +/** + * Helper for list_open_fd(). + */ +static 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; +} + +/** + * Warn for files that are open. It is useful right after PRoot has + * attached a process. + */ +int list_open_fd(const Tracee *tracee) +{ + return foreach_fd(tracee, list_open_fd_callback); +} + +/** + * Substitute the first @old_prefix_length bytes of @path with + * @new_prefix (the caller has to provides a correct + * @new_prefix_length). This function returns the new length of + * @path. Note: this function takes care about special cases (like + * "/"). + */ +size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_length, + const char *new_prefix, size_t new_prefix_length) +{ + size_t path_length; + size_t new_length; + + path_length = strlen(path); + + assert(old_prefix_length < PATH_MAX); + assert(new_prefix_length < PATH_MAX); + + if (new_prefix_length == 1) { + /* Special case: "/foo" -> "/". Substitute "/foo/bin" + * with "/bin" not "//bin". */ + + new_length = path_length - old_prefix_length; + if (new_length != 0) + memmove(path, path + old_prefix_length, new_length); + else { + /* Special case: "/". */ + path[0] = '/'; + new_length = 1; + } + } + else if (old_prefix_length == 1) { + /* Special case: "/" -> "/foo". Substitute "/bin" with + * "/foo/bin" not "/foobin". */ + + new_length = new_prefix_length + path_length; + if (new_length >= PATH_MAX) + return -ENAMETOOLONG; + + if (path_length > 1) { + memmove(path + new_prefix_length, path, path_length); + memcpy(path, new_prefix, new_prefix_length); + } + else { + /* Special case: "/". */ + memcpy(path, new_prefix, new_prefix_length); + new_length = new_prefix_length; + } + } + else { + /* Generic case. */ + + new_length = path_length - old_prefix_length + new_prefix_length; + if (new_length >= PATH_MAX) + return -ENAMETOOLONG; + + memmove(path + new_prefix_length, + path + old_prefix_length, + path_length - old_prefix_length); + memcpy(path, new_prefix, new_prefix_length); + } + + assert(new_length < PATH_MAX); + path[new_length] = '\0'; + + return new_length; +}
diff --git a/5.1.0/src/path/path.h b/5.1.0/src/path/path.h new file mode 100644 index 0000000..1676056 --- /dev/null +++ b/5.1.0/src/path/path.h
@@ -0,0 +1,99 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* pid_t, */ +#include <fcntl.h> /* AT_FDCWD, */ +#include <limits.h> /* PATH_MAX, */ +#include <stdbool.h> + +#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; + +#define IS_FINAL(a) ((a) != NOT_FINAL) + +/* 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], const char *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 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); + +extern size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_length, + const char *new_prefix, size_t new_prefix_length); + +extern int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX]); + +/* 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 */
diff --git a/5.1.0/src/path/proc.c b/5.1.0/src/path/proc.c new file mode 100644 index 0000000..08d705b --- /dev/null +++ b/5.1.0/src/path/proc.c
@@ -0,0 +1,195 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdio.h> /* snprintf(3), */ +#include <string.h> /* strcmp(3), */ +#include <stdlib.h> /* atoi(3), strtol(3), */ +#include <errno.h> /* E*, */ +#include <assert.h> /* assert(3), */ + +#include "path/proc.h" +#include "tracee/tracee.h" +#include "path/path.h" +#include "path/binding.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/<PID>". */ + 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/<PID>" below, where <PID> is process + * monitored by PRoot. */ + break; + + default: + return DEFAULT; + } + + pid = atoi(base + strlen("/proc/")); + if (pid == 0) + return DEFAULT; + + /* Handle links in "/proc/<PID>/". */ + 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: + known_tracee = get_tracee(tracee, pid, false); + if (known_tracee == NULL) + return DEFAULT; + +#define SUBSTITUTE(name, string) \ + do { \ + if (strcmp(component, #name) != 0) \ + break; \ + \ + status = strlen(string); \ + if (status >= PATH_MAX) \ + return -EPERM; \ + \ + strncpy(result, string, status + 1); \ + return CANONICALIZE; \ + } while (0) + + /* Substitute link "/proc/<PID>/???" with the content + * of tracee->???. */ + SUBSTITUTE(exe, known_tracee->exe); + SUBSTITUTE(cwd, known_tracee->fs->cwd); + SUBSTITUTE(root, get_root(known_tracee)); +#undef SUBSTITUTE + return DEFAULT; + + case PATH1_IS_PREFIX: + /* Handle "/proc/<PID>/???" below. */ + break; + + default: + return DEFAULT; + } + + /* Handle links in "/proc/<PID>/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/<PID>/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: + break; + } + + 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 -errno if an error occured, 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). + */ +ssize_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char referer[PATH_MAX]) +{ + Action action; + char base[PATH_MAX]; + char *component; + + /* Sanity check. */ + if (strnlen(referer, PATH_MAX) >= PATH_MAX) + return -ENAMETOOLONG; + + 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); +}
diff --git a/5.1.0/src/path/proc.h b/5.1.0/src/path/proc.h new file mode 100644 index 0000000..55d26cc --- /dev/null +++ b/5.1.0/src/path/proc.h
@@ -0,0 +1,44 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <limits.h> + +#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 ssize_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX]); + +#endif /* PROC_H */
diff --git a/5.1.0/src/path/temp.c b/5.1.0/src/path/temp.c new file mode 100644 index 0000000..bbcfb5f --- /dev/null +++ b/5.1.0/src/path/temp.c
@@ -0,0 +1,305 @@ +#include <sys/types.h> /* stat(2), opendir(3), */ +#include <sys/stat.h> /* stat(2), chmod(2), */ +#include <unistd.h> /* stat(2), rmdir(2), unlink(2), readlink(2), */ +#include <errno.h> /* errno(2), */ +#include <dirent.h> /* readdir(3), opendir(3), */ +#include <string.h> /* strcmp(3), */ +#include <stdlib.h> /* free(3), */ +#include <stdio.h> /* P_tmpdir, */ +#include <talloc.h> /* talloc(3), */ + +#include "cli/note.h" + +/** + * Remove recursively the content of the current working directory. + * This latter has to lie in P_tmpdir (ie. "/tmp" on most systems). + * This function returns -1 if a fatal error occured (ie. the + * recursion must be stopped), the number of non-fatal errors + * otherwise. + * + * WARNING: this function changes the current working directory for + * the calling process. + */ +static int clean_temp_cwd() +{ + const size_t length_tmpdir = strlen(P_tmpdir); + char prefix[length_tmpdir]; + int nb_errors = 0; + int status; + DIR *dir; + + /* Sanity check: ensure the current directory lies in + * "/tmp". */ + status = readlink("/proc/self/cwd", prefix, length_tmpdir); + if (status < 0) { + note(NULL, WARNING, SYSTEM, "can't readlink '/proc/self/cwd'"); + return ++nb_errors; + } + if (strncmp(prefix, P_tmpdir, length_tmpdir) != 0) { + note(NULL, ERROR, INTERNAL, + "trying to remove a directory outside of '%s', " + "please report this error.\n", P_tmpdir); + return ++nb_errors; + } + + dir = opendir("."); + if (dir == NULL) { + note(NULL, WARNING, SYSTEM, "can't open '.'"); + return ++nb_errors; + } + + while (1) { + struct dirent *entry; + + errno = 0; + entry = readdir(dir); + if (entry == NULL) + break; + + if ( strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) + continue; + + status = chmod(entry->d_name, 0700); + if (status < 0) { + note(NULL, WARNING, SYSTEM, "cant chmod '%s'", entry->d_name); + nb_errors++; + continue; + } + + if (entry->d_type == DT_DIR) { + status = chdir(entry->d_name); + if (status < 0) { + note(NULL, WARNING, SYSTEM, "can't chdir '%s'", entry->d_name); + nb_errors++; + continue; + } + + /* Recurse. */ + status = clean_temp_cwd(); + if (status < 0) { + nb_errors = -1; + goto end; + } + nb_errors += status; + + status = chdir(".."); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chdir to '..'"); + nb_errors = -1; + goto end; + } + + status = rmdir(entry->d_name); + } + else { + status = unlink(entry->d_name); + } + if (status < 0) { + note(NULL, WARNING, SYSTEM, "can't remove '%s'", entry->d_name); + nb_errors++; + continue; + } + } + if (errno != 0) { + note(NULL, WARNING, SYSTEM, "can't readdir '.'"); + nb_errors++; + } + +end: + (void) closedir(dir); + return nb_errors; +} + +/** + * Remove recursively @path. This latter has to be a directory lying + * in P_tmpdir (ie. "/tmp" on most systems). This function returns -1 + * on error, otherwise 0. + */ +static int remove_temp_directory2(const char *path) +{ + int result; + int status; + char *cwd; + + cwd = get_current_dir_name(); + + status = chmod(path, 0700); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chmod '%s'", path); + result = -1; + goto end; + } + + status = chdir(path); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chdir to '%s'", path); + result = -1; + goto end; + } + + status = clean_temp_cwd(); + result = (status == 0 ? 0 : -1); + + /* Try to remove path even if something went wrong. */ + status = chdir(".."); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chdir to '..'"); + result = -1; + goto end; + } + + status = rmdir(path); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "cant remove '%s'", path); + result = -1; + goto end; + } + +end: + if (cwd != NULL) { + status = chdir(cwd); + if (status < 0) { + result = -1; + note(NULL, ERROR, SYSTEM, "can't chdir to '%s'", cwd); + } + free(cwd); + } + + return result; +} + +/** + * Like remove_temp_directory2() but always return 0. + * + * Note: this is a talloc destructor. + */ +static int remove_temp_directory(char *path) +{ + (void) remove_temp_directory2(path); + return 0; +} + +/** + * Remove the file @path. This function always returns 0. + * + * Note: this is a talloc destructor. + */ +static int remove_temp_file(char *path) +{ + int status; + + status = unlink(path); + if (status < 0) + note(NULL, ERROR, SYSTEM, "can't remove '%s'", path); + + return 0; +} + +/** + * Create a path name with the following format: + * "/tmp/@prefix-$PID-XXXXXX". The returned C string is either + * auto-freed if @context is NULL. This function returns NULL if an + * error occurred. + */ +char *create_temp_name(TALLOC_CTX *context, const char *prefix) +{ + char *name; + + if (context == NULL) + context = talloc_autofree_context(); + + name = talloc_asprintf(context, "%s/%s-%d-XXXXXX", P_tmpdir, prefix, getpid()); + if (name == NULL) { + note(NULL, ERROR, INTERNAL, "can't allocate memory"); + return NULL; + } + + return name; +} + +/** + * Create a directory that will be automatically removed either on + * PRoot termination if @context is NULL, or once its path name + * (attached to @context) is freed. This function returns NULL on + * error, otherwise the absolute path name to the created directory + * (@prefix-ed). + */ +const char *create_temp_directory(TALLOC_CTX *context, const char *prefix) +{ + char *name; + + name = create_temp_name(context, prefix); + if (name == NULL) + return NULL; + + name = mkdtemp(name); + if (name == NULL) { + note(NULL, ERROR, SYSTEM, "can't create temporary directory"); + return NULL; + } + + talloc_set_destructor(name, remove_temp_directory); + + return name; +} + +/** + * Create a file that will be automatically removed either on PRoot + * termination if @context is NULL, or once its path name (attached to + * @context) is freed. This function returns NULL on error, + * otherwise the absolute path name to the created file (@prefix-ed). + */ +const char *create_temp_file(TALLOC_CTX *context, const char *prefix) +{ + char *name; + int fd; + + name = create_temp_name(context, prefix); + if (name == NULL) + return NULL; + + fd = mkstemp(name); + if (fd < 0) { + note(NULL, ERROR, SYSTEM, "can't create temporary file"); + return NULL; + } + close(fd); + + talloc_set_destructor(name, remove_temp_file); + + return name; +} + +/** + * Like create_temp_file() but returns an open file stream to the + * created file. It's up to the caller to close returned stream. + */ +FILE* open_temp_file(TALLOC_CTX *context, const char *prefix) +{ + char *name; + FILE *file; + int fd; + + name = create_temp_name(context, prefix); + if (name == NULL) + return NULL; + + fd = mkstemp(name); + if (fd < 0) + goto error; + + talloc_set_destructor(name, remove_temp_file); + + file = fdopen(fd, "w"); + if (file == NULL) + goto error; + + return file; + +error: + if (fd >= 0) + close(fd); + note(NULL, ERROR, SYSTEM, "can't create temporary file"); + return NULL; +}
diff --git a/5.1.0/src/path/temp.h b/5.1.0/src/path/temp.h new file mode 100644 index 0000000..faa9341 --- /dev/null +++ b/5.1.0/src/path/temp.h
@@ -0,0 +1,33 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 TEMP_H +#define TEMP_H + +#include <talloc.h> + +extern char *create_temp_name(TALLOC_CTX *context, const char *prefix); +extern const char *create_temp_directory(TALLOC_CTX *context, const char *prefix); +extern const char *create_temp_file(TALLOC_CTX *context, const char *prefix); +extern FILE* open_temp_file(TALLOC_CTX *context, const char *prefix); + +#endif /* TEMP_H */
diff --git a/5.1.0/src/ptrace/ptrace.c b/5.1.0/src/ptrace/ptrace.c new file mode 100644 index 0000000..ef69cf5 --- /dev/null +++ b/5.1.0/src/ptrace/ptrace.c
@@ -0,0 +1,656 @@ +/* -*- 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 <sys/ptrace.h> /* PTRACE_*, */ +#include <errno.h> /* E*, */ +#include <assert.h> /* assert(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <signal.h> /* siginfo_t, */ +#include <sys/uio.h> /* struct iovec, */ +#include <sys/param.h> /* MIN(), MAX(), */ +#include <string.h> /* memcpy(3), */ + +#include "ptrace/ptrace.h" +#include "ptrace/user.h" +#include "tracee/tracee.h" +#include "syscall/sysnum.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "tracee/event.h" +#include "cli/note.h" +#include "arch.h" + +#include "compat.h" + +#if defined(ARCH_X86_64) || defined(ARCH_X86) +#include <asm/ldt.h> /* struct user_desc, */ +#endif + +#if defined(ARCH_X86_64) +#include <asm/prctl.h> /* ARCH_{G,S}ET_{F,G}S, */ +#endif + +#if defined(ARCH_ARM_EABI) +#define user_fpregs_struct user_fpregs +#endif + +#if defined(ARCH_ARM64) +#define user_fpregs_struct user_fpsimd_struct +#endif + +static const char *stringify_ptrace(enum __ptrace_request request) +{ +#define CASE_STR(a) case a: return #a; break; + switch ((int) request) { + CASE_STR(PTRACE_TRACEME) CASE_STR(PTRACE_PEEKTEXT) CASE_STR(PTRACE_PEEKDATA) + CASE_STR(PTRACE_PEEKUSER) CASE_STR(PTRACE_POKETEXT) CASE_STR(PTRACE_POKEDATA) + CASE_STR(PTRACE_POKEUSER) CASE_STR(PTRACE_CONT) CASE_STR(PTRACE_KILL) + CASE_STR(PTRACE_SINGLESTEP) CASE_STR(PTRACE_GETREGS) CASE_STR(PTRACE_SETREGS) + CASE_STR(PTRACE_GETFPREGS) CASE_STR(PTRACE_SETFPREGS) CASE_STR(PTRACE_ATTACH) + CASE_STR(PTRACE_DETACH) CASE_STR(PTRACE_GETFPXREGS) CASE_STR(PTRACE_SETFPXREGS) + CASE_STR(PTRACE_SYSCALL) CASE_STR(PTRACE_SETOPTIONS) CASE_STR(PTRACE_GETEVENTMSG) + CASE_STR(PTRACE_GETSIGINFO) CASE_STR(PTRACE_SETSIGINFO) CASE_STR(PTRACE_GETREGSET) + CASE_STR(PTRACE_SETREGSET) CASE_STR(PTRACE_SEIZE) CASE_STR(PTRACE_INTERRUPT) + CASE_STR(PTRACE_LISTEN) CASE_STR(PTRACE_SET_SYSCALL) + CASE_STR(PTRACE_GET_THREAD_AREA) CASE_STR(PTRACE_SET_THREAD_AREA) + CASE_STR(PTRACE_GETVFPREGS) CASE_STR(PTRACE_SINGLEBLOCK) CASE_STR(PTRACE_ARCH_PRCTL) + default: return "PTRACE_???"; } +} + +/** + * Translate the ptrace syscall made by @tracee into a "void" syscall + * in order to emulate the ptrace mechanism within PRoot. This + * function returns -errno if an error occured (unsupported request), + * otherwise 0. + */ +int translate_ptrace_enter(Tracee *tracee) +{ + /* The ptrace syscall have to be emulated since it can't be nested. */ + set_sysnum(tracee, PR_void); + return 0; +} + +/** + * Set @ptracee's tracer to @ptracer, and increment ptracees counter + * of this later. + */ +void attach_to_ptracer(Tracee *ptracee, Tracee *ptracer) +{ + bzero(&(PTRACEE), sizeof(PTRACEE)); + PTRACEE.ptracer = ptracer; + + PTRACER.nb_ptracees++; +} + +/** + * Unset @ptracee's tracer, and decrement ptracees counter of this + * later. + */ +void detach_from_ptracer(Tracee *ptracee) +{ + Tracee *ptracer = PTRACEE.ptracer; + + PTRACEE.ptracer = NULL; + + assert(PTRACER.nb_ptracees > 0); + PTRACER.nb_ptracees--; +} + +/** + * Emulate the ptrace syscall made by @tracee. This function returns + * -errno if an error occured (unsupported request), otherwise 0. + */ +int translate_ptrace_exit(Tracee *tracee) +{ + word_t request, pid, address, data, result; + Tracee *ptracee, *ptracer; + int forced_signal = -1; + int signal; + int status; + + /* Read ptrace parameters. */ + request = peek_reg(tracee, ORIGINAL, SYSARG_1); + pid = peek_reg(tracee, ORIGINAL, SYSARG_2); + address = peek_reg(tracee, ORIGINAL, SYSARG_3); + data = peek_reg(tracee, ORIGINAL, SYSARG_4); + + /* Propagate signedness for this special value. */ + if (is_32on64_mode(tracee) && pid == 0xFFFFFFFF) + pid = (word_t) -1; + + /* The TRACEME request is the only one used by a tracee. */ + if (request == PTRACE_TRACEME) { + ptracer = tracee->parent; + ptracee = tracee; + + /* The emulated ptrace in PRoot has the same + * limitation as the real ptrace in the Linux kernel: + * only one tracer per process. */ + if (PTRACEE.ptracer != NULL || ptracee == ptracer) + return -EPERM; + + attach_to_ptracer(ptracee, ptracer); + + /* Detect when the ptracer has gone to wait before the + * ptracee did the ptrace(ATTACHME) request. */ + if (PTRACER.waits_in == WAITS_IN_KERNEL) { + status = kill(ptracer->pid, SIGSTOP); + if (status < 0) + note(tracee, WARNING, INTERNAL, + "can't wake ptracer %d", ptracer->pid); + else { + ptracer->sigstop = SIGSTOP_IGNORED; + PTRACER.waits_in = WAITS_IN_PROOT; + } + } + + /* Disable seccomp acceleration for this tracee and + * all its children since we can't assume what are the + * syscalls its tracer is interested with. */ + if (tracee->seccomp == ENABLED) + tracee->seccomp = DISABLING; + + return 0; + } + + /* The ATTACH, SEIZE, and INTERRUPT requests are the only ones + * where the ptracee is in an unknown state. */ + if (request == PTRACE_ATTACH) { + ptracer = tracee; + ptracee = get_tracee(ptracer, pid, false); + if (ptracee == NULL) + return -ESRCH; + + /* The emulated ptrace in PRoot has the same + * limitation as the real ptrace in the Linux kernel: + * only one tracer per process. */ + if (PTRACEE.ptracer != NULL || ptracee == ptracer) + return -EPERM; + + attach_to_ptracer(ptracee, ptracer); + + /* The tracee is sent a SIGSTOP, but will not + * necessarily have stopped by the completion of this + * call. + * + * -- man 2 ptrace. */ + kill(pid, SIGSTOP); + + return 0; + } + + /* Here, the tracee is a ptracer. Also, the requested ptracee + * has to be in the "stopped for ptracer" state. */ + ptracer = tracee; + ptracee = get_stopped_ptracee(ptracer, pid, false, __WALL); + if (ptracee == NULL) { + static bool warned = false; + + /* Ensure we didn't get there only because inheritance + * mechanism has missed this one. */ + ptracee = get_tracee(tracee, pid, false); + if (ptracee != NULL && ptracee->exe == NULL && !warned) { + warned = true; + note(ptracer, WARNING, INTERNAL, "ptrace request to an unexpected ptracee"); + } + + return -ESRCH; + } + + /* Sanity checks. */ + if ( PTRACEE.is_zombie + || PTRACEE.ptracer != ptracer + || pid == (word_t) -1) + return -ESRCH; + + switch (request) { + case PTRACE_SYSCALL: + PTRACEE.ignore_syscalls = false; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_CONT: + PTRACEE.ignore_syscalls = true; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_SINGLESTEP: + ptracee->restart_how = PTRACE_SINGLESTEP; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_SINGLEBLOCK: + ptracee->restart_how = PTRACE_SINGLEBLOCK; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_DETACH: + detach_from_ptracer(ptracee); + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_KILL: + status = ptrace(request, pid, NULL, NULL); + break; /* Restart the ptracee. */ + + case PTRACE_SETOPTIONS: + PTRACEE.options = data; + return 0; /* Don't restart the ptracee. */ + + case PTRACE_GETEVENTMSG: { + status = ptrace(request, pid, NULL, &result); + if (status < 0) + return -errno; + + poke_word(ptracer, data, result); + if (errno != 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_PEEKUSER: + if (is_32on64_mode(ptracer)) { + address = convert_user_offset(address); + if (address == (word_t) -1) + return -EIO; + } + /* Fall through. */ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + errno = 0; + result = (word_t) ptrace(request, pid, address, NULL); + if (errno != 0) + return -errno; + + poke_word(ptracer, data, result); + if (errno != 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + + case PTRACE_POKEUSER: + if (is_32on64_mode(ptracer)) { + address = convert_user_offset(address); + if (address == (word_t) -1) + return -EIO; + } + + status = ptrace(request, pid, address, data); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + if (is_32on64_mode(ptracer)) { + word_t tmp; + + errno = 0; + tmp = (word_t) ptrace(PTRACE_PEEKDATA, ptracee->pid, address, NULL); + if (errno != 0) + return -errno; + + data |= (tmp & 0xFFFFFFFF00000000ULL); + } + + status = ptrace(request, pid, address, data); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + + case PTRACE_GETSIGINFO: { + siginfo_t siginfo; + + status = ptrace(request, pid, NULL, &siginfo); + if (status < 0) + return -errno; + + status = write_data(ptracer, data, &siginfo, sizeof(siginfo)); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETSIGINFO: { + siginfo_t siginfo; + + status = read_data(ptracer, &siginfo, data, sizeof(siginfo)); + if (status < 0) + return status; + + status = ptrace(request, pid, NULL, &siginfo); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_GETREGS: { + size_t size; + union { + struct user_regs_struct regs; + uint32_t regs32[USER32_NB_REGS]; + } buffer; + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + if (is_32on64_mode(tracee)) { + struct user_regs_struct regs64; + + memcpy(®s64, &buffer.regs, sizeof(struct user_regs_struct)); + convert_user_regs_struct(false, (uint64_t *) ®s64, buffer.regs32); + + size = sizeof(buffer.regs32); + } + else + size = sizeof(buffer.regs); + + status = write_data(ptracer, data, &buffer, size); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETREGS: { + size_t size; + union { + struct user_regs_struct regs; + uint32_t regs32[USER32_NB_REGS]; + } buffer; + + size = (is_32on64_mode(ptracer) + ? sizeof(buffer.regs32) + : sizeof(buffer.regs)); + + status = read_data(ptracer, &buffer, data, size); + if (status < 0) + return status; + + if (is_32on64_mode(ptracer)) { + uint32_t regs32[USER32_NB_REGS]; + + memcpy(regs32, buffer.regs32, sizeof(regs32)); + convert_user_regs_struct(true, (uint64_t *) &buffer.regs, regs32); + } + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_GETFPREGS: { + size_t size; + union { + struct user_fpregs_struct fpregs; + uint32_t fpregs32[USER32_NB_FPREGS]; + } buffer; + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + if (is_32on64_mode(tracee)) { +#if 0 /* TODO */ + struct user_fpregs_struct fpregs64; + + memcpy(&fpregs64, &buffer.fpregs, sizeof(struct user_fpregs_struct)); + convert_user_fpregs_struct(false, (uint64_t *) &fpregs64, buffer.fpregs32); +#else + static bool warned = false; + if (!warned) + note(ptracer, WARNING, INTERNAL, + "ptrace 32-bit request '%s' not supported on 64-bit yet", + stringify_ptrace(request)); + warned = true; + bzero(&buffer, sizeof(buffer)); +#endif + size = sizeof(buffer.fpregs32); + } + else + size = sizeof(buffer.fpregs); + + status = write_data(ptracer, data, &buffer, size); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETFPREGS: { + size_t size; + union { + struct user_fpregs_struct fpregs; + uint32_t fpregs32[USER32_NB_FPREGS]; + } buffer; + + size = (is_32on64_mode(ptracer) + ? sizeof(buffer.fpregs32) + : sizeof(buffer.fpregs)); + + status = read_data(ptracer, &buffer, data, size); + if (status < 0) + return status; + + if (is_32on64_mode(ptracer)) { +#if 0 /* TODO */ + uint32_t fpregs32[USER32_NB_FPREGS]; + + memcpy(fpregs32, buffer.fpregs32, sizeof(fpregs32)); + convert_user_fpregs_struct(true, (uint64_t *) &buffer.fpregs, fpregs32); +#else + static bool warned = false; + if (!warned) + note(ptracer, WARNING, INTERNAL, + "ptrace 32-bit request '%s' not supported on 64-bit yet", + stringify_ptrace(request)); + warned = true; + return -ENOTSUP; +#endif + } + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + +#if defined(ARCH_X86_64) || defined(ARCH_X86) + case PTRACE_GET_THREAD_AREA: { + struct user_desc user_desc; + + status = ptrace(request, pid, address, &user_desc); + if (status < 0) + return -errno; + + status = write_data(ptracer, data, &user_desc, sizeof(user_desc)); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SET_THREAD_AREA: { + struct user_desc user_desc; + + status = read_data(ptracer, &user_desc, data, sizeof(user_desc)); + if (status < 0) + return status; + + status = ptrace(request, pid, address, &user_desc); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } +#endif + + case PTRACE_GETREGSET: { + struct iovec local_iovec; + word_t remote_iovec_base; + word_t remote_iovec_len; + + remote_iovec_base = peek_word(ptracer, data); + if (errno != 0) + return -errno; + + remote_iovec_len = peek_word(ptracer, data + sizeof_word(ptracer)); + if (errno != 0) + return -errno; + + /* Sanity check. */ + assert(sizeof(local_iovec.iov_len) == sizeof(word_t)); + + local_iovec.iov_len = remote_iovec_len; + local_iovec.iov_base = talloc_zero_size(ptracer->ctx, remote_iovec_len); + if (local_iovec.iov_base == NULL) + return -ENOMEM; + + status = ptrace(PTRACE_GETREGSET, pid, address, &local_iovec); + if (status < 0) + return status; + + remote_iovec_len = local_iovec.iov_len = + MIN(remote_iovec_len, local_iovec.iov_len); + + /* Update remote vector content. */ + status = writev_data(ptracer, remote_iovec_base, &local_iovec, 1); + if (status < 0) + return status; + + /* Update remote vector length. */ + poke_word(ptracer, data + sizeof_word(ptracer), remote_iovec_len); + if (errno != 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETREGSET: { + struct iovec local_iovec; + word_t remote_iovec_base; + word_t remote_iovec_len; + + remote_iovec_base = peek_word(ptracer, data); + if (errno != 0) + return -errno; + + remote_iovec_len = peek_word(ptracer, data + sizeof_word(ptracer)); + if (errno != 0) + return -errno; + + /* Sanity check. */ + assert(sizeof(local_iovec.iov_len) == sizeof(word_t)); + + local_iovec.iov_len = remote_iovec_len; + local_iovec.iov_base = talloc_zero_size(ptracer->ctx, remote_iovec_len); + if (local_iovec.iov_base == NULL) + return -ENOMEM; + + /* Copy remote content into the local vector. */ + status = read_data(ptracer, local_iovec.iov_base, + remote_iovec_base, local_iovec.iov_len); + if (status < 0) + return status; + + status = ptrace(PTRACE_SETREGSET, pid, address, &local_iovec); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_GETVFPREGS: + case PTRACE_GETFPXREGS: { + static bool warned = false; + if (!warned) + note(ptracer, WARNING, INTERNAL, "ptrace request '%s' not supported yet", + stringify_ptrace(request)); + warned = true; + return -ENOTSUP; + } + +#if defined(ARCH_X86_64) + case PTRACE_ARCH_PRCTL: + switch (data) { + case ARCH_GET_GS: + case ARCH_GET_FS: + status = ptrace(request, pid, &result, data); + if (status < 0) + return -errno; + + poke_word(ptracer, address, result); + if (errno != 0) + return -errno; + break; + + case ARCH_SET_GS: + case ARCH_SET_FS: { + static bool warned = false; + if (!warned) + note(ptracer, WARNING, INTERNAL, + "ptrace request '%s' ARCH_SET_{G,F}S not supported yet", + stringify_ptrace(request)); + return -ENOTSUP; + } + + default: + return -ENOTSUP; + } + + return 0; /* Don't restart the ptracee. */ +#endif + + default: + note(ptracer, WARNING, INTERNAL, "ptrace request '%s' not supported yet", + stringify_ptrace(request)); + return -ENOTSUP; + } + + /* Now, the initial tracee's event can be handled. */ + signal = PTRACEE.event4.proot.pending + ? handle_tracee_event(ptracee, PTRACEE.event4.proot.value) + : PTRACEE.event4.proot.value; + + /* The restarting signal from the ptracer overrides the + * restarting signal from PRoot. */ + if (forced_signal != -1) + signal = forced_signal; + + (void) restart_tracee(ptracee, signal); + + return status; +}
diff --git a/5.1.0/src/ptrace/ptrace.h b/5.1.0/src/ptrace/ptrace.h new file mode 100644 index 0000000..a5ad170 --- /dev/null +++ b/5.1.0/src/ptrace/ptrace.h
@@ -0,0 +1,36 @@ +/* -*- 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 PTRACE_H +#define PTRACE_H + +#include "tracee/tracee.h" + +extern int translate_ptrace_enter(Tracee *tracee); +extern int translate_ptrace_exit(Tracee *tracee); +extern void attach_to_ptracer(Tracee *ptracee, Tracee *ptracer); +extern void detach_from_ptracer(Tracee *ptracee); + +#define PTRACEE (ptracee->as_ptracee) +#define PTRACER (ptracer->as_ptracer) + +#endif /* PTRACE_H */
diff --git a/5.1.0/src/ptrace/user.c b/5.1.0/src/ptrace/user.c new file mode 100644 index 0000000..4a27fba --- /dev/null +++ b/5.1.0/src/ptrace/user.c
@@ -0,0 +1,166 @@ +/* -*- 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 <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/user.h> +#include <stddef.h> + +#include "ptrace/user.h" +#include "cli/note.h" + +#if defined(ARCH_X86_64) + +/** + * Return the index in the "regs" field of a 64-bit "user" area that + * corresponds to the specified @index in the "regs" field of a 32-bit + * "user" area. + */ +static inline size_t convert_user_regs_index(size_t index) +{ + static size_t mapping[USER32_NB_REGS] = { + 05, /* ?bx */ 11, /* ?cx */ 12, /* ?dx */ + 13, /* ?si */ 14, /* ?di */ 04, /* ?bp */ + 10, /* ?ax */ 23, /* ds */ 24, /* es */ + 25, /* fs */ 26, /* gs */ 15, /* orig_?ax */ + 16, /* ?ip */ 17, /* cs */ 18, /* eflags */ + 19, /* ?sp */ 20, /* ss */ }; + + /* Sanity check. */ + assert(index < USER32_NB_REGS); + + return mapping[index]; +} + +/* Layout of a 32-bit "user" area. */ +#define USER32_REGS_OFFSET 0 +#define USER32_REGS_SIZE (USER32_NB_REGS * sizeof(uint32_t)) +#define USER32_FPVALID_OFFSET (USER32_REGS_OFFSET + USER32_REGS_SIZE) +#define USER32_I387_OFFSET (USER32_FPVALID_OFFSET + sizeof(uint32_t)) +#define USER32_I387_SIZE (USER32_NB_FPREGS * sizeof(uint32_t)) +#define USER32_TSIZE_OFFSET (USER32_I387_OFFSET + USER32_I387_SIZE) +#define USER32_DSIZE_OFFSET (USER32_TSIZE_OFFSET + sizeof(uint32_t)) +#define USER32_SSIZE_OFFSET (USER32_DSIZE_OFFSET + sizeof(uint32_t)) +#define USER32_START_CODE_OFFSET (USER32_SSIZE_OFFSET + sizeof(uint32_t)) +#define USER32_START_STACK_OFFSET (USER32_START_CODE_OFFSET + sizeof(uint32_t)) +#define USER32_SIGNAL_OFFSET (USER32_START_STACK_OFFSET + sizeof(uint32_t)) +#define USER32_RESERVED_OFFSET (USER32_SIGNAL_OFFSET + sizeof(uint32_t)) +#define USER32_AR0_OFFSET (USER32_RESERVED_OFFSET + sizeof(uint32_t)) +#define USER32_FPSTATE_OFFSET (USER32_AR0_OFFSET + sizeof(uint32_t)) +#define USER32_MAGIC_OFFSET (USER32_FPSTATE_OFFSET + sizeof(uint32_t)) +#define USER32_COMM_OFFSET (USER32_MAGIC_OFFSET + sizeof(uint32_t)) +#define USER32_COMM_SIZE (32 * sizeof(uint8_t)) +#define USER32_DEBUGREG_OFFSET (USER32_COMM_OFFSET + USER32_COMM_SIZE) +#define USER32_DEBUGREG_SIZE (8 * sizeof(uint32_t)) + +/** + * Return the offset in the "debugreg" field of a 64-bit "user" area + * that corresponds to the specified @offset in the "debugreg" field + * of a 32-bit "user" area. + */ +static inline size_t convert_user_debugreg_offset(size_t offset) +{ + size_t index; + + /* Sanity check. */ + assert(offset >= USER32_DEBUGREG_OFFSET + && offset < USER32_DEBUGREG_OFFSET + USER32_DEBUGREG_SIZE); + + index = (offset - USER32_DEBUGREG_OFFSET) / sizeof(uint32_t); + return offsetof(struct user, u_debugreg) + index * sizeof(uint64_t); +} + +/** + * Return the offset in a 64-bit "user" area that corresponds to the + * specified @offset in a 32-bit "user" area. This function returns + * "(word_t) -1" if the specified @offset is invalid. + */ +word_t convert_user_offset(word_t offset) +{ + const char *area_name = NULL; + + if (/* offset >= 0 && */ offset < USER32_REGS_OFFSET + USER32_REGS_SIZE) { + /* Sanity checks. */ + if ((offset % sizeof(uint32_t)) != 0) + return (word_t) -1; + + return convert_user_regs_index(offset / sizeof(uint32_t)) * sizeof(uint64_t); + } + else if (offset == USER32_FPVALID_OFFSET) + area_name = "fpvalid"; /* Not yet supported. */ + else if (offset >= USER32_I387_OFFSET && offset < USER32_I387_OFFSET + USER32_I387_SIZE) + area_name = "i387"; /* Not yet supported. */ + else if (offset == USER32_TSIZE_OFFSET) + area_name = "tsize"; /* Not yet supported. */ + else if (offset == USER32_DSIZE_OFFSET) + area_name = "dsize"; /* Not yet supported. */ + else if (offset == USER32_SSIZE_OFFSET) + area_name = "ssize"; /* Not yet supported. */ + else if (offset == USER32_START_CODE_OFFSET) + area_name = "start_code"; /* Not yet supported. */ + else if (offset == USER32_START_STACK_OFFSET) + area_name = "start_stack"; /* Not yet supported. */ + else if (offset == USER32_SIGNAL_OFFSET) + area_name = "signal"; /* Not yet supported. */ + else if (offset == USER32_RESERVED_OFFSET) + area_name = "reserved"; /* Not yet supported. */ + else if (offset == USER32_AR0_OFFSET) + area_name = "ar0"; /* Not yet supported. */ + else if (offset == USER32_FPSTATE_OFFSET) + area_name = "fpstate"; /* Not yet supported. */ + else if (offset == USER32_MAGIC_OFFSET) + area_name = "magic"; /* Not yet supported. */ + else if (offset >= USER32_COMM_OFFSET && offset < USER32_COMM_OFFSET + USER32_COMM_SIZE) + area_name = "comm"; /* Not yet supported. */ + else if (offset >= USER32_DEBUGREG_OFFSET && offset < USER32_DEBUGREG_OFFSET + USER32_DEBUGREG_SIZE) + return convert_user_debugreg_offset(offset); + else + area_name = "<unknown>"; + + note(NULL, WARNING, INTERNAL, "ptrace user area '%s' not supported yet", area_name); + return (word_t) -1; /* Unknown offset. */ +} + +/** + * Convert the "regs" field from a 64-bit "user" area into a "regs" + * field from a 32-bit "user" area, or vice versa according to + * @reverse. + */ +void convert_user_regs_struct(bool reverse, uint64_t *user_regs64, + uint32_t user_regs32[USER32_NB_REGS]) +{ + size_t index32; + + for (index32 = 0; index32 < USER32_NB_REGS; index32++) { + size_t index64 = convert_user_regs_index(index32); + assert(index64 != (size_t) -1); + + if (reverse) + user_regs64[index64] = (uint64_t) user_regs32[index32]; + else + user_regs32[index32] = (uint32_t) user_regs64[index64]; + } +} + +#endif /* ARCH_X86_64 */
diff --git a/5.1.0/src/ptrace/user.h b/5.1.0/src/ptrace/user.h new file mode 100644 index 0000000..152f355 --- /dev/null +++ b/5.1.0/src/ptrace/user.h
@@ -0,0 +1,56 @@ +/* -*- 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 <stdint.h> +#include <stdbool.h> +#include <assert.h> + +#include "arch.h" +#include "attribute.h" + +#if defined(ARCH_X86_64) + +#define USER32_NB_REGS 17 +#define USER32_NB_FPREGS 27 + +extern word_t convert_user_offset(word_t offset); +extern void convert_user_regs_struct(bool reverse, uint64_t *user_regs64, + uint32_t user_regs32[USER32_NB_REGS]); + +#else + +#define USER32_NB_REGS 0 +#define USER32_NB_FPREGS 0 + +static inline word_t convert_user_offset(word_t offset UNUSED) +{ + assert(0); +} + +static inline void convert_user_regs_struct(bool reverse UNUSED, + uint64_t *user_regs64 UNUSED, + uint32_t user_regs32[USER32_NB_REGS] UNUSED) +{ + assert(0); +} + +#endif
diff --git a/5.1.0/src/ptrace/wait.c b/5.1.0/src/ptrace/wait.c new file mode 100644 index 0000000..1b172ce --- /dev/null +++ b/5.1.0/src/ptrace/wait.c
@@ -0,0 +1,360 @@ +/* -*- 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 <sys/ptrace.h> /* PTRACE_*, */ +#include <errno.h> /* E*, */ +#include <assert.h> /* assert(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <signal.h> /* SIG*, */ +#include <talloc.h> /* talloc*, */ + +#include "ptrace/wait.h" +#include "ptrace/ptrace.h" +#include "syscall/sysnum.h" +#include "syscall/chain.h" +#include "tracee/tracee.h" +#include "tracee/event.h" +#include "tracee/reg.h" +#include "tracee/mem.h" + +#include "attribute.h" + +static const char *stringify_event(int event) UNUSED; +static const char *stringify_event(int event) +{ + if (WIFEXITED(event)) + return "exited"; + else if (WIFSIGNALED(event)) + return "signaled"; + else if (WIFCONTINUED(event)) + return "continued"; + else if (WIFSTOPPED(event)) { + switch ((event & 0xfff00) >> 8) { + case SIGTRAP: + return "stopped: SIGTRAP"; + case SIGTRAP | 0x80: + return "stopped: SIGTRAP: 0x80"; + case SIGTRAP | PTRACE_EVENT_VFORK << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_VFORK"; + case SIGTRAP | PTRACE_EVENT_FORK << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_FORK"; + case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_VFORK_DONE"; + case SIGTRAP | PTRACE_EVENT_CLONE << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_CLONE"; + case SIGTRAP | PTRACE_EVENT_EXEC << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_EXEC"; + case SIGTRAP | PTRACE_EVENT_EXIT << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_EXIT"; + case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_SECCOMP2"; + case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_SECCOMP"; + case SIGSTOP: + return "stopped: SIGSTOP"; + default: + return "stopped: unknown"; + } + } + return "unknown"; +} + +/** + * Translate the wait syscall made by @ptracer into a "void" syscall + * if the expected pid is one of its ptracees, in order to emulate the + * ptrace mechanism within PRoot. This function returns -errno if an + * error occured (unsupported request), otherwise 0. + */ +int translate_wait_enter(Tracee *ptracer) +{ + Tracee *ptracee; + pid_t pid; + + PTRACER.waits_in = WAITS_IN_KERNEL; + + /* Don't emulate the ptrace mechanism if it's not a ptracer. */ + if (PTRACER.nb_ptracees == 0) + return 0; + + /* Don't emulate the ptrace mechanism if the requested pid is + * not a ptracee. */ + pid = (pid_t) peek_reg(ptracer, ORIGINAL, SYSARG_1); + if (pid != -1) { + ptracee = get_tracee(ptracer, pid, false); + if (ptracee == NULL || PTRACEE.ptracer != ptracer) + return 0; + } + + /* This syscall is canceled at the enter stage in order to be + * handled at the exit stage. */ + set_sysnum(ptracer, PR_void); + PTRACER.waits_in = WAITS_IN_PROOT; + + return 0; +} + +/** + * Update pid & wait status of @ptracer's wait(2) for the given + * @ptracee. This function returns -errno if an error occurred, 0 if + * the wait syscall will be restarted (ie. the event is discarded), + * otherwise @ptracee's pid. + */ +static int update_wait_status(Tracee *ptracer, Tracee *ptracee) +{ + word_t address; + int result; + + /* Special case: the Linux kernel reports the terminating + * event issued by a process to both its parent and its + * tracer, except when they are the same. In this case the + * Linux kernel reports the terminating event only once to the + * tracing parent ... */ + if (PTRACEE.ptracer == ptracee->parent + && (WIFEXITED(PTRACEE.event4.ptracer.value) + || WIFSIGNALED(PTRACEE.event4.ptracer.value))) { + /* ... So hide this terminating event (toward its + * tracer, ie. PRoot) and make the second one appear + * (towards its parent, ie. the ptracer). This will + * ensure its exit status is collected from a kernel + * point-of-view (ie. it doesn't stay a zombie + * forever). */ + restart_original_syscall(ptracer); + + /* Detach this ptracee from its ptracer, PRoot doesn't + * have anything else to emulate. */ + detach_from_ptracer(ptracee); + + /* Zombies can rest in peace once the ptracer is + * notified. */ + if (PTRACEE.is_zombie) + TALLOC_FREE(ptracee); + + return 0; + } + + address = peek_reg(ptracer, ORIGINAL, SYSARG_2); + if (address != 0) { + poke_int32(ptracer, address, PTRACEE.event4.ptracer.value); + if (errno != 0) + return -errno; + } + + PTRACEE.event4.ptracer.pending = false; + + /* Be careful; ptracee might get freed before its pid is + * returned. */ + result = ptracee->pid; + + /* Zombies can rest in peace once the ptracer is notified. */ + if (PTRACEE.is_zombie) { + detach_from_ptracer(ptracee); + TALLOC_FREE(ptracee); + } + + return result; +} + +/** + * Emulate the wait* syscall made by @ptracer if it was in the context + * of the ptrace mechanism. This function returns -errno if an error + * occured, otherwise the pid of the expected tracee. + */ +int translate_wait_exit(Tracee *ptracer) +{ + Tracee *ptracee; + word_t options; + int status; + pid_t pid; + + assert(PTRACER.waits_in == WAITS_IN_PROOT); + PTRACER.waits_in = DOESNT_WAIT; + + pid = (pid_t) peek_reg(ptracer, ORIGINAL, SYSARG_1); + options = peek_reg(ptracer, ORIGINAL, SYSARG_3); + + /* Is there such a stopped ptracee with an event not yet + * passed to its ptracer? */ + ptracee = get_stopped_ptracee(ptracer, pid, true, options); + if (ptracee == NULL) { + /* Is there still living ptracees? */ + if (PTRACER.nb_ptracees == 0) + return -ECHILD; + + /* Non blocking wait(2) ? */ + if ((options & WNOHANG) != 0) { + /* if WNOHANG was specified and one or more + * child(ren) specified by pid exist, but have + * not yet changed state, then 0 is returned. + * On error, -1 is returned. + * + * -- man 2 waitpid */ + return (has_ptracees(ptracer, pid, options) ? 0 : -ECHILD); + } + + /* Otherwise put this ptracer in the "waiting for + * ptracee" state, it will be woken up in + * handle_ptracee_event() later. */ + PTRACER.wait_pid = pid; + PTRACER.wait_options = options; + + return 0; + } + + status = update_wait_status(ptracer, ptracee); + if (status < 0) + return status; + + return status; +} + +/** + * For the given @ptracee, pass its current @event to its ptracer if + * this latter is waiting for it, otherwise put the @ptracee in the + * "waiting for ptracer" state. This function returns whether + * @ptracee shall be kept in the stop state or not. + */ +bool handle_ptracee_event(Tracee *ptracee, int event) +{ + bool handled_by_proot_first = false; + Tracee *ptracer = PTRACEE.ptracer; + bool keep_stopped; + + assert(ptracer != NULL); + + /* Remember what the event initially was, this will be + * required by PRoot to handle this event later. */ + PTRACEE.event4.proot.value = event; + PTRACEE.event4.proot.pending = true; + + /* By default, this ptracee should be kept stopped until its + * ptracer restarts it. */ + keep_stopped = true; + + /* Not all events are expected for this ptracee. */ + if (WIFSTOPPED(event)) { + switch ((event & 0xfff00) >> 8) { + case SIGTRAP | 0x80: + if (PTRACEE.ignore_syscalls || PTRACEE.ignore_loader_syscalls) + return false; + + if ((PTRACEE.options & PTRACE_O_TRACESYSGOOD) == 0) + event &= ~(0x80 << 8); + + handled_by_proot_first = IS_IN_SYSEXIT(ptracee); + break; + +#define PTRACE_EVENT_VFORKDONE PTRACE_EVENT_VFORK_DONE +#define CASE_FILTER_EVENT(name) \ + case SIGTRAP | PTRACE_EVENT_ ##name << 8: \ + if ((PTRACEE.options & PTRACE_O_TRACE ##name) == 0) \ + return false; \ + PTRACEE.tracing_started = true; \ + handled_by_proot_first = true; \ + break; + + CASE_FILTER_EVENT(FORK); + CASE_FILTER_EVENT(VFORK); + CASE_FILTER_EVENT(VFORKDONE); + CASE_FILTER_EVENT(CLONE); + CASE_FILTER_EVENT(EXIT); + CASE_FILTER_EVENT(EXEC); + + /* Never reached. */ + assert(0); + + case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: + case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: + /* These events are not supported [yet?] under + * ptrace emulation. */ + return false; + + default: + PTRACEE.tracing_started = true; + break; + } + } + /* In these cases, the ptracee isn't really alive anymore. To + * ensure it will not be in limbo, PRoot restarts it whether + * its ptracer is waiting for it or not. */ + else if (WIFEXITED(event) || WIFSIGNALED(event)) { + PTRACEE.tracing_started = true; + keep_stopped = false; + } + + /* A process is not traced right from the TRACEME request; it + * is traced from the first received signal, whether it was + * raised by the process itself (implicitly or explicitly), or + * it was induced by a PTRACE_EVENT_*. */ + if (!PTRACEE.tracing_started) + return false; + + /* Under some circumstances, the event must be handled by + * PRoot first. */ + if (handled_by_proot_first) { + int signal; + signal = handle_tracee_event(ptracee, PTRACEE.event4.proot.value); + PTRACEE.event4.proot.value = signal; + + /* The computed signal is always 0 since we can come + * in this block only on sysexit and special events + * (as for now). */ + assert(signal == 0); + } + + /* Remember what the new event is, this will be required by + the ptracer in translate_ptrace_exit() in order to restart + this ptracee. */ + PTRACEE.event4.ptracer.value = event; + PTRACEE.event4.ptracer.pending = true; + + /* Notify asynchronously the ptracer about this event, as the + * kernel does. */ + kill(ptracer->pid, SIGCHLD); + + /* Note: wait_pid is set in translate_wait_exit() if no + * ptracee event was pending when the ptracer started to + * wait. */ + if ( (PTRACER.wait_pid == -1 || PTRACER.wait_pid == ptracee->pid) + && EXPECTED_WAIT_CLONE(PTRACER.wait_options, ptracee)) { + bool restarted; + int status; + + status = update_wait_status(ptracer, ptracee); + if (status == 0) + chain_next_syscall(ptracer); + else + poke_reg(ptracer, SYSARG_RESULT, (word_t) status); + + /* Write ptracer's register cache back. */ + (void) push_regs(ptracer); + + /* Restart the ptracer. */ + PTRACER.wait_pid = 0; + restarted = restart_tracee(ptracer, 0); + if (!restarted) + keep_stopped = false; + + return keep_stopped; + } + + return keep_stopped; +}
diff --git a/5.1.0/src/ptrace/wait.h b/5.1.0/src/ptrace/wait.h new file mode 100644 index 0000000..7f94303 --- /dev/null +++ b/5.1.0/src/ptrace/wait.h
@@ -0,0 +1,47 @@ +/* -*- 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 PTRACE_WAIT_H +#define PTRACE_WAIT_H + +#include "tracee/tracee.h" + +extern int translate_wait_enter(Tracee *ptracer); +extern int translate_wait_exit(Tracee *ptracer); +extern bool handle_ptracee_event(Tracee *ptracee, int wait_status); + +/* __WCLONE: Wait for "clone" children only. If omitted then wait for + * "non-clone" children only. (A "clone" child is one which delivers + * no signal, or a signal other than SIGCHLD to its parent upon + * termination.) This option is ignored if __WALL is also specified. + * + * __WALL: Wait for all children, regardless of type ("clone" or + * "non-clone"). + * + * -- wait(2) man-page + */ +#define EXPECTED_WAIT_CLONE(wait_options, tracee) \ + ((((wait_options) & __WALL) != 0) \ + || ((((wait_options) & __WCLONE) != 0) && (tracee)->clone) \ + || ((((wait_options) & __WCLONE) == 0) && !(tracee)->clone)) + +#endif /* PTRACE_WAIT_H */
diff --git a/5.1.0/src/syscall/chain.c b/5.1.0/src/syscall/chain.c new file mode 100644 index 0000000..b2d28bc --- /dev/null +++ b/5.1.0/src/syscall/chain.c
@@ -0,0 +1,156 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <talloc.h> /* talloc*, */ +#include <sys/queue.h> /* STAILQ_*, */ +#include <errno.h> /* E*, */ +#include <assert.h> /* assert(3), */ + +#include "syscall/chain.h" +#include "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "arch.h" + +struct chained_syscall { + Sysnum sysnum; + word_t sysargs[6]; + STAILQ_ENTRY(chained_syscall) link; +}; + +STAILQ_HEAD(chained_syscalls, chained_syscall); + +/** + * Append a new syscall (@sysnum, @sysarg_*) to the list of + * "unrequested" syscalls for the given @tracee. These new syscalls + * will be triggered in order once the current syscall is done. The + * caller is free to force the last result of this syscall chain in + * @tracee->chain.final_result. This function returns -errno if an + * error occurred, otherwise 0. + */ +int register_chained_syscall(Tracee *tracee, Sysnum sysnum, + word_t sysarg_1, word_t sysarg_2, word_t sysarg_3, + word_t sysarg_4, word_t sysarg_5, word_t sysarg_6) +{ + struct chained_syscall *syscall; + + if (tracee->chain.syscalls == NULL) { + tracee->chain.syscalls = talloc_zero(tracee, struct chained_syscalls); + if (tracee->chain.syscalls == NULL) + return -ENOMEM; + + STAILQ_INIT(tracee->chain.syscalls); + } + + syscall = talloc_zero(tracee->chain.syscalls, struct chained_syscall); + if (syscall == NULL) + return -ENOMEM; + + syscall->sysnum = sysnum; + syscall->sysargs[0] = sysarg_1; + syscall->sysargs[1] = sysarg_2; + syscall->sysargs[2] = sysarg_3; + syscall->sysargs[3] = sysarg_4; + syscall->sysargs[4] = sysarg_5; + syscall->sysargs[5] = sysarg_6; + + STAILQ_INSERT_TAIL(tracee->chain.syscalls, syscall, link); + + return 0; +} + +/** + * Use/remove the first element of @tracee->chain.syscalls to forge a + * new syscall. This function should be called only at the end of in + * the sysexit stage. + */ +void chain_next_syscall(Tracee *tracee) +{ + struct chained_syscall *syscall; + word_t instr_pointer; + word_t sysnum; + + assert(tracee->chain.syscalls != NULL); + + /* No more chained syscalls: force the result of the initial + * syscall (the one explicitly requested by the tracee). */ + if (STAILQ_EMPTY(tracee->chain.syscalls)) { + TALLOC_FREE(tracee->chain.syscalls); + + if (tracee->chain.force_final_result) + poke_reg(tracee, SYSARG_RESULT, tracee->chain.final_result); + + tracee->chain.force_final_result = false; + tracee->chain.final_result = 0; + + return; + } + + /* Original register values will be restored right after the + * last chained syscall. */ + tracee->restore_original_regs = false; + + /* The list of chained syscalls is a FIFO. */ + syscall = STAILQ_FIRST(tracee->chain.syscalls); + STAILQ_REMOVE_HEAD(tracee->chain.syscalls, link); + + poke_reg(tracee, SYSARG_1, syscall->sysargs[0]); + poke_reg(tracee, SYSARG_2, syscall->sysargs[1]); + poke_reg(tracee, SYSARG_3, syscall->sysargs[2]); + poke_reg(tracee, SYSARG_4, syscall->sysargs[3]); + poke_reg(tracee, SYSARG_5, syscall->sysargs[4]); + poke_reg(tracee, SYSARG_6, syscall->sysargs[5]); + + sysnum = detranslate_sysnum(get_abi(tracee), syscall->sysnum); + poke_reg(tracee, SYSTRAP_NUM, sysnum); + + /* Move the instruction pointer back to the original trap. */ + instr_pointer = peek_reg(tracee, CURRENT, INSTR_POINTER); + poke_reg(tracee, INSTR_POINTER, instr_pointer - SYSTRAP_SIZE); +} + +/** + * Force the last result of the @tracee's current syscall chain to be + * @forced_result. + */ +void force_chain_final_result(Tracee *tracee, word_t forced_result) +{ + tracee->chain.force_final_result = true; + tracee->chain.final_result = forced_result; +} + +/** + * Restart the original syscall of the given @tracee. The result of + * the current syscall will be overwritten. This function returns the + * same status as register_chained_syscall(). + */ +int restart_original_syscall(Tracee *tracee) +{ + return register_chained_syscall(tracee, + get_sysnum(tracee, ORIGINAL), + peek_reg(tracee, ORIGINAL, SYSARG_1), + peek_reg(tracee, ORIGINAL, SYSARG_2), + peek_reg(tracee, ORIGINAL, SYSARG_3), + peek_reg(tracee, ORIGINAL, SYSARG_4), + peek_reg(tracee, ORIGINAL, SYSARG_5), + peek_reg(tracee, ORIGINAL, SYSARG_6)); +}
diff --git a/5.1.0/src/syscall/chain.h b/5.1.0/src/syscall/chain.h new file mode 100644 index 0000000..7c4f056 --- /dev/null +++ b/5.1.0/src/syscall/chain.h
@@ -0,0 +1,41 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 CHAIN_H +#define CHAIN_H + +#include "tracee/tracee.h" +#include "syscall/sysnum.h" +#include "arch.h" + +extern int register_chained_syscall(Tracee *tracee, Sysnum sysnum, + word_t sysarg_1, word_t sysarg_2, word_t sysarg_3, + word_t sysarg_4, word_t sysarg_5, word_t sysarg_6); + +extern void force_chain_final_result(Tracee *tracee, word_t forced_result); + +extern int restart_original_syscall(Tracee *tracee); + +extern void chain_next_syscall(Tracee *tracee); + + +#endif /* CHAIN_H */
diff --git a/5.1.0/src/syscall/enter.c b/5.1.0/src/syscall/enter.c new file mode 100644 index 0000000..2c5ad92 --- /dev/null +++ b/5.1.0/src/syscall/enter.c
@@ -0,0 +1,576 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* errno(3), E* */ +#include <talloc.h> /* talloc_*, */ +#include <sys/un.h> /* struct sockaddr_un, */ +#include <linux/net.h> /* SYS_*, */ +#include <fcntl.h> /* AT_FDCWD, */ +#include <limits.h> /* PATH_MAX, */ + +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "syscall/socket.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "syscall/heap.h" +#include "extension/extension.h" +#include "execve/execve.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "path/path.h" +#include "path/canon.h" +#include "arch.h" + +/** + * 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); +} + +/** + * 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. + */ +int 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; + bool special = false; + + status = notify_extensions(tracee, SYSCALL_ENTER_START, 0, 0); + if (status < 0) + goto end; + if (status > 0) + return 0; + + /* Translate input arguments. */ + syscall_number = get_sysnum(tracee, ORIGINAL); + switch (syscall_number) { + default: + /* Nothing to do. */ + status = 0; + break; + + case PR_execve: + status = translate_execve_enter(tracee); + break; + + case PR_ptrace: + status = translate_ptrace_enter(tracee); + break; + + case PR_wait4: + case PR_waitpid: + status = translate_wait_enter(tracee); + break; + + case PR_brk: + status = translate_brk_enter(tracee); + break; + + case PR_getcwd: + set_sysnum(tracee, PR_void); + status = 0; + break; + + case PR_fchdir: + case PR_chdir: { + struct stat statl; + char *tmp; + + /* The ending "." ensures an error will be reported if + * path does not exist or if it is not a directory. */ + if (syscall_number == PR_chdir) { + status = get_sysarg_path(tracee, path, SYSARG_1); + if (status < 0) + break; + + status = join_paths(2, oldpath, path, "."); + if (status < 0) + break; + + dirfd = AT_FDCWD; + } + else { + strcpy(oldpath, "."); + dirfd = peek_reg(tracee, CURRENT, SYSARG_1); + } + + status = translate_path(tracee, path, dirfd, oldpath, true); + if (status < 0) + break; + + status = lstat(path, &statl); + if (status < 0) + break; + + /* Check this directory is accessible. */ + if ((statl.st_mode & S_IXUSR) == 0) + return -EACCES; + + /* 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 = 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"); + + set_sysnum(tracee, PR_void); + 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; + } + +#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) + +#define PEEK_WORD(addr, forced_errno) \ + peek_word(tracee, addr); \ + if (errno != 0) { \ + status = forced_errno ?: -errno; \ + break; \ + } + +#define POKE_WORD(addr, value) \ + poke_word(tracee, addr, value); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + + case PR_accept: + case PR_accept4: + /* Nothing special to do if no sockaddr was specified. */ + if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) { + status = 0; + break; + } + special = true; + /* Fall through. */ + case PR_getsockname: + case PR_getpeername:{ + int size; + + /* Remember: PEEK_WORD puts -errno in status and breaks if an + * error occured. */ + size = (int) PEEK_WORD(peek_reg(tracee, ORIGINAL, SYSARG_3), special ? -EINVAL : 0); + + /* 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; + + 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: + /* Nothing special to do if no sockaddr was specified. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2), 0); + if (sock_addr == 0) { + status = 0; + break; + } + special = true; + /* Fall through. */ + case SYS_GETSOCKNAME: + case SYS_GETPEERNAME: + /* Remember: PEEK_WORD puts -errno in status and breaks + * if an error occured. */ + size_addr = PEEK_WORD(SYSARG_ADDR(3), 0); + size = (int) PEEK_WORD(size_addr, special ? -EINVAL : 0); + + /* 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_WORD puts -errno in status and breaks if an + * error occured. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2), 0); + size = PEEK_WORD(SYSARG_ADDR(3), 0); + + 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_WORD puts -errno in status and breaks if an + * error occured. */ + POKE_WORD(SYSARG_ADDR(2), sock_addr); + POKE_WORD(SYSARG_ADDR(3), sizeof(struct sockaddr_un)); + + status = 0; + break; + } + +#undef SYSARG_ADDR +#undef PEEK_WORD +#undef POKE_WORD + + 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_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_fchmodat: + case PR_faccessat: + 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: + 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: + 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: + case PR_renameat2: + 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; + } + +end: + status2 = notify_extensions(tracee, SYSCALL_ENTER_END, status, 0); + if (status2 < 0) + status = status2; + + return status; +} +
diff --git a/5.1.0/src/syscall/exit.c b/5.1.0/src/syscall/exit.c new file mode 100644 index 0000000..81937bd --- /dev/null +++ b/5.1.0/src/syscall/exit.c
@@ -0,0 +1,455 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* errno(3), E* */ +#include <sys/utsname.h> /* struct utsname, */ +#include <linux/net.h> /* SYS_*, */ +#include <string.h> /* strlen(3), */ + +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "syscall/socket.h" +#include "syscall/chain.h" +#include "syscall/heap.h" +#include "execve/execve.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "path/path.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "extension/extension.h" +#include "arch.h" + +/** + * 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. + */ +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: + * - break: update the syscall result register with "status" + * - goto end: nothing else to do. + */ + syscall_number = get_sysnum(tracee, ORIGINAL); + syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + switch (syscall_number) { + case PR_brk: + translate_brk_exit(tracee); + goto end; + + case PR_getcwd: { + char path[PATH_MAX]; + size_t new_size; + size_t size; + word_t output; + + size = (size_t) peek_reg(tracee, ORIGINAL, SYSARG_2); + if (size == 0) { + status = -EINVAL; + break; + } + + /* Ensure cwd still exists. */ + status = translate_path(tracee, path, AT_FDCWD, ".", false); + if (status < 0) + break; + + new_size = strlen(tracee->fs->cwd) + 1; + if (size < new_size) { + status = -ERANGE; + break; + } + + /* Overwrite the path. */ + output = peek_reg(tracee, ORIGINAL, SYSARG_1); + 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: + /* Nothing special to do if no sockaddr was specified. */ + if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) + goto end; + /* Fall through. */ + 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; + } + +#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) + +#define POKE_WORD(addr, value) \ + poke_word(tracee, addr, value); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + +#define PEEK_WORD(addr) \ + peek_word(tracee, addr); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + + 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: + /* Nothing special to do if no sockaddr was specified. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2)); + if (sock_addr == 0) + goto end; + /* Fall through. */ + 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_WORD + * puts -errno in status and breaks if an error + * occured. */ + POKE_WORD(SYSARG_ADDR(2), peek_reg(tracee, MODIFIED, SYSARG_5)); + POKE_WORD(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_WORD puts -errno in status and breaks if an + * error occured. */ + sock_addr = PEEK_WORD(SYSARG_ADDR(2)); + size_addr = PEEK_WORD(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; + } + +#undef SYSARG_ADDR +#undef PEEK_WORD +#undef POKE_WORD + + case PR_fchdir: + case PR_chdir: + /* These syscalls are fully emulated, see enter.c for details + * (like errors). */ + status = 0; + break; + + case PR_rename: + case PR_renameat: + case PR_renameat2: { + char old_path[PATH_MAX]; + char new_path[PATH_MAX]; + ssize_t old_length; + ssize_t new_length; + Comparison comparison; + Reg old_reg; + Reg new_reg; + char *tmp; + + /* Error reported by the kernel. */ + if ((int) syscall_result < 0) + goto end; + + if (syscall_number == PR_rename) { + old_reg = SYSARG_1; + new_reg = SYSARG_2; + } + else { + old_reg = SYSARG_2; + new_reg = SYSARG_4; + } + + /* Get the old path, then convert it to the same + * "point-of-view" as tracee->fs->cwd (guest). */ + status = read_path(tracee, old_path, peek_reg(tracee, MODIFIED, old_reg)); + if (status < 0) + break; + + status = detranslate_path(tracee, old_path, NULL); + if (status < 0) + break; + old_length = (status > 0 ? status - 1 : (ssize_t) strlen(old_path)); + + /* Nothing special to do if the moved path is not the + * current working directory. */ + comparison = compare_paths(old_path, tracee->fs->cwd); + if (comparison != PATH1_IS_PREFIX && comparison != PATHS_ARE_EQUAL) { + status = 0; + break; + } + + /* Get the new path, then convert it to the same + * "point-of-view" as tracee->fs->cwd (guest). */ + status = read_path(tracee, new_path, peek_reg(tracee, MODIFIED, new_reg)); + if (status < 0) + break; + + status = detranslate_path(tracee, new_path, NULL); + if (status < 0) + break; + new_length = (status > 0 ? status - 1 : (ssize_t) strlen(new_path)); + + /* Sanity check. */ + if (strlen(tracee->fs->cwd) >= PATH_MAX) { + status = 0; + break; + } + strcpy(old_path, tracee->fs->cwd); + + /* Update the virtual current working directory. */ + substitute_path_prefix(old_path, old_length, new_path, new_length); + + tmp = talloc_strdup(tracee->fs, old_path); + if (tmp == NULL) { + status = -ENOMEM; + break; + } + + TALLOC_FREE(tracee->fs->cwd); + tracee->fs->cwd = tmp; + + 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_path(tracee, referer, input); + 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: + translate_execve_exit(tracee); + goto end; + + case PR_ptrace: + status = translate_ptrace_exit(tracee); + break; + + case PR_wait4: + case PR_waitpid: + if (tracee->as_ptracer.waits_in != WAITS_IN_PROOT) + goto end; + + status = translate_wait_exit(tracee); + break; + + default: + goto end; + } + + 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); +}
diff --git a/5.1.0/src/syscall/heap.c b/5.1.0/src/syscall/heap.c new file mode 100644 index 0000000..d96196b --- /dev/null +++ b/5.1.0/src/syscall/heap.c
@@ -0,0 +1,226 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/mman.h> /* PROT_*, MAP_*, */ +#include <assert.h> /* assert(3), */ +#include <string.h> /* strerror(3), */ +#include <unistd.h> /* sysconf(3), */ +#include <sys/param.h> /* MIN(), MAX(), */ + +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "syscall/sysnum.h" +#include "cli/note.h" + +#include "compat.h" + +#define DEBUG_BRK(...) /* fprintf(stderr, __VA_ARGS__) */ + +/* The size of the heap can be zero, unlike the size of a memory + * mapping. As a consequence, the first page of the "heap" memory + * mapping is discarded in order to emulate an empty heap. */ +static word_t heap_offset = 0; + +#define PREALLOCATED_HEAP_SIZE (16 * 1024 * 1024) + +/** + * Emulate the brk(2) syscall with mmap(2) and/or mremap(2); this is + * required to ensure a minimal heap size [1]. This function always + * returns 0. + * + * [1] With some versions of the Linux kernel and some versions of the + * GNU ELF interpreter (ld.so), the heap can't grow at all because + * the kernel has put a memory mapping right after the heap. This + * issue can be reproduced when using a Ubuntu 12.04 x86_64 rootfs + * on RHEL 5 x86_64. + */ +word_t translate_brk_enter(Tracee *tracee) +{ + word_t new_brk_address; + size_t old_heap_size; + size_t new_heap_size; + + if (tracee->heap->disabled) + return 0; + + if (heap_offset == 0) { + heap_offset = sysconf(_SC_PAGE_SIZE); + if ((int) heap_offset <= 0) + heap_offset = 0x1000; + } + + /* Non-fixed mmap pages might be placed right after the + * emulated heap on some architectures. A solution is to + * preallocate some space to ensure a minimal heap size. */ + if (tracee->heap->prealloc_size == 0) + tracee->heap->prealloc_size = MAX(PREALLOCATED_HEAP_SIZE, heap_offset); + + new_brk_address = peek_reg(tracee, CURRENT, SYSARG_1); + DEBUG_BRK("brk(0x%lx)\n", new_brk_address); + + /* Allocate a new mapping for the emulated heap. */ + if (tracee->heap->base == 0) { + Sysnum sysnum; + + /* From PRoot's point-of-view this is the first time this + * tracee calls brk(2), although an address was specified. + * This is not supposed to happen the first time. It is + * likely because this tracee is the very first child of PRoot + * but the first execve(2) didn't happen yet (so this is not + * its first call to brk(2)). For instance, the installation + * of seccomp filters is made after this very first process is + * traced, and might call malloc(3) before the first + * execve(2). */ + if (new_brk_address != 0) { + if (tracee->verbose > 0) + note(tracee, WARNING, INTERNAL, + "process %d is doing suspicious brk()", tracee->pid); + return 0; + } + + /* I don't understand yet why mmap(2) fails (EFAULT) + * on architectures that also have mmap2(2). Maybe + * this former implies MAP_FIXED in such cases. */ + sysnum = detranslate_sysnum(get_abi(tracee), PR_mmap2) != SYSCALL_AVOIDER + ? PR_mmap2 + : PR_mmap; + + set_sysnum(tracee, sysnum); + poke_reg(tracee, SYSARG_1 /* address */, 0); + poke_reg(tracee, SYSARG_2 /* length */, heap_offset + tracee->heap->prealloc_size); + poke_reg(tracee, SYSARG_3 /* prot */, PROT_READ | PROT_WRITE); + poke_reg(tracee, SYSARG_4 /* flags */, MAP_PRIVATE | MAP_ANONYMOUS); + poke_reg(tracee, SYSARG_5 /* fd */, -1); + poke_reg(tracee, SYSARG_6 /* offset */, 0); + + return 0; + } + + /* The size of the heap can't be negative. */ + if (new_brk_address < tracee->heap->base) { + set_sysnum(tracee, PR_void); + return 0; + } + + new_heap_size = new_brk_address - tracee->heap->base; + old_heap_size = tracee->heap->size; + + /* Clear the released memory in preallocated space, so it will be + * in the expected state next time it will be reallocated. */ + if (new_heap_size < old_heap_size && new_heap_size < tracee->heap->prealloc_size) { + (void) clear_mem(tracee, + tracee->heap->base + new_heap_size, + MIN(old_heap_size, tracee->heap->prealloc_size) - new_heap_size); + } + + /* No need to use mremap when both old size and new size are + * in the preallocated space. */ + if ( new_heap_size <= tracee->heap->prealloc_size + && old_heap_size <= tracee->heap->prealloc_size) { + tracee->heap->size = new_heap_size; + set_sysnum(tracee, PR_void); + return 0; + } + + /* Ensure the preallocated space will never be released. */ + new_heap_size = MAX(new_heap_size, tracee->heap->prealloc_size); + old_heap_size = MAX(old_heap_size, tracee->heap->prealloc_size); + + /* Actually resizing. */ + set_sysnum(tracee, PR_mremap); + poke_reg(tracee, SYSARG_1 /* old_address */, tracee->heap->base - heap_offset); + poke_reg(tracee, SYSARG_2 /* old_size */, old_heap_size + heap_offset); + poke_reg(tracee, SYSARG_3 /* new_size */, new_heap_size + heap_offset); + poke_reg(tracee, SYSARG_4 /* flags */, 0); + poke_reg(tracee, SYSARG_5 /* new_address */, 0); + + return 0; +} + +/** + * c.f. function above. + */ +void translate_brk_exit(Tracee *tracee) +{ + word_t result; + word_t sysnum; + int tracee_errno; + + if (tracee->heap->disabled) + return; + + assert(heap_offset > 0); + + sysnum = get_sysnum(tracee, MODIFIED); + result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + tracee_errno = (int) result; + + switch (sysnum) { + case PR_void: + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + + case PR_mmap: + case PR_mmap2: + /* On error, mmap(2) returns -errno (the last 4k is + * reserved for this), whereas brk(2) returns the + * previous value. */ + if (tracee_errno < 0 && tracee_errno > -4096) { + poke_reg(tracee, SYSARG_RESULT, 0); + break; + } + + tracee->heap->base = result + heap_offset; + tracee->heap->size = 0; + + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + + case PR_mremap: + /* On error, mremap(2) returns -errno (the last 4k is + * reserved this), whereas brk(2) returns the previous + * value. */ + if ( (tracee_errno < 0 && tracee_errno > -4096) + || (tracee->heap->base != result + heap_offset)) { + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + } + + tracee->heap->size = peek_reg(tracee, MODIFIED, SYSARG_3) - heap_offset; + + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + + case PR_brk: + /* Is it confirmed that this suspicious call to brk(2) + * is actually legit? */ + if (result == peek_reg(tracee, ORIGINAL, SYSARG_1)) + tracee->heap->disabled = true; + break; + + default: + assert(0); + } + + DEBUG_BRK("brk() = 0x%lx\n", peek_reg(tracee, CURRENT, SYSARG_RESULT)); +}
diff --git a/5.1.0/src/syscall/heap.h b/5.1.0/src/syscall/heap.h new file mode 100644 index 0000000..15230cd --- /dev/null +++ b/5.1.0/src/syscall/heap.h
@@ -0,0 +1,42 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 HEAP_H +#define HEAP_H + +#include "tracee/tracee.h" + +extern int translate_brk_enter(Tracee *tracee); +extern void translate_brk_exit(Tracee *tracee); + +/** + * Check if the @address is in the @tracee's preallocated heap space. + * This space is not supposed to be accessible. + */ +static inline bool belongs_to_heap_prealloc(const Tracee *tracee, word_t address) +{ + return (tracee->heap != NULL && !tracee->heap->disabled + && address >= tracee->heap->base + tracee->heap->size + && address < tracee->heap->base + tracee->heap->prealloc_size); +} + +#endif /* HEAP_H */
diff --git a/5.1.0/src/syscall/seccomp.c b/5.1.0/src/syscall/seccomp.c new file mode 100644 index 0000000..b548b69 --- /dev/null +++ b/5.1.0/src/syscall/seccomp.c
@@ -0,0 +1,512 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/prctl.h> /* prctl(2), PR_* */ +#include <linux/filter.h> /* struct sock_*, */ +#include <linux/seccomp.h> /* SECCOMP_MODE_FILTER, */ +#include <linux/filter.h> /* struct sock_*, */ +#include <linux/audit.h> /* AUDIT_, */ +#include <sys/queue.h> /* LIST_FOREACH, */ +#include <sys/types.h> /* size_t, */ +#include <talloc.h> /* talloc_*, */ +#include <errno.h> /* E*, */ +#include <string.h> /* memcpy(3), */ +#include <stddef.h> /* offsetof(3), */ +#include <stdint.h> /* uint*_t, UINT*_MAX, */ +#include <assert.h> /* assert(3), */ + +#include "syscall/seccomp.h" +#include "tracee/tracee.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "extension/extension.h" +#include "cli/note.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 arch, 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, arch, 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", + arch, 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) +{ + TALLOC_FREE(program->filter); + program->len = 0; +} + +/** + * Convert the given @sysnums into BPF 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 filtered syscall + * trace + * allow + * kill + * + * This function returns -errno if an error occurred, otherwise 0. + */ +static int set_seccomp_filters(const FilteredSysnum *sysnums) +{ + SeccompArch seccomp_archs[] = SECCOMP_ARCHS; + size_t nb_archs = sizeof(seccomp_archs) / sizeof(SeccompArch); + + struct sock_fprog program = { .len = 0, .filter = NULL }; + size_t nb_traced_syscalls; + size_t i, j, k; + int status; + + status = new_program_filter(&program); + if (status < 0) + goto end; + + /* For each handled architectures */ + for (i = 0; i < nb_archs; i++) { + word_t syscall; + + nb_traced_syscalls = 0; + + /* Pre-compute the number of traced syscalls for this architecture. */ + for (j = 0; j < seccomp_archs[i].nb_abis; j++) { + for (k = 0; sysnums[k].value != PR_void; k++) { + syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value); + if (syscall != SYSCALL_AVOIDER) + nb_traced_syscalls++; + } + } + + /* Filter: if handled architecture */ + status = start_arch_section(&program, seccomp_archs[i].value, nb_traced_syscalls); + if (status < 0) + goto end; + + for (j = 0; j < seccomp_archs[i].nb_abis; j++) { + for (k = 0; sysnums[k].value != PR_void; k++) { + /* Get the architecture specific syscall number. */ + syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value); + if (syscall == SYSCALL_AVOIDER) + continue; + + /* Filter: trace if handled syscall */ + status = add_trace_syscall(&program, syscall, sysnums[k].flags); + 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) + 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) + goto end; + + status = 0; +end: + free_program_filter(&program); + return status; +} + +/* List of sysnums handled by PRoot. */ +static FilteredSysnum proot_sysnums[] = { + { PR_accept, FILTER_SYSEXIT }, + { PR_accept4, FILTER_SYSEXIT }, + { PR_access, 0 }, + { PR_acct, 0 }, + { PR_bind, 0 }, + { PR_brk, FILTER_SYSEXIT }, + { PR_chdir, FILTER_SYSEXIT }, + { PR_chmod, 0 }, + { PR_chown, 0 }, + { PR_chown32, 0 }, + { PR_chroot, 0 }, + { PR_connect, 0 }, + { PR_creat, 0 }, + { PR_execve, FILTER_SYSEXIT }, + { PR_faccessat, 0 }, + { PR_fchdir, FILTER_SYSEXIT }, + { PR_fchmodat, 0 }, + { PR_fchownat, 0 }, + { PR_fstatat64, 0 }, + { PR_futimesat, 0 }, + { PR_getcwd, FILTER_SYSEXIT }, + { PR_getpeername, FILTER_SYSEXIT }, + { PR_getsockname, FILTER_SYSEXIT }, + { 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_ptrace, FILTER_SYSEXIT }, + { PR_readlink, FILTER_SYSEXIT }, + { PR_readlinkat, FILTER_SYSEXIT }, + { PR_removexattr, 0 }, + { PR_rename, FILTER_SYSEXIT }, + { PR_renameat, FILTER_SYSEXIT }, + { PR_renameat2, FILTER_SYSEXIT }, + { PR_rmdir, 0 }, + { PR_setxattr, 0 }, + { PR_socketcall, FILTER_SYSEXIT }, + { 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, FILTER_SYSEXIT }, + { PR_unlink, 0 }, + { PR_unlinkat, 0 }, + { PR_uselib, 0 }, + { PR_utime, 0 }, + { PR_utimensat, 0 }, + { PR_utimes, 0 }, + { PR_wait4, FILTER_SYSEXIT }, + { PR_waitpid, FILTER_SYSEXIT }, + FILTERED_SYSNUM_END, +}; + +/** + * Add the @new_sysnums to the list of filtered @sysnums, using the + * given Talloc @context. This function returns -errno if an error + * occurred, otherwise 0. + */ +static int merge_filtered_sysnums(TALLOC_CTX *context, FilteredSysnum **sysnums, + const FilteredSysnum *new_sysnums) +{ + size_t i, j; + + assert(sysnums != NULL); + + if (*sysnums == NULL) { + /* Start with no sysnums but the terminator. */ + *sysnums = talloc_array(context, FilteredSysnum, 1); + if (*sysnums == NULL) + return -ENOMEM; + + (*sysnums)[0].value = PR_void; + } + + for (i = 0; new_sysnums[i].value != PR_void; i++) { + /* Search for the given sysnum. */ + for (j = 0; (*sysnums)[j].value != PR_void + && (*sysnums)[j].value != new_sysnums[i].value; j++) + ; + + if ((*sysnums)[j].value == PR_void) { + /* No such sysnum, allocate a new entry. */ + (*sysnums) = talloc_realloc(context, (*sysnums), FilteredSysnum, j + 2); + if ((*sysnums) == NULL) + return -ENOMEM; + + (*sysnums)[j] = new_sysnums[i]; + + /* The last item is the terminator. */ + (*sysnums)[j + 1].value = PR_void; + } + else + /* The sysnum is already filtered, merge the + * flags. */ + (*sysnums)[j].flags |= new_sysnums[i].flags; + } + + return 0; +} + +/** + * 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) +{ + FilteredSysnum *filtered_sysnums = NULL; + Extension *extension; + int status; + + assert(tracee != NULL && tracee->ctx != NULL); + + /* Add the sysnums required by PRoot to the list of filtered + * sysnums. TODO: only if path translation is required. */ + status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, proot_sysnums); + if (status < 0) + return status; + + /* Merge the sysnums required by the extensions to the list + * of filtered sysnums. */ + if (tracee->extensions != NULL) { + LIST_FOREACH(extension, tracee->extensions, link) { + if (extension->filtered_sysnums == NULL) + continue; + + status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, + extension->filtered_sysnums); + if (status < 0) + return status; + } + } + + status = set_seccomp_filters(filtered_sysnums); + if (status < 0) + return status; + + return 0; +} + +#else + +#include "tracee/tracee.h" +#include "attribute.h" + +int enable_syscall_filtering(const Tracee *tracee UNUSED) +{ + return 0; +} + +#endif /* defined(HAVE_SECCOMP_FILTER) */
diff --git a/5.1.0/src/syscall/seccomp.h b/5.1.0/src/syscall/seccomp.h new file mode 100644 index 0000000..1e8a1e7 --- /dev/null +++ b/5.1.0/src/syscall/seccomp.h
@@ -0,0 +1,48 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "attribute.h" +#include "arch.h" + +typedef struct { + Sysnum value; + word_t flags; +} FilteredSysnum; + +typedef struct { + unsigned int value; + size_t nb_abis; + Abi abis[NB_MAX_ABIS]; +} SeccompArch; + +#define FILTERED_SYSNUM_END { PR_void, 0 } + +#define FILTER_SYSEXIT 0x1 + +extern int enable_syscall_filtering(const Tracee *tracee); + +#endif /* SECCOMP_H */
diff --git a/5.1.0/src/syscall/socket.c b/5.1.0/src/syscall/socket.c new file mode 100644 index 0000000..381ae64 --- /dev/null +++ b/5.1.0/src/syscall/socket.c
@@ -0,0 +1,216 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stddef.h> /* offsetof(3), */ +#include <strings.h> /* bzero(3), */ +#include <string.h> /* strncpy(3), strlen(3), */ +#include <assert.h> /* assert(3), */ +#include <errno.h> /* E*, */ +#include <sys/socket.h> /* struct sockaddr_un, AF_UNIX, */ +#include <sys/un.h> /* struct sockaddr_un, */ +#include <sys/param.h> /* MIN(), MAX(), */ + +#include "syscall/socket.h" +#include "tracee/tracee.h" +#include "tracee/mem.h" +#include "path/binding.h" +#include "path/temp.h" +#include "path/path.h" +#include "arch.h" + +#include "compat.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); +extern 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 user_path[PATH_MAX]; + char host_path[PATH_MAX]; + int status; + + if (*address == 0) + return 0; + + status = read_sockaddr_un(tracee, &sockaddr, sizeof(sockaddr), user_path, *address, size); + if (status <= 0) + return status; + + status = translate_path(tracee, host_path, AT_FDCWD, user_path, true); + if (status < 0) + return status; + + /* Be careful: sun_path doesn't have to be null-terminated. */ + if (strlen(host_path) > sizeof_path) { + char *shorter_host_path; + Binding *binding; + + /* The translated path is too long to fit the sun_path + * array, so let's bind it to a shorter path. */ + shorter_host_path = create_temp_name(tracee->ctx, "proot"); + if (shorter_host_path == NULL || strlen(shorter_host_path) > sizeof_path) + return -EINVAL; + + (void) mktemp(shorter_host_path); + + if (strlen(shorter_host_path) > sizeof_path) + return -EINVAL; + + /* Ensure the guest path of this new binding is + * canonicalized, as it is always assumed. */ + strcpy(user_path, host_path); + status = detranslate_path(tracee, user_path, NULL); + if (status < 0) + return -EINVAL; + + /* Bing the guest path to a shorter host path. */ + binding = insort_binding3(tracee, tracee->ctx, shorter_host_path, user_path); + if (binding == NULL) + return -EINVAL; + + /* This temporary file (shorter_host_path) will be removed once the + * binding is destroyed. */ + talloc_reparent(tracee->ctx, binding, shorter_host_path); + + /* Let's use this shorter path now. */ + strcpy(host_path, shorter_host_path); + } + strncpy(sockaddr.sun_path, host_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; +} + +/** + * 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 = peek_int32(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; + + poke_int32(tracee, size_addr, size); + if (errno != 0) + return -errno; + + return 0; +}
diff --git a/5.1.0/src/syscall/socket.h b/5.1.0/src/syscall/socket.h new file mode 100644 index 0000000..0a41932 --- /dev/null +++ b/5.1.0/src/syscall/socket.h
@@ -0,0 +1,32 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 */
diff --git a/5.1.0/src/syscall/syscall.c b/5.1.0/src/syscall/syscall.c new file mode 100644 index 0000000..c5ca734 --- /dev/null +++ b/5.1.0/src/syscall/syscall.c
@@ -0,0 +1,181 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> /* assert(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <string.h> /* strlen(3), */ +#include <errno.h> /* errno(3), E* */ + +#include "syscall/syscall.h" +#include "syscall/chain.h" +#include "extension/extension.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.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_path(tracee, path, src); + if (size < 0) + return size; + + 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. + */ +static int set_sysarg_data(Tracee *tracee, const 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, const char path[PATH_MAX], Reg reg) +{ + return set_sysarg_data(tracee, path, strlen(path) + 1, reg); +} + +void translate_syscall(Tracee *tracee) +{ + const bool is_enter_stage = IS_IN_SYSENTER(tracee); + 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"); + + /* Translate the syscall only if it was actually + * requested by the tracee, it is not a syscall + * chained by PRoot. */ + if (tracee->chain.syscalls == NULL) { + save_current_regs(tracee, ORIGINAL); + status = translate_syscall_enter(tracee); + save_current_regs(tracee, MODIFIED); + } + else { + status = notify_extensions(tracee, SYSCALL_CHAINED_ENTER, 0, 0); + tracee->restart_how = PTRACE_SYSCALL; + } + + /* Remember the tracee status for the "exit" stage and + * avoid the actual syscall if an error was reported + * by the translation/extension. */ + if (status < 0) { + set_sysnum(tracee, PR_void); + poke_reg(tracee, SYSARG_RESULT, (word_t) status); + tracee->status = status; + } + else + tracee->status = 1; + + /* 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 the syscall only if it was actually + * requested by the tracee, it is not a syscall + * chained by PRoot. */ + if (tracee->chain.syscalls == NULL) + translate_syscall_exit(tracee); + else + (void) notify_extensions(tracee, SYSCALL_CHAINED_EXIT, 0, 0); + + /* Reset the tracee's status. */ + tracee->status = 0; + + /* Insert the next chained syscall, if any. */ + if (tracee->chain.syscalls != NULL) + chain_next_syscall(tracee); + } + + (void) push_regs(tracee); + + if (is_enter_stage) + print_current_regs(tracee, 5, "sysenter end" ); + else + print_current_regs(tracee, 4, "sysexit end"); +}
diff --git a/5.1.0/src/syscall/syscall.h b/5.1.0/src/syscall/syscall.h new file mode 100644 index 0000000..98f59b2 --- /dev/null +++ b/5.1.0/src/syscall/syscall.h
@@ -0,0 +1,38 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <limits.h> /* PATH_MAX, */ + +#include "tracee/tracee.h" +#include "tracee/reg.h" + +extern int get_sysarg_path(const Tracee *tracee, char path[PATH_MAX], Reg reg); +extern int set_sysarg_path(Tracee *tracee, const char path[PATH_MAX], Reg reg); + +extern void translate_syscall(Tracee *tracee); +extern int translate_syscall_enter(Tracee *tracee); +extern void translate_syscall_exit(Tracee *tracee); + +#endif /* SYSCALL_H */
diff --git a/5.1.0/src/syscall/sysnum.c b/5.1.0/src/syscall/sysnum.c new file mode 100644 index 0000000..5802a34 --- /dev/null +++ b/5.1.0/src/syscall/sysnum.c
@@ -0,0 +1,161 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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.h> + +#include "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "tracee/abi.h" +#include "tracee/reg.h" +#include "arch.h" +#include "cli/note.h" + +#include SYSNUMS_HEADER1 + +#ifdef SYSNUMS_HEADER2 +#include SYSNUMS_HEADER2 +#endif + +#ifdef SYSNUMS_HEADER3 +#include SYSNUMS_HEADER3 +#endif + +typedef struct { + const Sysnum *table; + word_t offset; + word_t length; +} Sysnums; + +/** + * Update @sysnums' fields with the sysnum table for the given @abi. + */ +static void get_sysnums(Abi abi, Sysnums *sysnums) +{ + switch (abi) { + case ABI_DEFAULT: + sysnums->table = SYSNUMS_ABI1; + sysnums->length = sizeof(SYSNUMS_ABI1) / sizeof(Sysnum); + sysnums->offset = 0; + return; +#ifdef SYSNUMS_ABI2 + case ABI_2: + sysnums->table = SYSNUMS_ABI2; + sysnums->length = sizeof(SYSNUMS_ABI2) / sizeof(Sysnum); + sysnums->offset = 0; + return; +#endif +#ifdef SYSNUMS_ABI3 + case ABI_3: + sysnums->table = SYSNUMS_ABI3; + sysnums->length = sizeof(SYSNUMS_ABI3) / sizeof(Sysnum); + sysnums->offset = 0x40000000; /* x32 */ + return; +#endif + default: + assert(0); + } +} + +/** + * Return the neutral value of @sysnum from the given @abi. + */ +static Sysnum translate_sysnum(Abi abi, word_t sysnum) +{ + Sysnums sysnums; + word_t index; + + get_sysnums(abi, &sysnums); + + /* Sanity checks. */ + if (sysnum < sysnums.offset) + return PR_void; + + index = sysnum - sysnums.offset; + + /* Sanity checks. */ + if (index > sysnums.length) + return PR_void; + + return sysnums.table[index]; +} + +/** + * Return the architecture value of @sysnum for the given @abi. + */ +word_t detranslate_sysnum(Abi abi, Sysnum sysnum) +{ + Sysnums sysnums; + size_t i; + + /* Very special case. */ + if (sysnum == PR_void) + return SYSCALL_AVOIDER; + + get_sysnums(abi, &sysnums); + + for (i = 0; i < sysnums.length; i++) { + if (sysnums.table[i] != sysnum) + continue; + + return i + sysnums.offset; + } + + return SYSCALL_AVOIDER; +} + +/** + * Return the neutral value of the @tracee's current syscall number. + */ +Sysnum get_sysnum(const Tracee *tracee, RegVersion version) +{ + return translate_sysnum(get_abi(tracee), peek_reg(tracee, version, SYSARG_NUM)); +} + +/** + * Overwrite the @tracee's current syscall number with @sysnum. Note: + * this neutral value is automatically converted into the architecture + * value. + */ +void set_sysnum(Tracee *tracee, Sysnum sysnum) +{ + poke_reg(tracee, SYSARG_NUM, detranslate_sysnum(get_abi(tracee), sysnum)); +} + +/** + * Return the human readable name of @sysnum. + */ +const char *stringify_sysnum(Sysnum sysnum) +{ + #define SYSNUM(item) [ PR_ ## item ] = #item, + static const char *names[] = { + #include "syscall/sysnums.list" + }; + #undef SYSNUM + + if (sysnum == 0) + return "void"; + + if (sysnum >= PR_NB_SYSNUM) + return ""; + + return names[sysnum]; +}
diff --git a/5.1.0/src/syscall/sysnum.h b/5.1.0/src/syscall/sysnum.h new file mode 100644 index 0000000..2deb69a --- /dev/null +++ b/5.1.0/src/syscall/sysnum.h
@@ -0,0 +1,45 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 SYSNUM_H +#define SYSNUM_H + +#include <stdbool.h> + +#include "tracee/tracee.h" +#include "tracee/abi.h" +#include "tracee/reg.h" + +#define SYSNUM(item) PR_ ## item, +typedef enum { + PR_void = 0, + #include "syscall/sysnums.list" + PR_NB_SYSNUM +} Sysnum; +#undef SYSNUM + +extern Sysnum get_sysnum(const Tracee *tracee, RegVersion version); +extern void set_sysnum(Tracee *tracee, Sysnum sysnum); +extern word_t detranslate_sysnum(Abi abi, Sysnum sysnum); +extern const char *stringify_sysnum(Sysnum sysnum); + +#endif /* SYSNUM_H */
diff --git a/5.1.0/src/syscall/sysnums-arm.h b/5.1.0/src/syscall/sysnums-arm.h new file mode 100644 index 0000000..42d3540 --- /dev/null +++ b/5.1.0/src/syscall/sysnums-arm.h
@@ -0,0 +1,343 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_arm[] = { + [ 0 ] = PR_restart_syscall, + [ 1 ] = PR_exit, + [ 2 ] = PR_fork, + [ 3 ] = PR_read, + [ 4 ] = PR_write, + [ 5 ] = PR_open, + [ 6 ] = PR_close, + [ 8 ] = PR_creat, + [ 9 ] = PR_link, + [ 10 ] = PR_unlink, + [ 11 ] = PR_execve, + [ 12 ] = PR_chdir, + [ 14 ] = PR_mknod, + [ 15 ] = PR_chmod, + [ 16 ] = PR_lchown, + [ 19 ] = PR_lseek, + [ 20 ] = PR_getpid, + [ 21 ] = PR_mount, + [ 23 ] = PR_setuid, + [ 24 ] = PR_getuid, + [ 26 ] = PR_ptrace, + [ 29 ] = PR_pause, + [ 33 ] = PR_access, + [ 34 ] = PR_nice, + [ 36 ] = PR_sync, + [ 37 ] = PR_kill, + [ 38 ] = PR_rename, + [ 39 ] = PR_mkdir, + [ 40 ] = PR_rmdir, + [ 41 ] = PR_dup, + [ 42 ] = PR_pipe, + [ 43 ] = PR_times, + [ 45 ] = PR_brk, + [ 46 ] = PR_setgid, + [ 47 ] = PR_getgid, + [ 49 ] = PR_geteuid, + [ 50 ] = PR_getegid, + [ 51 ] = PR_acct, + [ 52 ] = PR_umount2, + [ 54 ] = PR_ioctl, + [ 55 ] = PR_fcntl, + [ 57 ] = PR_setpgid, + [ 60 ] = PR_umask, + [ 61 ] = PR_chroot, + [ 62 ] = PR_ustat, + [ 63 ] = PR_dup2, + [ 64 ] = PR_getppid, + [ 65 ] = PR_getpgrp, + [ 66 ] = PR_setsid, + [ 67 ] = PR_sigaction, + [ 70 ] = PR_setreuid, + [ 71 ] = PR_setregid, + [ 72 ] = PR_sigsuspend, + [ 73 ] = PR_sigpending, + [ 74 ] = PR_sethostname, + [ 75 ] = PR_setrlimit, + [ 77 ] = PR_getrusage, + [ 78 ] = PR_gettimeofday, + [ 79 ] = PR_settimeofday, + [ 80 ] = PR_getgroups, + [ 81 ] = PR_setgroups, + [ 83 ] = PR_symlink, + [ 85 ] = PR_readlink, + [ 86 ] = PR_uselib, + [ 87 ] = PR_swapon, + [ 88 ] = PR_reboot, + [ 91 ] = PR_munmap, + [ 92 ] = PR_truncate, + [ 93 ] = PR_ftruncate, + [ 94 ] = PR_fchmod, + [ 95 ] = PR_fchown, + [ 96 ] = PR_getpriority, + [ 97 ] = PR_setpriority, + [ 99 ] = PR_statfs, + [ 100 ] = PR_fstatfs, + [ 103 ] = PR_syslog, + [ 104 ] = PR_setitimer, + [ 105 ] = PR_getitimer, + [ 106 ] = PR_stat, + [ 107 ] = PR_lstat, + [ 108 ] = PR_fstat, + [ 111 ] = PR_vhangup, + [ 114 ] = PR_wait4, + [ 115 ] = PR_swapoff, + [ 116 ] = PR_sysinfo, + [ 118 ] = PR_fsync, + [ 119 ] = PR_sigreturn, + [ 120 ] = PR_clone, + [ 121 ] = PR_setdomainname, + [ 122 ] = PR_uname, + [ 124 ] = PR_adjtimex, + [ 125 ] = PR_mprotect, + [ 126 ] = PR_sigprocmask, + [ 128 ] = PR_init_module, + [ 129 ] = PR_delete_module, + [ 131 ] = PR_quotactl, + [ 132 ] = PR_getpgid, + [ 133 ] = PR_fchdir, + [ 134 ] = PR_bdflush, + [ 135 ] = PR_sysfs, + [ 136 ] = PR_personality, + [ 138 ] = PR_setfsuid, + [ 139 ] = PR_setfsgid, + [ 140 ] = PR__llseek, + [ 141 ] = PR_getdents, + [ 142 ] = PR__newselect, + [ 143 ] = PR_flock, + [ 144 ] = PR_msync, + [ 145 ] = PR_readv, + [ 146 ] = PR_writev, + [ 147 ] = PR_getsid, + [ 148 ] = PR_fdatasync, + [ 149 ] = PR__sysctl, + [ 150 ] = PR_mlock, + [ 151 ] = PR_munlock, + [ 152 ] = PR_mlockall, + [ 153 ] = PR_munlockall, + [ 154 ] = PR_sched_setparam, + [ 155 ] = PR_sched_getparam, + [ 156 ] = PR_sched_setscheduler, + [ 157 ] = PR_sched_getscheduler, + [ 158 ] = PR_sched_yield, + [ 159 ] = PR_sched_get_priority_max, + [ 160 ] = PR_sched_get_priority_min, + [ 161 ] = PR_sched_rr_get_interval, + [ 162 ] = PR_nanosleep, + [ 163 ] = PR_mremap, + [ 164 ] = PR_setresuid, + [ 165 ] = PR_getresuid, + [ 168 ] = PR_poll, + [ 169 ] = PR_nfsservctl, + [ 170 ] = PR_setresgid, + [ 171 ] = PR_getresgid, + [ 172 ] = PR_prctl, + [ 173 ] = PR_rt_sigreturn, + [ 174 ] = PR_rt_sigaction, + [ 175 ] = PR_rt_sigprocmask, + [ 176 ] = PR_rt_sigpending, + [ 177 ] = PR_rt_sigtimedwait, + [ 178 ] = PR_rt_sigqueueinfo, + [ 179 ] = PR_rt_sigsuspend, + [ 180 ] = PR_pread64, + [ 181 ] = PR_pwrite64, + [ 182 ] = PR_chown, + [ 183 ] = PR_getcwd, + [ 184 ] = PR_capget, + [ 185 ] = PR_capset, + [ 186 ] = PR_sigaltstack, + [ 187 ] = PR_sendfile, + [ 190 ] = PR_vfork, + [ 191 ] = PR_ugetrlimit, + [ 192 ] = PR_mmap2, + [ 193 ] = PR_truncate64, + [ 194 ] = PR_ftruncate64, + [ 195 ] = PR_stat64, + [ 196 ] = PR_lstat64, + [ 197 ] = PR_fstat64, + [ 198 ] = PR_lchown32, + [ 199 ] = PR_getuid32, + [ 200 ] = PR_getgid32, + [ 201 ] = PR_geteuid32, + [ 202 ] = PR_getegid32, + [ 203 ] = PR_setreuid32, + [ 204 ] = PR_setregid32, + [ 205 ] = PR_getgroups32, + [ 206 ] = PR_setgroups32, + [ 207 ] = PR_fchown32, + [ 208 ] = PR_setresuid32, + [ 209 ] = PR_getresuid32, + [ 210 ] = PR_setresgid32, + [ 211 ] = PR_getresgid32, + [ 212 ] = PR_chown32, + [ 213 ] = PR_setuid32, + [ 214 ] = PR_setgid32, + [ 215 ] = PR_setfsuid32, + [ 216 ] = PR_setfsgid32, + [ 217 ] = PR_getdents64, + [ 218 ] = PR_pivot_root, + [ 219 ] = PR_mincore, + [ 220 ] = PR_madvise, + [ 221 ] = PR_fcntl64, + [ 222 ] = PR_void, + [ 224 ] = PR_gettid, + [ 225 ] = PR_readahead, + [ 226 ] = PR_setxattr, + [ 227 ] = PR_lsetxattr, + [ 228 ] = PR_fsetxattr, + [ 229 ] = PR_getxattr, + [ 230 ] = PR_lgetxattr, + [ 231 ] = PR_fgetxattr, + [ 232 ] = PR_listxattr, + [ 233 ] = PR_llistxattr, + [ 234 ] = PR_flistxattr, + [ 235 ] = PR_removexattr, + [ 236 ] = PR_lremovexattr, + [ 237 ] = PR_fremovexattr, + [ 238 ] = PR_tkill, + [ 239 ] = PR_sendfile64, + [ 240 ] = PR_futex, + [ 241 ] = PR_sched_setaffinity, + [ 242 ] = PR_sched_getaffinity, + [ 243 ] = PR_io_setup, + [ 244 ] = PR_io_destroy, + [ 245 ] = PR_io_getevents, + [ 246 ] = PR_io_submit, + [ 247 ] = PR_io_cancel, + [ 248 ] = PR_exit_group, + [ 249 ] = PR_lookup_dcookie, + [ 250 ] = PR_epoll_create, + [ 251 ] = PR_epoll_ctl, + [ 252 ] = PR_epoll_wait, + [ 253 ] = PR_remap_file_pages, + [ 256 ] = PR_set_tid_address, + [ 257 ] = PR_timer_create, + [ 258 ] = PR_timer_settime, + [ 259 ] = PR_timer_gettime, + [ 260 ] = PR_timer_getoverrun, + [ 261 ] = PR_timer_delete, + [ 262 ] = PR_clock_settime, + [ 263 ] = PR_clock_gettime, + [ 264 ] = PR_clock_getres, + [ 265 ] = PR_clock_nanosleep, + [ 266 ] = PR_statfs64, + [ 267 ] = PR_fstatfs64, + [ 268 ] = PR_tgkill, + [ 269 ] = PR_utimes, + [ 270 ] = PR_arm_fadvise64_64, + [ 271 ] = PR_pciconfig_iobase, + [ 272 ] = PR_pciconfig_read, + [ 273 ] = PR_pciconfig_write, + [ 274 ] = PR_mq_open, + [ 275 ] = PR_mq_unlink, + [ 276 ] = PR_mq_timedsend, + [ 277 ] = PR_mq_timedreceive, + [ 278 ] = PR_mq_notify, + [ 279 ] = PR_mq_getsetattr, + [ 280 ] = PR_waitid, + [ 281 ] = PR_socket, + [ 282 ] = PR_bind, + [ 283 ] = PR_connect, + [ 284 ] = PR_listen, + [ 285 ] = PR_accept, + [ 286 ] = PR_getsockname, + [ 287 ] = PR_getpeername, + [ 288 ] = PR_socketpair, + [ 289 ] = PR_send, + [ 290 ] = PR_sendto, + [ 291 ] = PR_recv, + [ 292 ] = PR_recvfrom, + [ 293 ] = PR_shutdown, + [ 294 ] = PR_setsockopt, + [ 295 ] = PR_getsockopt, + [ 296 ] = PR_sendmsg, + [ 297 ] = PR_recvmsg, + [ 298 ] = PR_semop, + [ 299 ] = PR_semget, + [ 300 ] = PR_semctl, + [ 301 ] = PR_msgsnd, + [ 302 ] = PR_msgrcv, + [ 303 ] = PR_msgget, + [ 304 ] = PR_msgctl, + [ 305 ] = PR_shmat, + [ 306 ] = PR_shmdt, + [ 307 ] = PR_shmget, + [ 308 ] = PR_shmctl, + [ 309 ] = PR_add_key, + [ 310 ] = PR_request_key, + [ 311 ] = PR_keyctl, + [ 312 ] = PR_semtimedop, + [ 313 ] = PR_vserver, + [ 314 ] = PR_ioprio_set, + [ 315 ] = PR_ioprio_get, + [ 316 ] = PR_inotify_init, + [ 317 ] = PR_inotify_add_watch, + [ 318 ] = PR_inotify_rm_watch, + [ 319 ] = PR_mbind, + [ 320 ] = PR_get_mempolicy, + [ 321 ] = PR_set_mempolicy, + [ 322 ] = PR_openat, + [ 323 ] = PR_mkdirat, + [ 324 ] = PR_mknodat, + [ 325 ] = PR_fchownat, + [ 326 ] = PR_futimesat, + [ 327 ] = PR_fstatat64, + [ 328 ] = PR_unlinkat, + [ 329 ] = PR_renameat, + [ 330 ] = PR_linkat, + [ 331 ] = PR_symlinkat, + [ 332 ] = PR_readlinkat, + [ 333 ] = PR_fchmodat, + [ 334 ] = PR_faccessat, + [ 335 ] = PR_pselect6, + [ 336 ] = PR_ppoll, + [ 337 ] = PR_unshare, + [ 338 ] = PR_set_robust_list, + [ 339 ] = PR_get_robust_list, + [ 340 ] = PR_splice, + [ 341 ] = PR_arm_sync_file_range, + [ 342 ] = PR_tee, + [ 343 ] = PR_vmsplice, + [ 344 ] = PR_move_pages, + [ 345 ] = PR_getcpu, + [ 346 ] = PR_epoll_pwait, + [ 347 ] = PR_kexec_load, + [ 348 ] = PR_utimensat, + [ 349 ] = PR_signalfd, + [ 350 ] = PR_timerfd_create, + [ 351 ] = PR_eventfd, + [ 352 ] = PR_fallocate, + [ 353 ] = PR_timerfd_settime, + [ 354 ] = PR_timerfd_gettime, + [ 355 ] = PR_signalfd4, + [ 356 ] = PR_eventfd2, + [ 357 ] = PR_epoll_create1, + [ 358 ] = PR_dup3, + [ 359 ] = PR_pipe2, + [ 360 ] = PR_inotify_init1, + [ 361 ] = PR_preadv, + [ 362 ] = PR_pwritev, + [ 363 ] = PR_rt_tgsigqueueinfo, + [ 364 ] = PR_perf_event_open, + [ 365 ] = PR_recvmmsg, + [ 366 ] = PR_accept4, + [ 367 ] = PR_fanotify_init, + [ 368 ] = PR_fanotify_mark, + [ 369 ] = PR_prlimit64, + [ 370 ] = PR_name_to_handle_at, + [ 371 ] = PR_open_by_handle_at, + [ 372 ] = PR_clock_adjtime, + [ 373 ] = PR_syncfs, + [ 374 ] = PR_sendmmsg, + [ 375 ] = PR_setns, + [ 376 ] = PR_process_vm_readv, + [ 377 ] = PR_process_vm_writev, + [ 378 ] = PR_kcmp, + [ 379 ] = PR_finit_module, + [ 380 ] = PR_sched_setattr, + [ 381 ] = PR_sched_getattr, + [ 382 ] = PR_renameat2, +};
diff --git a/5.1.0/src/syscall/sysnums-arm64.h b/5.1.0/src/syscall/sysnums-arm64.h new file mode 100644 index 0000000..f7d7a5e --- /dev/null +++ b/5.1.0/src/syscall/sysnums-arm64.h
@@ -0,0 +1,266 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_arm64[] = { + [ 0 ] = PR_io_setup, + [ 1 ] = PR_io_destroy, + [ 2 ] = PR_io_submit, + [ 3 ] = PR_io_cancel, + [ 4 ] = PR_io_getevents, + [ 5 ] = PR_setxattr, + [ 6 ] = PR_lsetxattr, + [ 7 ] = PR_fsetxattr, + [ 8 ] = PR_getxattr, + [ 9 ] = PR_lgetxattr, + [ 10 ] = PR_fgetxattr, + [ 11 ] = PR_listxattr, + [ 12 ] = PR_llistxattr, + [ 13 ] = PR_flistxattr, + [ 14 ] = PR_removexattr, + [ 15 ] = PR_lremovexattr, + [ 16 ] = PR_fremovexattr, + [ 17 ] = PR_getcwd, + [ 18 ] = PR_lookup_dcookie, + [ 19 ] = PR_eventfd2, + [ 20 ] = PR_epoll_create1, + [ 21 ] = PR_epoll_ctl, + [ 22 ] = PR_epoll_pwait, + [ 23 ] = PR_dup, + [ 24 ] = PR_dup3, + [ 25 ] = PR_fcntl, + [ 26 ] = PR_inotify_init1, + [ 27 ] = PR_inotify_add_watch, + [ 28 ] = PR_inotify_rm_watch, + [ 29 ] = PR_ioctl, + [ 30 ] = PR_ioprio_set, + [ 31 ] = PR_ioprio_get, + [ 32 ] = PR_flock, + [ 33 ] = PR_mknodat, + [ 34 ] = PR_mkdirat, + [ 35 ] = PR_unlinkat, + [ 36 ] = PR_symlinkat, + [ 37 ] = PR_linkat, + [ 38 ] = PR_renameat, + [ 39 ] = PR_umount2, + [ 40 ] = PR_mount, + [ 41 ] = PR_pivot_root, + [ 42 ] = PR_nfsservctl, + [ 43 ] = PR_statfs, + [ 44 ] = PR_fstatfs, + [ 45 ] = PR_truncate, + [ 46 ] = PR_ftruncate, + [ 47 ] = PR_fallocate, + [ 48 ] = PR_faccessat, + [ 49 ] = PR_chdir, + [ 50 ] = PR_fchdir, + [ 51 ] = PR_chroot, + [ 52 ] = PR_fchmod, + [ 53 ] = PR_fchmodat, + [ 54 ] = PR_fchownat, + [ 55 ] = PR_fchown, + [ 56 ] = PR_openat, + [ 57 ] = PR_close, + [ 58 ] = PR_vhangup, + [ 59 ] = PR_pipe2, + [ 60 ] = PR_quotactl, + [ 61 ] = PR_getdents64, + [ 62 ] = PR_lseek, + [ 63 ] = PR_read, + [ 64 ] = PR_write, + [ 65 ] = PR_readv, + [ 66 ] = PR_writev, + [ 67 ] = PR_pread64, + [ 68 ] = PR_pwrite64, + [ 69 ] = PR_preadv, + [ 70 ] = PR_pwritev, + [ 71 ] = PR_sendfile, + [ 72 ] = PR_pselect6, + [ 73 ] = PR_ppoll, + [ 74 ] = PR_signalfd4, + [ 75 ] = PR_vmsplice, + [ 76 ] = PR_splice, + [ 77 ] = PR_tee, + [ 78 ] = PR_readlinkat, + [ 79 ] = PR_fstatat64, + [ 80 ] = PR_fstat, + [ 81 ] = PR_sync, + [ 82 ] = PR_fsync, + [ 83 ] = PR_fdatasync, + [ 84 ] = PR_sync_file_range, + [ 85 ] = PR_timerfd_create, + [ 86 ] = PR_timerfd_settime, + [ 87 ] = PR_timerfd_gettime, + [ 88 ] = PR_utimensat, + [ 89 ] = PR_acct, + [ 90 ] = PR_capget, + [ 91 ] = PR_capset, + [ 92 ] = PR_personality, + [ 93 ] = PR_exit, + [ 94 ] = PR_exit_group, + [ 95 ] = PR_waitid, + [ 96 ] = PR_set_tid_address, + [ 97 ] = PR_unshare, + [ 98 ] = PR_futex, + [ 99 ] = PR_set_robust_list, + [ 100 ] = PR_get_robust_list, + [ 101 ] = PR_nanosleep, + [ 102 ] = PR_getitimer, + [ 103 ] = PR_setitimer, + [ 104 ] = PR_kexec_load, + [ 105 ] = PR_init_module, + [ 106 ] = PR_delete_module, + [ 107 ] = PR_timer_create, + [ 108 ] = PR_timer_gettime, + [ 109 ] = PR_timer_getoverrun, + [ 110 ] = PR_timer_settime, + [ 111 ] = PR_timer_delete, + [ 112 ] = PR_clock_settime, + [ 113 ] = PR_clock_gettime, + [ 114 ] = PR_clock_getres, + [ 115 ] = PR_clock_nanosleep, + [ 116 ] = PR_syslog, + [ 117 ] = PR_ptrace, + [ 118 ] = PR_sched_setparam, + [ 119 ] = PR_sched_setscheduler, + [ 120 ] = PR_sched_getscheduler, + [ 121 ] = PR_sched_getparam, + [ 122 ] = PR_sched_setaffinity, + [ 123 ] = PR_sched_getaffinity, + [ 124 ] = PR_sched_yield, + [ 125 ] = PR_sched_get_priority_max, + [ 126 ] = PR_sched_get_priority_min, + [ 127 ] = PR_sched_rr_get_interval, + [ 128 ] = PR_restart_syscall, + [ 129 ] = PR_kill, + [ 130 ] = PR_tkill, + [ 131 ] = PR_tgkill, + [ 132 ] = PR_sigaltstack, + [ 133 ] = PR_rt_sigsuspend, + [ 134 ] = PR_rt_sigaction, + [ 135 ] = PR_rt_sigprocmask, + [ 136 ] = PR_rt_sigpending, + [ 137 ] = PR_rt_sigtimedwait, + [ 138 ] = PR_rt_sigqueueinfo, + [ 139 ] = PR_rt_sigreturn, + [ 140 ] = PR_setpriority, + [ 141 ] = PR_getpriority, + [ 142 ] = PR_reboot, + [ 143 ] = PR_setregid, + [ 144 ] = PR_setgid, + [ 145 ] = PR_setreuid, + [ 146 ] = PR_setuid, + [ 147 ] = PR_setresuid, + [ 148 ] = PR_getresuid, + [ 149 ] = PR_setresgid, + [ 150 ] = PR_getresgid, + [ 151 ] = PR_setfsuid, + [ 152 ] = PR_setfsgid, + [ 153 ] = PR_times, + [ 154 ] = PR_setpgid, + [ 155 ] = PR_getpgid, + [ 156 ] = PR_getsid, + [ 157 ] = PR_setsid, + [ 158 ] = PR_getgroups, + [ 159 ] = PR_setgroups, + [ 160 ] = PR_uname, + [ 161 ] = PR_sethostname, + [ 162 ] = PR_setdomainname, + [ 163 ] = PR_getrlimit, + [ 164 ] = PR_setrlimit, + [ 165 ] = PR_getrusage, + [ 166 ] = PR_umask, + [ 167 ] = PR_prctl, + [ 168 ] = PR_getcpu, + [ 169 ] = PR_gettimeofday, + [ 170 ] = PR_settimeofday, + [ 171 ] = PR_adjtimex, + [ 172 ] = PR_getpid, + [ 173 ] = PR_getppid, + [ 174 ] = PR_getuid, + [ 175 ] = PR_geteuid, + [ 176 ] = PR_getgid, + [ 177 ] = PR_getegid, + [ 178 ] = PR_gettid, + [ 179 ] = PR_sysinfo, + [ 180 ] = PR_mq_open, + [ 181 ] = PR_mq_unlink, + [ 182 ] = PR_mq_timedsend, + [ 183 ] = PR_mq_timedreceive, + [ 184 ] = PR_mq_notify, + [ 185 ] = PR_mq_getsetattr, + [ 186 ] = PR_msgget, + [ 187 ] = PR_msgctl, + [ 188 ] = PR_msgrcv, + [ 189 ] = PR_msgsnd, + [ 190 ] = PR_semget, + [ 191 ] = PR_semctl, + [ 192 ] = PR_semtimedop, + [ 193 ] = PR_semop, + [ 194 ] = PR_shmget, + [ 195 ] = PR_shmctl, + [ 196 ] = PR_shmat, + [ 197 ] = PR_shmdt, + [ 198 ] = PR_socket, + [ 199 ] = PR_socketpair, + [ 200 ] = PR_bind, + [ 201 ] = PR_listen, + [ 202 ] = PR_accept, + [ 203 ] = PR_connect, + [ 204 ] = PR_getsockname, + [ 205 ] = PR_getpeername, + [ 206 ] = PR_sendto, + [ 207 ] = PR_recvfrom, + [ 208 ] = PR_setsockopt, + [ 209 ] = PR_getsockopt, + [ 210 ] = PR_shutdown, + [ 211 ] = PR_sendmsg, + [ 212 ] = PR_recvmsg, + [ 213 ] = PR_readahead, + [ 214 ] = PR_brk, + [ 215 ] = PR_munmap, + [ 216 ] = PR_mremap, + [ 217 ] = PR_add_key, + [ 218 ] = PR_request_key, + [ 219 ] = PR_keyctl, + [ 220 ] = PR_clone, + [ 221 ] = PR_execve, + [ 222 ] = PR_mmap, + [ 223 ] = PR_fadvise64, + [ 224 ] = PR_swapon, + [ 225 ] = PR_swapoff, + [ 226 ] = PR_mprotect, + [ 227 ] = PR_msync, + [ 228 ] = PR_mlock, + [ 229 ] = PR_munlock, + [ 230 ] = PR_mlockall, + [ 231 ] = PR_munlockall, + [ 232 ] = PR_mincore, + [ 233 ] = PR_madvise, + [ 234 ] = PR_remap_file_pages, + [ 235 ] = PR_mbind, + [ 236 ] = PR_get_mempolicy, + [ 237 ] = PR_set_mempolicy, + [ 238 ] = PR_migrate_pages, + [ 239 ] = PR_move_pages, + [ 240 ] = PR_rt_tgsigqueueinfo, + [ 241 ] = PR_perf_event_open, + [ 242 ] = PR_accept4, + [ 243 ] = PR_recvmmsg, + [ 244 ] = PR_arch_specific_syscall, + [ 260 ] = PR_wait4, + [ 261 ] = PR_prlimit64, + [ 262 ] = PR_fanotify_init, + [ 263 ] = PR_fanotify_mark, + [ 264 ] = PR_name_to_handle_at, + [ 265 ] = PR_open_by_handle_at, + [ 266 ] = PR_clock_adjtime, + [ 267 ] = PR_syncfs, + [ 268 ] = PR_setns, + [ 269 ] = PR_sendmmsg, + [ 270 ] = PR_process_vm_readv, + [ 271 ] = PR_process_vm_writev, + [ 272 ] = PR_kcmp, + [ 273 ] = PR_finit_module, + [ 274 ] = PR_sched_setattr, + [ 275 ] = PR_sched_getattr, + [ 276 ] = PR_renameat2, +};
diff --git a/5.1.0/src/syscall/sysnums-i386.h b/5.1.0/src/syscall/sysnums-i386.h new file mode 100644 index 0000000..dff8919 --- /dev/null +++ b/5.1.0/src/syscall/sysnums-i386.h
@@ -0,0 +1,354 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_i386[] = { + [ 0 ] = PR_restart_syscall, + [ 1 ] = PR_exit, + [ 2 ] = PR_fork, + [ 3 ] = PR_read, + [ 4 ] = PR_write, + [ 5 ] = PR_open, + [ 6 ] = PR_close, + [ 7 ] = PR_waitpid, + [ 8 ] = PR_creat, + [ 9 ] = PR_link, + [ 10 ] = PR_unlink, + [ 11 ] = PR_execve, + [ 12 ] = PR_chdir, + [ 13 ] = PR_time, + [ 14 ] = PR_mknod, + [ 15 ] = PR_chmod, + [ 16 ] = PR_lchown, + [ 17 ] = PR_break, + [ 18 ] = PR_oldstat, + [ 19 ] = PR_lseek, + [ 20 ] = PR_getpid, + [ 21 ] = PR_mount, + [ 22 ] = PR_umount, + [ 23 ] = PR_setuid, + [ 24 ] = PR_getuid, + [ 25 ] = PR_stime, + [ 26 ] = PR_ptrace, + [ 27 ] = PR_alarm, + [ 28 ] = PR_oldfstat, + [ 29 ] = PR_pause, + [ 30 ] = PR_utime, + [ 31 ] = PR_stty, + [ 32 ] = PR_gtty, + [ 33 ] = PR_access, + [ 34 ] = PR_nice, + [ 35 ] = PR_ftime, + [ 36 ] = PR_sync, + [ 37 ] = PR_kill, + [ 38 ] = PR_rename, + [ 39 ] = PR_mkdir, + [ 40 ] = PR_rmdir, + [ 41 ] = PR_dup, + [ 42 ] = PR_pipe, + [ 43 ] = PR_times, + [ 44 ] = PR_prof, + [ 45 ] = PR_brk, + [ 46 ] = PR_setgid, + [ 47 ] = PR_getgid, + [ 48 ] = PR_signal, + [ 49 ] = PR_geteuid, + [ 50 ] = PR_getegid, + [ 51 ] = PR_acct, + [ 52 ] = PR_umount2, + [ 53 ] = PR_lock, + [ 54 ] = PR_ioctl, + [ 55 ] = PR_fcntl, + [ 56 ] = PR_mpx, + [ 57 ] = PR_setpgid, + [ 58 ] = PR_ulimit, + [ 59 ] = PR_oldolduname, + [ 60 ] = PR_umask, + [ 61 ] = PR_chroot, + [ 62 ] = PR_ustat, + [ 63 ] = PR_dup2, + [ 64 ] = PR_getppid, + [ 65 ] = PR_getpgrp, + [ 66 ] = PR_setsid, + [ 67 ] = PR_sigaction, + [ 68 ] = PR_sgetmask, + [ 69 ] = PR_ssetmask, + [ 70 ] = PR_setreuid, + [ 71 ] = PR_setregid, + [ 72 ] = PR_sigsuspend, + [ 73 ] = PR_sigpending, + [ 74 ] = PR_sethostname, + [ 75 ] = PR_setrlimit, + [ 76 ] = PR_getrlimit, + [ 77 ] = PR_getrusage, + [ 78 ] = PR_gettimeofday, + [ 79 ] = PR_settimeofday, + [ 80 ] = PR_getgroups, + [ 81 ] = PR_setgroups, + [ 82 ] = PR_select, + [ 83 ] = PR_symlink, + [ 84 ] = PR_oldlstat, + [ 85 ] = PR_readlink, + [ 86 ] = PR_uselib, + [ 87 ] = PR_swapon, + [ 88 ] = PR_reboot, + [ 89 ] = PR_readdir, + [ 90 ] = PR_mmap, + [ 91 ] = PR_munmap, + [ 92 ] = PR_truncate, + [ 93 ] = PR_ftruncate, + [ 94 ] = PR_fchmod, + [ 95 ] = PR_fchown, + [ 96 ] = PR_getpriority, + [ 97 ] = PR_setpriority, + [ 98 ] = PR_profil, + [ 99 ] = PR_statfs, + [ 100 ] = PR_fstatfs, + [ 101 ] = PR_ioperm, + [ 102 ] = PR_socketcall, + [ 103 ] = PR_syslog, + [ 104 ] = PR_setitimer, + [ 105 ] = PR_getitimer, + [ 106 ] = PR_stat, + [ 107 ] = PR_lstat, + [ 108 ] = PR_fstat, + [ 109 ] = PR_olduname, + [ 110 ] = PR_iopl, + [ 111 ] = PR_vhangup, + [ 112 ] = PR_idle, + [ 113 ] = PR_vm86old, + [ 114 ] = PR_wait4, + [ 115 ] = PR_swapoff, + [ 116 ] = PR_sysinfo, + [ 117 ] = PR_ipc, + [ 118 ] = PR_fsync, + [ 119 ] = PR_sigreturn, + [ 120 ] = PR_clone, + [ 121 ] = PR_setdomainname, + [ 122 ] = PR_uname, + [ 123 ] = PR_modify_ldt, + [ 124 ] = PR_adjtimex, + [ 125 ] = PR_mprotect, + [ 126 ] = PR_sigprocmask, + [ 127 ] = PR_create_module, + [ 128 ] = PR_init_module, + [ 129 ] = PR_delete_module, + [ 130 ] = PR_get_kernel_syms, + [ 131 ] = PR_quotactl, + [ 132 ] = PR_getpgid, + [ 133 ] = PR_fchdir, + [ 134 ] = PR_bdflush, + [ 135 ] = PR_sysfs, + [ 136 ] = PR_personality, + [ 137 ] = PR_afs_syscall, + [ 138 ] = PR_setfsuid, + [ 139 ] = PR_setfsgid, + [ 140 ] = PR__llseek, + [ 141 ] = PR_getdents, + [ 142 ] = PR__newselect, + [ 143 ] = PR_flock, + [ 144 ] = PR_msync, + [ 145 ] = PR_readv, + [ 146 ] = PR_writev, + [ 147 ] = PR_getsid, + [ 148 ] = PR_fdatasync, + [ 149 ] = PR__sysctl, + [ 150 ] = PR_mlock, + [ 151 ] = PR_munlock, + [ 152 ] = PR_mlockall, + [ 153 ] = PR_munlockall, + [ 154 ] = PR_sched_setparam, + [ 155 ] = PR_sched_getparam, + [ 156 ] = PR_sched_setscheduler, + [ 157 ] = PR_sched_getscheduler, + [ 158 ] = PR_sched_yield, + [ 159 ] = PR_sched_get_priority_max, + [ 160 ] = PR_sched_get_priority_min, + [ 161 ] = PR_sched_rr_get_interval, + [ 162 ] = PR_nanosleep, + [ 163 ] = PR_mremap, + [ 164 ] = PR_setresuid, + [ 165 ] = PR_getresuid, + [ 166 ] = PR_vm86, + [ 167 ] = PR_query_module, + [ 168 ] = PR_poll, + [ 169 ] = PR_nfsservctl, + [ 170 ] = PR_setresgid, + [ 171 ] = PR_getresgid, + [ 172 ] = PR_prctl, + [ 173 ] = PR_rt_sigreturn, + [ 174 ] = PR_rt_sigaction, + [ 175 ] = PR_rt_sigprocmask, + [ 176 ] = PR_rt_sigpending, + [ 177 ] = PR_rt_sigtimedwait, + [ 178 ] = PR_rt_sigqueueinfo, + [ 179 ] = PR_rt_sigsuspend, + [ 180 ] = PR_pread64, + [ 181 ] = PR_pwrite64, + [ 182 ] = PR_chown, + [ 183 ] = PR_getcwd, + [ 184 ] = PR_capget, + [ 185 ] = PR_capset, + [ 186 ] = PR_sigaltstack, + [ 187 ] = PR_sendfile, + [ 188 ] = PR_getpmsg, + [ 189 ] = PR_putpmsg, + [ 190 ] = PR_vfork, + [ 191 ] = PR_ugetrlimit, + [ 192 ] = PR_mmap2, + [ 193 ] = PR_truncate64, + [ 194 ] = PR_ftruncate64, + [ 195 ] = PR_stat64, + [ 196 ] = PR_lstat64, + [ 197 ] = PR_fstat64, + [ 198 ] = PR_lchown32, + [ 199 ] = PR_getuid32, + [ 200 ] = PR_getgid32, + [ 201 ] = PR_geteuid32, + [ 202 ] = PR_getegid32, + [ 203 ] = PR_setreuid32, + [ 204 ] = PR_setregid32, + [ 205 ] = PR_getgroups32, + [ 206 ] = PR_setgroups32, + [ 207 ] = PR_fchown32, + [ 208 ] = PR_setresuid32, + [ 209 ] = PR_getresuid32, + [ 210 ] = PR_setresgid32, + [ 211 ] = PR_getresgid32, + [ 212 ] = PR_chown32, + [ 213 ] = PR_setuid32, + [ 214 ] = PR_setgid32, + [ 215 ] = PR_setfsuid32, + [ 216 ] = PR_setfsgid32, + [ 217 ] = PR_pivot_root, + [ 218 ] = PR_mincore, + [ 219 ] = PR_madvise, + [ 220 ] = PR_getdents64, + [ 221 ] = PR_fcntl64, + [ 224 ] = PR_gettid, + [ 225 ] = PR_readahead, + [ 226 ] = PR_setxattr, + [ 227 ] = PR_lsetxattr, + [ 228 ] = PR_fsetxattr, + [ 229 ] = PR_getxattr, + [ 230 ] = PR_lgetxattr, + [ 231 ] = PR_fgetxattr, + [ 232 ] = PR_listxattr, + [ 233 ] = PR_llistxattr, + [ 234 ] = PR_flistxattr, + [ 235 ] = PR_removexattr, + [ 236 ] = PR_lremovexattr, + [ 237 ] = PR_fremovexattr, + [ 238 ] = PR_tkill, + [ 239 ] = PR_sendfile64, + [ 240 ] = PR_futex, + [ 241 ] = PR_sched_setaffinity, + [ 242 ] = PR_sched_getaffinity, + [ 243 ] = PR_set_thread_area, + [ 244 ] = PR_get_thread_area, + [ 245 ] = PR_io_setup, + [ 246 ] = PR_io_destroy, + [ 247 ] = PR_io_getevents, + [ 248 ] = PR_io_submit, + [ 249 ] = PR_io_cancel, + [ 250 ] = PR_fadvise64, + [ 252 ] = PR_exit_group, + [ 253 ] = PR_lookup_dcookie, + [ 254 ] = PR_epoll_create, + [ 255 ] = PR_epoll_ctl, + [ 256 ] = PR_epoll_wait, + [ 257 ] = PR_remap_file_pages, + [ 258 ] = PR_set_tid_address, + [ 259 ] = PR_timer_create, + [ 260 ] = PR_timer_settime, + [ 261 ] = PR_timer_gettime, + [ 262 ] = PR_timer_getoverrun, + [ 263 ] = PR_timer_delete, + [ 264 ] = PR_clock_settime, + [ 265 ] = PR_clock_gettime, + [ 266 ] = PR_clock_getres, + [ 267 ] = PR_clock_nanosleep, + [ 268 ] = PR_statfs64, + [ 269 ] = PR_fstatfs64, + [ 270 ] = PR_tgkill, + [ 271 ] = PR_utimes, + [ 272 ] = PR_fadvise64_64, + [ 273 ] = PR_vserver, + [ 274 ] = PR_mbind, + [ 275 ] = PR_get_mempolicy, + [ 276 ] = PR_set_mempolicy, + [ 277 ] = PR_mq_open, + [ 278 ] = PR_mq_unlink, + [ 279 ] = PR_mq_timedsend, + [ 280 ] = PR_mq_timedreceive, + [ 281 ] = PR_mq_notify, + [ 282 ] = PR_mq_getsetattr, + [ 283 ] = PR_kexec_load, + [ 284 ] = PR_waitid, + [ 286 ] = PR_add_key, + [ 287 ] = PR_request_key, + [ 288 ] = PR_keyctl, + [ 289 ] = PR_ioprio_set, + [ 290 ] = PR_ioprio_get, + [ 291 ] = PR_inotify_init, + [ 292 ] = PR_inotify_add_watch, + [ 293 ] = PR_inotify_rm_watch, + [ 294 ] = PR_migrate_pages, + [ 295 ] = PR_openat, + [ 296 ] = PR_mkdirat, + [ 297 ] = PR_mknodat, + [ 298 ] = PR_fchownat, + [ 299 ] = PR_futimesat, + [ 300 ] = PR_fstatat64, + [ 301 ] = PR_unlinkat, + [ 302 ] = PR_renameat, + [ 303 ] = PR_linkat, + [ 304 ] = PR_symlinkat, + [ 305 ] = PR_readlinkat, + [ 306 ] = PR_fchmodat, + [ 307 ] = PR_faccessat, + [ 308 ] = PR_pselect6, + [ 309 ] = PR_ppoll, + [ 310 ] = PR_unshare, + [ 311 ] = PR_set_robust_list, + [ 312 ] = PR_get_robust_list, + [ 313 ] = PR_splice, + [ 314 ] = PR_sync_file_range, + [ 315 ] = PR_tee, + [ 316 ] = PR_vmsplice, + [ 317 ] = PR_move_pages, + [ 318 ] = PR_getcpu, + [ 319 ] = PR_epoll_pwait, + [ 320 ] = PR_utimensat, + [ 321 ] = PR_signalfd, + [ 322 ] = PR_timerfd_create, + [ 323 ] = PR_eventfd, + [ 324 ] = PR_fallocate, + [ 325 ] = PR_timerfd_settime, + [ 326 ] = PR_timerfd_gettime, + [ 327 ] = PR_signalfd4, + [ 328 ] = PR_eventfd2, + [ 329 ] = PR_epoll_create1, + [ 330 ] = PR_dup3, + [ 331 ] = PR_pipe2, + [ 332 ] = PR_inotify_init1, + [ 333 ] = PR_preadv, + [ 334 ] = PR_pwritev, + [ 335 ] = PR_rt_tgsigqueueinfo, + [ 336 ] = PR_perf_event_open, + [ 337 ] = PR_recvmmsg, + [ 338 ] = PR_fanotify_init, + [ 339 ] = PR_fanotify_mark, + [ 340 ] = PR_prlimit64, + [ 341 ] = PR_name_to_handle_at, + [ 342 ] = PR_open_by_handle_at, + [ 343 ] = PR_clock_adjtime, + [ 344 ] = PR_syncfs, + [ 345 ] = PR_sendmmsg, + [ 346 ] = PR_setns, + [ 347 ] = PR_process_vm_readv, + [ 348 ] = PR_process_vm_writev, + [ 349 ] = PR_kcmp, + [ 350 ] = PR_finit_module, + [ 351 ] = PR_sched_setattr, + [ 352 ] = PR_sched_getattr, + [ 353 ] = PR_renameat2, +};
diff --git a/5.1.0/src/syscall/sysnums-sh4.h b/5.1.0/src/syscall/sysnums-sh4.h new file mode 100644 index 0000000..1d3758c --- /dev/null +++ b/5.1.0/src/syscall/sysnums-sh4.h
@@ -0,0 +1,347 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_sh4[] = { + [ 0 ] = PR_restart_syscall, + [ 1 ] = PR_exit, + [ 2 ] = PR_fork, + [ 3 ] = PR_read, + [ 4 ] = PR_write, + [ 5 ] = PR_open, + [ 6 ] = PR_close, + [ 7 ] = PR_waitpid, + [ 8 ] = PR_creat, + [ 9 ] = PR_link, + [ 10 ] = PR_unlink, + [ 11 ] = PR_execve, + [ 12 ] = PR_chdir, + [ 13 ] = PR_time, + [ 14 ] = PR_mknod, + [ 15 ] = PR_chmod, + [ 16 ] = PR_lchown, + [ 18 ] = PR_oldstat, + [ 19 ] = PR_lseek, + [ 20 ] = PR_getpid, + [ 21 ] = PR_mount, + [ 22 ] = PR_umount, + [ 23 ] = PR_setuid, + [ 24 ] = PR_getuid, + [ 25 ] = PR_stime, + [ 26 ] = PR_ptrace, + [ 27 ] = PR_alarm, + [ 28 ] = PR_oldfstat, + [ 29 ] = PR_pause, + [ 30 ] = PR_utime, + [ 33 ] = PR_access, + [ 34 ] = PR_nice, + [ 36 ] = PR_sync, + [ 37 ] = PR_kill, + [ 38 ] = PR_rename, + [ 39 ] = PR_mkdir, + [ 40 ] = PR_rmdir, + [ 41 ] = PR_dup, + [ 42 ] = PR_pipe, + [ 43 ] = PR_times, + [ 45 ] = PR_brk, + [ 46 ] = PR_setgid, + [ 47 ] = PR_getgid, + [ 48 ] = PR_signal, + [ 49 ] = PR_geteuid, + [ 50 ] = PR_getegid, + [ 51 ] = PR_acct, + [ 52 ] = PR_umount2, + [ 54 ] = PR_ioctl, + [ 55 ] = PR_fcntl, + [ 57 ] = PR_setpgid, + [ 60 ] = PR_umask, + [ 61 ] = PR_chroot, + [ 62 ] = PR_ustat, + [ 63 ] = PR_dup2, + [ 64 ] = PR_getppid, + [ 65 ] = PR_getpgrp, + [ 66 ] = PR_setsid, + [ 67 ] = PR_sigaction, + [ 68 ] = PR_sgetmask, + [ 69 ] = PR_ssetmask, + [ 70 ] = PR_setreuid, + [ 71 ] = PR_setregid, + [ 72 ] = PR_sigsuspend, + [ 73 ] = PR_sigpending, + [ 74 ] = PR_sethostname, + [ 75 ] = PR_setrlimit, + [ 76 ] = PR_getrlimit, + [ 77 ] = PR_getrusage, + [ 78 ] = PR_gettimeofday, + [ 79 ] = PR_settimeofday, + [ 80 ] = PR_getgroups, + [ 81 ] = PR_setgroups, + [ 83 ] = PR_symlink, + [ 84 ] = PR_oldlstat, + [ 85 ] = PR_readlink, + [ 86 ] = PR_uselib, + [ 87 ] = PR_swapon, + [ 88 ] = PR_reboot, + [ 89 ] = PR_readdir, + [ 90 ] = PR_mmap, + [ 91 ] = PR_munmap, + [ 92 ] = PR_truncate, + [ 93 ] = PR_ftruncate, + [ 94 ] = PR_fchmod, + [ 95 ] = PR_fchown, + [ 96 ] = PR_getpriority, + [ 97 ] = PR_setpriority, + [ 99 ] = PR_statfs, + [ 100 ] = PR_fstatfs, + [ 102 ] = PR_socketcall, + [ 103 ] = PR_syslog, + [ 104 ] = PR_setitimer, + [ 105 ] = PR_getitimer, + [ 106 ] = PR_stat, + [ 107 ] = PR_lstat, + [ 108 ] = PR_fstat, + [ 109 ] = PR_olduname, + [ 111 ] = PR_vhangup, + [ 114 ] = PR_wait4, + [ 115 ] = PR_swapoff, + [ 116 ] = PR_sysinfo, + [ 117 ] = PR_ipc, + [ 118 ] = PR_fsync, + [ 119 ] = PR_sigreturn, + [ 120 ] = PR_clone, + [ 121 ] = PR_setdomainname, + [ 122 ] = PR_uname, + [ 123 ] = PR_cacheflush, + [ 124 ] = PR_adjtimex, + [ 125 ] = PR_mprotect, + [ 126 ] = PR_sigprocmask, + [ 128 ] = PR_init_module, + [ 129 ] = PR_delete_module, + [ 131 ] = PR_quotactl, + [ 132 ] = PR_getpgid, + [ 133 ] = PR_fchdir, + [ 134 ] = PR_bdflush, + [ 135 ] = PR_sysfs, + [ 136 ] = PR_personality, + [ 138 ] = PR_setfsuid, + [ 139 ] = PR_setfsgid, + [ 140 ] = PR__llseek, + [ 141 ] = PR_getdents, + [ 142 ] = PR__newselect, + [ 143 ] = PR_flock, + [ 144 ] = PR_msync, + [ 145 ] = PR_readv, + [ 146 ] = PR_writev, + [ 147 ] = PR_getsid, + [ 148 ] = PR_fdatasync, + [ 149 ] = PR__sysctl, + [ 150 ] = PR_mlock, + [ 151 ] = PR_munlock, + [ 152 ] = PR_mlockall, + [ 153 ] = PR_munlockall, + [ 154 ] = PR_sched_setparam, + [ 155 ] = PR_sched_getparam, + [ 156 ] = PR_sched_setscheduler, + [ 157 ] = PR_sched_getscheduler, + [ 158 ] = PR_sched_yield, + [ 159 ] = PR_sched_get_priority_max, + [ 160 ] = PR_sched_get_priority_min, + [ 161 ] = PR_sched_rr_get_interval, + [ 162 ] = PR_nanosleep, + [ 163 ] = PR_mremap, + [ 164 ] = PR_setresuid, + [ 165 ] = PR_getresuid, + [ 168 ] = PR_poll, + [ 169 ] = PR_nfsservctl, + [ 170 ] = PR_setresgid, + [ 171 ] = PR_getresgid, + [ 172 ] = PR_prctl, + [ 173 ] = PR_rt_sigreturn, + [ 174 ] = PR_rt_sigaction, + [ 175 ] = PR_rt_sigprocmask, + [ 176 ] = PR_rt_sigpending, + [ 177 ] = PR_rt_sigtimedwait, + [ 178 ] = PR_rt_sigqueueinfo, + [ 179 ] = PR_rt_sigsuspend, + [ 180 ] = PR_pread64, + [ 181 ] = PR_pwrite64, + [ 182 ] = PR_chown, + [ 183 ] = PR_getcwd, + [ 184 ] = PR_capget, + [ 185 ] = PR_capset, + [ 186 ] = PR_sigaltstack, + [ 187 ] = PR_sendfile, + [ 190 ] = PR_vfork, + [ 191 ] = PR_ugetrlimit, + [ 192 ] = PR_mmap2, + [ 193 ] = PR_truncate64, + [ 194 ] = PR_ftruncate64, + [ 195 ] = PR_stat64, + [ 196 ] = PR_lstat64, + [ 197 ] = PR_fstat64, + [ 198 ] = PR_lchown32, + [ 199 ] = PR_getuid32, + [ 200 ] = PR_getgid32, + [ 201 ] = PR_geteuid32, + [ 202 ] = PR_getegid32, + [ 203 ] = PR_setreuid32, + [ 204 ] = PR_setregid32, + [ 205 ] = PR_getgroups32, + [ 206 ] = PR_setgroups32, + [ 207 ] = PR_fchown32, + [ 208 ] = PR_setresuid32, + [ 209 ] = PR_getresuid32, + [ 210 ] = PR_setresgid32, + [ 211 ] = PR_getresgid32, + [ 212 ] = PR_chown32, + [ 213 ] = PR_setuid32, + [ 214 ] = PR_setgid32, + [ 215 ] = PR_setfsuid32, + [ 216 ] = PR_setfsgid32, + [ 217 ] = PR_pivot_root, + [ 218 ] = PR_mincore, + [ 219 ] = PR_madvise, + [ 220 ] = PR_getdents64, + [ 221 ] = PR_fcntl64, + [ 224 ] = PR_gettid, + [ 225 ] = PR_readahead, + [ 226 ] = PR_setxattr, + [ 227 ] = PR_lsetxattr, + [ 228 ] = PR_fsetxattr, + [ 229 ] = PR_getxattr, + [ 230 ] = PR_lgetxattr, + [ 231 ] = PR_fgetxattr, + [ 232 ] = PR_listxattr, + [ 233 ] = PR_llistxattr, + [ 234 ] = PR_flistxattr, + [ 235 ] = PR_removexattr, + [ 236 ] = PR_lremovexattr, + [ 237 ] = PR_fremovexattr, + [ 238 ] = PR_tkill, + [ 239 ] = PR_sendfile64, + [ 240 ] = PR_futex, + [ 241 ] = PR_sched_setaffinity, + [ 242 ] = PR_sched_getaffinity, + [ 245 ] = PR_io_setup, + [ 246 ] = PR_io_destroy, + [ 247 ] = PR_io_getevents, + [ 248 ] = PR_io_submit, + [ 249 ] = PR_io_cancel, + [ 250 ] = PR_fadvise64, + [ 252 ] = PR_exit_group, + [ 253 ] = PR_lookup_dcookie, + [ 254 ] = PR_epoll_create, + [ 255 ] = PR_epoll_ctl, + [ 256 ] = PR_epoll_wait, + [ 257 ] = PR_remap_file_pages, + [ 258 ] = PR_set_tid_address, + [ 259 ] = PR_timer_create, + [ 260 ] = PR_timer_settime, + [ 261 ] = PR_timer_gettime, + [ 262 ] = PR_timer_getoverrun, + [ 263 ] = PR_timer_delete, + [ 264 ] = PR_clock_settime, + [ 265 ] = PR_clock_gettime, + [ 266 ] = PR_clock_getres, + [ 267 ] = PR_clock_nanosleep, + [ 268 ] = PR_statfs64, + [ 269 ] = PR_fstatfs64, + [ 270 ] = PR_tgkill, + [ 271 ] = PR_utimes, + [ 272 ] = PR_fadvise64_64, + [ 274 ] = PR_mbind, + [ 275 ] = PR_get_mempolicy, + [ 276 ] = PR_set_mempolicy, + [ 277 ] = PR_mq_open, + [ 278 ] = PR_mq_unlink, + [ 279 ] = PR_mq_timedsend, + [ 280 ] = PR_mq_timedreceive, + [ 281 ] = PR_mq_notify, + [ 282 ] = PR_mq_getsetattr, + [ 283 ] = PR_kexec_load, + [ 284 ] = PR_waitid, + [ 285 ] = PR_add_key, + [ 286 ] = PR_request_key, + [ 287 ] = PR_keyctl, + [ 288 ] = PR_ioprio_set, + [ 289 ] = PR_ioprio_get, + [ 290 ] = PR_inotify_init, + [ 291 ] = PR_inotify_add_watch, + [ 292 ] = PR_inotify_rm_watch, + [ 294 ] = PR_migrate_pages, + [ 295 ] = PR_openat, + [ 296 ] = PR_mkdirat, + [ 297 ] = PR_mknodat, + [ 298 ] = PR_fchownat, + [ 299 ] = PR_futimesat, + [ 300 ] = PR_fstatat64, + [ 301 ] = PR_unlinkat, + [ 302 ] = PR_renameat, + [ 303 ] = PR_linkat, + [ 304 ] = PR_symlinkat, + [ 305 ] = PR_readlinkat, + [ 306 ] = PR_fchmodat, + [ 307 ] = PR_faccessat, + [ 308 ] = PR_pselect6, + [ 309 ] = PR_ppoll, + [ 310 ] = PR_unshare, + [ 311 ] = PR_set_robust_list, + [ 312 ] = PR_get_robust_list, + [ 313 ] = PR_splice, + [ 314 ] = PR_sync_file_range, + [ 315 ] = PR_tee, + [ 316 ] = PR_vmsplice, + [ 317 ] = PR_move_pages, + [ 318 ] = PR_getcpu, + [ 319 ] = PR_epoll_pwait, + [ 320 ] = PR_utimensat, + [ 321 ] = PR_signalfd, + [ 322 ] = PR_timerfd_create, + [ 323 ] = PR_eventfd, + [ 324 ] = PR_fallocate, + [ 325 ] = PR_timerfd_settime, + [ 326 ] = PR_timerfd_gettime, + [ 327 ] = PR_signalfd4, + [ 328 ] = PR_eventfd2, + [ 329 ] = PR_epoll_create1, + [ 330 ] = PR_dup3, + [ 331 ] = PR_pipe2, + [ 332 ] = PR_inotify_init1, + [ 333 ] = PR_preadv, + [ 334 ] = PR_pwritev, + [ 335 ] = PR_rt_tgsigqueueinfo, + [ 336 ] = PR_perf_event_open, + [ 337 ] = PR_fanotify_init, + [ 338 ] = PR_fanotify_mark, + [ 339 ] = PR_prlimit64, + [ 340 ] = PR_socket, + [ 341 ] = PR_bind, + [ 342 ] = PR_connect, + [ 343 ] = PR_listen, + [ 344 ] = PR_accept, + [ 345 ] = PR_getsockname, + [ 346 ] = PR_getpeername, + [ 347 ] = PR_socketpair, + [ 348 ] = PR_send, + [ 349 ] = PR_sendto, + [ 350 ] = PR_recv, + [ 351 ] = PR_recvfrom, + [ 352 ] = PR_shutdown, + [ 353 ] = PR_setsockopt, + [ 354 ] = PR_getsockopt, + [ 355 ] = PR_sendmsg, + [ 356 ] = PR_recvmsg, + [ 357 ] = PR_recvmmsg, + [ 358 ] = PR_accept4, + [ 359 ] = PR_name_to_handle_at, + [ 360 ] = PR_open_by_handle_at, + [ 361 ] = PR_clock_adjtime, + [ 362 ] = PR_syncfs, + [ 363 ] = PR_sendmmsg, + [ 364 ] = PR_setns, + [ 365 ] = PR_process_vm_readv, + [ 366 ] = PR_process_vm_writev, + [ 367 ] = PR_kcmp, + [ 368 ] = PR_finit_module, + [ 369 ] = PR_sched_setattr, + [ 370 ] = PR_sched_getattr, + [ 371 ] = PR_renameat2, +};
diff --git a/5.1.0/src/syscall/sysnums-x32.h b/5.1.0/src/syscall/sysnums-x32.h new file mode 100644 index 0000000..0c3405b --- /dev/null +++ b/5.1.0/src/syscall/sysnums-x32.h
@@ -0,0 +1,310 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_x32[] = { + [ 0 ] = PR_read, + [ 1 ] = PR_write, + [ 2 ] = PR_open, + [ 3 ] = PR_close, + [ 4 ] = PR_stat, + [ 5 ] = PR_fstat, + [ 6 ] = PR_lstat, + [ 7 ] = PR_poll, + [ 8 ] = PR_lseek, + [ 9 ] = PR_mmap, + [ 10 ] = PR_mprotect, + [ 11 ] = PR_munmap, + [ 12 ] = PR_brk, + [ 14 ] = PR_rt_sigprocmask, + [ 17 ] = PR_pread64, + [ 18 ] = PR_pwrite64, + [ 21 ] = PR_access, + [ 22 ] = PR_pipe, + [ 23 ] = PR_select, + [ 24 ] = PR_sched_yield, + [ 25 ] = PR_mremap, + [ 26 ] = PR_msync, + [ 27 ] = PR_mincore, + [ 28 ] = PR_madvise, + [ 29 ] = PR_shmget, + [ 30 ] = PR_shmat, + [ 31 ] = PR_shmctl, + [ 32 ] = PR_dup, + [ 33 ] = PR_dup2, + [ 34 ] = PR_pause, + [ 35 ] = PR_nanosleep, + [ 36 ] = PR_getitimer, + [ 37 ] = PR_alarm, + [ 38 ] = PR_setitimer, + [ 39 ] = PR_getpid, + [ 40 ] = PR_sendfile, + [ 41 ] = PR_socket, + [ 42 ] = PR_connect, + [ 43 ] = PR_accept, + [ 44 ] = PR_sendto, + [ 48 ] = PR_shutdown, + [ 49 ] = PR_bind, + [ 50 ] = PR_listen, + [ 51 ] = PR_getsockname, + [ 52 ] = PR_getpeername, + [ 53 ] = PR_socketpair, + [ 56 ] = PR_clone, + [ 57 ] = PR_fork, + [ 58 ] = PR_vfork, + [ 60 ] = PR_exit, + [ 61 ] = PR_wait4, + [ 62 ] = PR_kill, + [ 63 ] = PR_uname, + [ 64 ] = PR_semget, + [ 65 ] = PR_semop, + [ 66 ] = PR_semctl, + [ 67 ] = PR_shmdt, + [ 68 ] = PR_msgget, + [ 69 ] = PR_msgsnd, + [ 70 ] = PR_msgrcv, + [ 71 ] = PR_msgctl, + [ 72 ] = PR_fcntl, + [ 73 ] = PR_flock, + [ 74 ] = PR_fsync, + [ 75 ] = PR_fdatasync, + [ 76 ] = PR_truncate, + [ 77 ] = PR_ftruncate, + [ 78 ] = PR_getdents, + [ 79 ] = PR_getcwd, + [ 80 ] = PR_chdir, + [ 81 ] = PR_fchdir, + [ 82 ] = PR_rename, + [ 83 ] = PR_mkdir, + [ 84 ] = PR_rmdir, + [ 85 ] = PR_creat, + [ 86 ] = PR_link, + [ 87 ] = PR_unlink, + [ 88 ] = PR_symlink, + [ 89 ] = PR_readlink, + [ 90 ] = PR_chmod, + [ 91 ] = PR_fchmod, + [ 92 ] = PR_chown, + [ 93 ] = PR_fchown, + [ 94 ] = PR_lchown, + [ 95 ] = PR_umask, + [ 96 ] = PR_gettimeofday, + [ 97 ] = PR_getrlimit, + [ 98 ] = PR_getrusage, + [ 99 ] = PR_sysinfo, + [ 100 ] = PR_times, + [ 102 ] = PR_getuid, + [ 103 ] = PR_syslog, + [ 104 ] = PR_getgid, + [ 105 ] = PR_setuid, + [ 106 ] = PR_setgid, + [ 107 ] = PR_geteuid, + [ 108 ] = PR_getegid, + [ 109 ] = PR_setpgid, + [ 110 ] = PR_getppid, + [ 111 ] = PR_getpgrp, + [ 112 ] = PR_setsid, + [ 113 ] = PR_setreuid, + [ 114 ] = PR_setregid, + [ 115 ] = PR_getgroups, + [ 116 ] = PR_setgroups, + [ 117 ] = PR_setresuid, + [ 118 ] = PR_getresuid, + [ 119 ] = PR_setresgid, + [ 120 ] = PR_getresgid, + [ 121 ] = PR_getpgid, + [ 122 ] = PR_setfsuid, + [ 123 ] = PR_setfsgid, + [ 124 ] = PR_getsid, + [ 125 ] = PR_capget, + [ 126 ] = PR_capset, + [ 130 ] = PR_rt_sigsuspend, + [ 132 ] = PR_utime, + [ 133 ] = PR_mknod, + [ 135 ] = PR_personality, + [ 136 ] = PR_ustat, + [ 137 ] = PR_statfs, + [ 138 ] = PR_fstatfs, + [ 139 ] = PR_sysfs, + [ 140 ] = PR_getpriority, + [ 141 ] = PR_setpriority, + [ 142 ] = PR_sched_setparam, + [ 143 ] = PR_sched_getparam, + [ 144 ] = PR_sched_setscheduler, + [ 145 ] = PR_sched_getscheduler, + [ 146 ] = PR_sched_get_priority_max, + [ 147 ] = PR_sched_get_priority_min, + [ 148 ] = PR_sched_rr_get_interval, + [ 149 ] = PR_mlock, + [ 150 ] = PR_munlock, + [ 151 ] = PR_mlockall, + [ 152 ] = PR_munlockall, + [ 153 ] = PR_vhangup, + [ 154 ] = PR_modify_ldt, + [ 155 ] = PR_pivot_root, + [ 157 ] = PR_prctl, + [ 158 ] = PR_arch_prctl, + [ 159 ] = PR_adjtimex, + [ 160 ] = PR_setrlimit, + [ 161 ] = PR_chroot, + [ 162 ] = PR_sync, + [ 163 ] = PR_acct, + [ 164 ] = PR_settimeofday, + [ 165 ] = PR_mount, + [ 166 ] = PR_umount2, + [ 167 ] = PR_swapon, + [ 168 ] = PR_swapoff, + [ 169 ] = PR_reboot, + [ 170 ] = PR_sethostname, + [ 171 ] = PR_setdomainname, + [ 172 ] = PR_iopl, + [ 173 ] = PR_ioperm, + [ 175 ] = PR_init_module, + [ 176 ] = PR_delete_module, + [ 179 ] = PR_quotactl, + [ 181 ] = PR_getpmsg, + [ 182 ] = PR_putpmsg, + [ 183 ] = PR_afs_syscall, + [ 184 ] = PR_tuxcall, + [ 185 ] = PR_security, + [ 186 ] = PR_gettid, + [ 187 ] = PR_readahead, + [ 188 ] = PR_setxattr, + [ 189 ] = PR_lsetxattr, + [ 190 ] = PR_fsetxattr, + [ 191 ] = PR_getxattr, + [ 192 ] = PR_lgetxattr, + [ 193 ] = PR_fgetxattr, + [ 194 ] = PR_listxattr, + [ 195 ] = PR_llistxattr, + [ 196 ] = PR_flistxattr, + [ 197 ] = PR_removexattr, + [ 198 ] = PR_lremovexattr, + [ 199 ] = PR_fremovexattr, + [ 200 ] = PR_tkill, + [ 201 ] = PR_time, + [ 202 ] = PR_futex, + [ 203 ] = PR_sched_setaffinity, + [ 204 ] = PR_sched_getaffinity, + [ 206 ] = PR_io_setup, + [ 207 ] = PR_io_destroy, + [ 208 ] = PR_io_getevents, + [ 209 ] = PR_io_submit, + [ 210 ] = PR_io_cancel, + [ 212 ] = PR_lookup_dcookie, + [ 213 ] = PR_epoll_create, + [ 216 ] = PR_remap_file_pages, + [ 217 ] = PR_getdents64, + [ 218 ] = PR_set_tid_address, + [ 219 ] = PR_restart_syscall, + [ 220 ] = PR_semtimedop, + [ 221 ] = PR_fadvise64, + [ 223 ] = PR_timer_settime, + [ 224 ] = PR_timer_gettime, + [ 225 ] = PR_timer_getoverrun, + [ 226 ] = PR_timer_delete, + [ 227 ] = PR_clock_settime, + [ 228 ] = PR_clock_gettime, + [ 229 ] = PR_clock_getres, + [ 230 ] = PR_clock_nanosleep, + [ 231 ] = PR_exit_group, + [ 232 ] = PR_epoll_wait, + [ 233 ] = PR_epoll_ctl, + [ 234 ] = PR_tgkill, + [ 235 ] = PR_utimes, + [ 237 ] = PR_mbind, + [ 238 ] = PR_set_mempolicy, + [ 239 ] = PR_get_mempolicy, + [ 240 ] = PR_mq_open, + [ 241 ] = PR_mq_unlink, + [ 242 ] = PR_mq_timedsend, + [ 243 ] = PR_mq_timedreceive, + [ 245 ] = PR_mq_getsetattr, + [ 248 ] = PR_add_key, + [ 249 ] = PR_request_key, + [ 250 ] = PR_keyctl, + [ 251 ] = PR_ioprio_set, + [ 252 ] = PR_ioprio_get, + [ 253 ] = PR_inotify_init, + [ 254 ] = PR_inotify_add_watch, + [ 255 ] = PR_inotify_rm_watch, + [ 256 ] = PR_migrate_pages, + [ 257 ] = PR_openat, + [ 258 ] = PR_mkdirat, + [ 259 ] = PR_mknodat, + [ 260 ] = PR_fchownat, + [ 261 ] = PR_futimesat, + [ 262 ] = PR_newfstatat, + [ 263 ] = PR_unlinkat, + [ 264 ] = PR_renameat, + [ 265 ] = PR_linkat, + [ 266 ] = PR_symlinkat, + [ 267 ] = PR_readlinkat, + [ 268 ] = PR_fchmodat, + [ 269 ] = PR_faccessat, + [ 270 ] = PR_pselect6, + [ 271 ] = PR_ppoll, + [ 272 ] = PR_unshare, + [ 275 ] = PR_splice, + [ 276 ] = PR_tee, + [ 277 ] = PR_sync_file_range, + [ 280 ] = PR_utimensat, + [ 281 ] = PR_epoll_pwait, + [ 282 ] = PR_signalfd, + [ 283 ] = PR_timerfd_create, + [ 284 ] = PR_eventfd, + [ 285 ] = PR_fallocate, + [ 286 ] = PR_timerfd_settime, + [ 287 ] = PR_timerfd_gettime, + [ 288 ] = PR_accept4, + [ 289 ] = PR_signalfd4, + [ 290 ] = PR_eventfd2, + [ 291 ] = PR_epoll_create1, + [ 292 ] = PR_dup3, + [ 293 ] = PR_pipe2, + [ 294 ] = PR_inotify_init1, + [ 298 ] = PR_perf_event_open, + [ 300 ] = PR_fanotify_init, + [ 301 ] = PR_fanotify_mark, + [ 302 ] = PR_prlimit64, + [ 303 ] = PR_name_to_handle_at, + [ 304 ] = PR_open_by_handle_at, + [ 305 ] = PR_clock_adjtime, + [ 306 ] = PR_syncfs, + [ 308 ] = PR_setns, + [ 309 ] = PR_getcpu, + [ 312 ] = PR_kcmp, + [ 313 ] = PR_finit_module, + [ 314 ] = PR_sched_setattr, + [ 315 ] = PR_sched_getattr, + [ 316 ] = PR_renameat2, + [ 512 ] = PR_rt_sigaction, + [ 513 ] = PR_rt_sigreturn, + [ 514 ] = PR_ioctl, + [ 515 ] = PR_readv, + [ 516 ] = PR_writev, + [ 517 ] = PR_recvfrom, + [ 518 ] = PR_sendmsg, + [ 519 ] = PR_recvmsg, + [ 520 ] = PR_execve, + [ 521 ] = PR_ptrace, + [ 522 ] = PR_rt_sigpending, + [ 523 ] = PR_rt_sigtimedwait, + [ 524 ] = PR_rt_sigqueueinfo, + [ 525 ] = PR_sigaltstack, + [ 526 ] = PR_timer_create, + [ 527 ] = PR_mq_notify, + [ 528 ] = PR_kexec_load, + [ 529 ] = PR_waitid, + [ 530 ] = PR_set_robust_list, + [ 531 ] = PR_get_robust_list, + [ 532 ] = PR_vmsplice, + [ 533 ] = PR_move_pages, + [ 534 ] = PR_preadv, + [ 535 ] = PR_pwritev, + [ 536 ] = PR_rt_tgsigqueueinfo, + [ 537 ] = PR_recvmmsg, + [ 538 ] = PR_sendmmsg, + [ 539 ] = PR_process_vm_readv, + [ 540 ] = PR_process_vm_writev, + [ 541 ] = PR_setsockopt, + [ 542 ] = PR_getsockopt, +};
diff --git a/5.1.0/src/syscall/sysnums-x86_64.h b/5.1.0/src/syscall/sysnums-x86_64.h new file mode 100644 index 0000000..eb44166 --- /dev/null +++ b/5.1.0/src/syscall/sysnums-x86_64.h
@@ -0,0 +1,321 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_x86_64[] = { + [ 0 ] = PR_read, + [ 1 ] = PR_write, + [ 2 ] = PR_open, + [ 3 ] = PR_close, + [ 4 ] = PR_stat, + [ 5 ] = PR_fstat, + [ 6 ] = PR_lstat, + [ 7 ] = PR_poll, + [ 8 ] = PR_lseek, + [ 9 ] = PR_mmap, + [ 10 ] = PR_mprotect, + [ 11 ] = PR_munmap, + [ 12 ] = PR_brk, + [ 13 ] = PR_rt_sigaction, + [ 14 ] = PR_rt_sigprocmask, + [ 15 ] = PR_rt_sigreturn, + [ 16 ] = PR_ioctl, + [ 17 ] = PR_pread64, + [ 18 ] = PR_pwrite64, + [ 19 ] = PR_readv, + [ 20 ] = PR_writev, + [ 21 ] = PR_access, + [ 22 ] = PR_pipe, + [ 23 ] = PR_select, + [ 24 ] = PR_sched_yield, + [ 25 ] = PR_mremap, + [ 26 ] = PR_msync, + [ 27 ] = PR_mincore, + [ 28 ] = PR_madvise, + [ 29 ] = PR_shmget, + [ 30 ] = PR_shmat, + [ 31 ] = PR_shmctl, + [ 32 ] = PR_dup, + [ 33 ] = PR_dup2, + [ 34 ] = PR_pause, + [ 35 ] = PR_nanosleep, + [ 36 ] = PR_getitimer, + [ 37 ] = PR_alarm, + [ 38 ] = PR_setitimer, + [ 39 ] = PR_getpid, + [ 40 ] = PR_sendfile, + [ 41 ] = PR_socket, + [ 42 ] = PR_connect, + [ 43 ] = PR_accept, + [ 44 ] = PR_sendto, + [ 45 ] = PR_recvfrom, + [ 46 ] = PR_sendmsg, + [ 47 ] = PR_recvmsg, + [ 48 ] = PR_shutdown, + [ 49 ] = PR_bind, + [ 50 ] = PR_listen, + [ 51 ] = PR_getsockname, + [ 52 ] = PR_getpeername, + [ 53 ] = PR_socketpair, + [ 54 ] = PR_setsockopt, + [ 55 ] = PR_getsockopt, + [ 56 ] = PR_clone, + [ 57 ] = PR_fork, + [ 58 ] = PR_vfork, + [ 59 ] = PR_execve, + [ 60 ] = PR_exit, + [ 61 ] = PR_wait4, + [ 62 ] = PR_kill, + [ 63 ] = PR_uname, + [ 64 ] = PR_semget, + [ 65 ] = PR_semop, + [ 66 ] = PR_semctl, + [ 67 ] = PR_shmdt, + [ 68 ] = PR_msgget, + [ 69 ] = PR_msgsnd, + [ 70 ] = PR_msgrcv, + [ 71 ] = PR_msgctl, + [ 72 ] = PR_fcntl, + [ 73 ] = PR_flock, + [ 74 ] = PR_fsync, + [ 75 ] = PR_fdatasync, + [ 76 ] = PR_truncate, + [ 77 ] = PR_ftruncate, + [ 78 ] = PR_getdents, + [ 79 ] = PR_getcwd, + [ 80 ] = PR_chdir, + [ 81 ] = PR_fchdir, + [ 82 ] = PR_rename, + [ 83 ] = PR_mkdir, + [ 84 ] = PR_rmdir, + [ 85 ] = PR_creat, + [ 86 ] = PR_link, + [ 87 ] = PR_unlink, + [ 88 ] = PR_symlink, + [ 89 ] = PR_readlink, + [ 90 ] = PR_chmod, + [ 91 ] = PR_fchmod, + [ 92 ] = PR_chown, + [ 93 ] = PR_fchown, + [ 94 ] = PR_lchown, + [ 95 ] = PR_umask, + [ 96 ] = PR_gettimeofday, + [ 97 ] = PR_getrlimit, + [ 98 ] = PR_getrusage, + [ 99 ] = PR_sysinfo, + [ 100 ] = PR_times, + [ 101 ] = PR_ptrace, + [ 102 ] = PR_getuid, + [ 103 ] = PR_syslog, + [ 104 ] = PR_getgid, + [ 105 ] = PR_setuid, + [ 106 ] = PR_setgid, + [ 107 ] = PR_geteuid, + [ 108 ] = PR_getegid, + [ 109 ] = PR_setpgid, + [ 110 ] = PR_getppid, + [ 111 ] = PR_getpgrp, + [ 112 ] = PR_setsid, + [ 113 ] = PR_setreuid, + [ 114 ] = PR_setregid, + [ 115 ] = PR_getgroups, + [ 116 ] = PR_setgroups, + [ 117 ] = PR_setresuid, + [ 118 ] = PR_getresuid, + [ 119 ] = PR_setresgid, + [ 120 ] = PR_getresgid, + [ 121 ] = PR_getpgid, + [ 122 ] = PR_setfsuid, + [ 123 ] = PR_setfsgid, + [ 124 ] = PR_getsid, + [ 125 ] = PR_capget, + [ 126 ] = PR_capset, + [ 127 ] = PR_rt_sigpending, + [ 128 ] = PR_rt_sigtimedwait, + [ 129 ] = PR_rt_sigqueueinfo, + [ 130 ] = PR_rt_sigsuspend, + [ 131 ] = PR_sigaltstack, + [ 132 ] = PR_utime, + [ 133 ] = PR_mknod, + [ 134 ] = PR_uselib, + [ 135 ] = PR_personality, + [ 136 ] = PR_ustat, + [ 137 ] = PR_statfs, + [ 138 ] = PR_fstatfs, + [ 139 ] = PR_sysfs, + [ 140 ] = PR_getpriority, + [ 141 ] = PR_setpriority, + [ 142 ] = PR_sched_setparam, + [ 143 ] = PR_sched_getparam, + [ 144 ] = PR_sched_setscheduler, + [ 145 ] = PR_sched_getscheduler, + [ 146 ] = PR_sched_get_priority_max, + [ 147 ] = PR_sched_get_priority_min, + [ 148 ] = PR_sched_rr_get_interval, + [ 149 ] = PR_mlock, + [ 150 ] = PR_munlock, + [ 151 ] = PR_mlockall, + [ 152 ] = PR_munlockall, + [ 153 ] = PR_vhangup, + [ 154 ] = PR_modify_ldt, + [ 155 ] = PR_pivot_root, + [ 156 ] = PR__sysctl, + [ 157 ] = PR_prctl, + [ 158 ] = PR_arch_prctl, + [ 159 ] = PR_adjtimex, + [ 160 ] = PR_setrlimit, + [ 161 ] = PR_chroot, + [ 162 ] = PR_sync, + [ 163 ] = PR_acct, + [ 164 ] = PR_settimeofday, + [ 165 ] = PR_mount, + [ 166 ] = PR_umount2, + [ 167 ] = PR_swapon, + [ 168 ] = PR_swapoff, + [ 169 ] = PR_reboot, + [ 170 ] = PR_sethostname, + [ 171 ] = PR_setdomainname, + [ 172 ] = PR_iopl, + [ 173 ] = PR_ioperm, + [ 174 ] = PR_create_module, + [ 175 ] = PR_init_module, + [ 176 ] = PR_delete_module, + [ 177 ] = PR_get_kernel_syms, + [ 178 ] = PR_query_module, + [ 179 ] = PR_quotactl, + [ 180 ] = PR_nfsservctl, + [ 181 ] = PR_getpmsg, + [ 182 ] = PR_putpmsg, + [ 183 ] = PR_afs_syscall, + [ 184 ] = PR_tuxcall, + [ 185 ] = PR_security, + [ 186 ] = PR_gettid, + [ 187 ] = PR_readahead, + [ 188 ] = PR_setxattr, + [ 189 ] = PR_lsetxattr, + [ 190 ] = PR_fsetxattr, + [ 191 ] = PR_getxattr, + [ 192 ] = PR_lgetxattr, + [ 193 ] = PR_fgetxattr, + [ 194 ] = PR_listxattr, + [ 195 ] = PR_llistxattr, + [ 196 ] = PR_flistxattr, + [ 197 ] = PR_removexattr, + [ 198 ] = PR_lremovexattr, + [ 199 ] = PR_fremovexattr, + [ 200 ] = PR_tkill, + [ 201 ] = PR_time, + [ 202 ] = PR_futex, + [ 203 ] = PR_sched_setaffinity, + [ 204 ] = PR_sched_getaffinity, + [ 205 ] = PR_set_thread_area, + [ 206 ] = PR_io_setup, + [ 207 ] = PR_io_destroy, + [ 208 ] = PR_io_getevents, + [ 209 ] = PR_io_submit, + [ 210 ] = PR_io_cancel, + [ 211 ] = PR_get_thread_area, + [ 212 ] = PR_lookup_dcookie, + [ 213 ] = PR_epoll_create, + [ 214 ] = PR_epoll_ctl_old, + [ 215 ] = PR_epoll_wait_old, + [ 216 ] = PR_remap_file_pages, + [ 217 ] = PR_getdents64, + [ 218 ] = PR_set_tid_address, + [ 219 ] = PR_restart_syscall, + [ 220 ] = PR_semtimedop, + [ 221 ] = PR_fadvise64, + [ 222 ] = PR_timer_create, + [ 223 ] = PR_timer_settime, + [ 224 ] = PR_timer_gettime, + [ 225 ] = PR_timer_getoverrun, + [ 226 ] = PR_timer_delete, + [ 227 ] = PR_clock_settime, + [ 228 ] = PR_clock_gettime, + [ 229 ] = PR_clock_getres, + [ 230 ] = PR_clock_nanosleep, + [ 231 ] = PR_exit_group, + [ 232 ] = PR_epoll_wait, + [ 233 ] = PR_epoll_ctl, + [ 234 ] = PR_tgkill, + [ 235 ] = PR_utimes, + [ 236 ] = PR_vserver, + [ 237 ] = PR_mbind, + [ 238 ] = PR_set_mempolicy, + [ 239 ] = PR_get_mempolicy, + [ 240 ] = PR_mq_open, + [ 241 ] = PR_mq_unlink, + [ 242 ] = PR_mq_timedsend, + [ 243 ] = PR_mq_timedreceive, + [ 244 ] = PR_mq_notify, + [ 245 ] = PR_mq_getsetattr, + [ 246 ] = PR_kexec_load, + [ 247 ] = PR_waitid, + [ 248 ] = PR_add_key, + [ 249 ] = PR_request_key, + [ 250 ] = PR_keyctl, + [ 251 ] = PR_ioprio_set, + [ 252 ] = PR_ioprio_get, + [ 253 ] = PR_inotify_init, + [ 254 ] = PR_inotify_add_watch, + [ 255 ] = PR_inotify_rm_watch, + [ 256 ] = PR_migrate_pages, + [ 257 ] = PR_openat, + [ 258 ] = PR_mkdirat, + [ 259 ] = PR_mknodat, + [ 260 ] = PR_fchownat, + [ 261 ] = PR_futimesat, + [ 262 ] = PR_newfstatat, + [ 263 ] = PR_unlinkat, + [ 264 ] = PR_renameat, + [ 265 ] = PR_linkat, + [ 266 ] = PR_symlinkat, + [ 267 ] = PR_readlinkat, + [ 268 ] = PR_fchmodat, + [ 269 ] = PR_faccessat, + [ 270 ] = PR_pselect6, + [ 271 ] = PR_ppoll, + [ 272 ] = PR_unshare, + [ 273 ] = PR_set_robust_list, + [ 274 ] = PR_get_robust_list, + [ 275 ] = PR_splice, + [ 276 ] = PR_tee, + [ 277 ] = PR_sync_file_range, + [ 278 ] = PR_vmsplice, + [ 279 ] = PR_move_pages, + [ 280 ] = PR_utimensat, + [ 281 ] = PR_epoll_pwait, + [ 282 ] = PR_signalfd, + [ 283 ] = PR_timerfd_create, + [ 284 ] = PR_eventfd, + [ 285 ] = PR_fallocate, + [ 286 ] = PR_timerfd_settime, + [ 287 ] = PR_timerfd_gettime, + [ 288 ] = PR_accept4, + [ 289 ] = PR_signalfd4, + [ 290 ] = PR_eventfd2, + [ 291 ] = PR_epoll_create1, + [ 292 ] = PR_dup3, + [ 293 ] = PR_pipe2, + [ 294 ] = PR_inotify_init1, + [ 295 ] = PR_preadv, + [ 296 ] = PR_pwritev, + [ 297 ] = PR_rt_tgsigqueueinfo, + [ 298 ] = PR_perf_event_open, + [ 299 ] = PR_recvmmsg, + [ 300 ] = PR_fanotify_init, + [ 301 ] = PR_fanotify_mark, + [ 302 ] = PR_prlimit64, + [ 303 ] = PR_name_to_handle_at, + [ 304 ] = PR_open_by_handle_at, + [ 305 ] = PR_clock_adjtime, + [ 306 ] = PR_syncfs, + [ 307 ] = PR_sendmmsg, + [ 308 ] = PR_setns, + [ 309 ] = PR_getcpu, + [ 310 ] = PR_process_vm_readv, + [ 311 ] = PR_process_vm_writev, + [ 312 ] = PR_kcmp, + [ 313 ] = PR_finit_module, + [ 314 ] = PR_sched_setattr, + [ 315 ] = PR_sched_getattr, + [ 316 ] = PR_renameat2, +};
diff --git a/5.1.0/src/syscall/sysnums.list b/5.1.0/src/syscall/sysnums.list new file mode 100644 index 0000000..7bdc731 --- /dev/null +++ b/5.1.0/src/syscall/sysnums.list
@@ -0,0 +1,430 @@ +SYSNUM(ARM_BASE) +SYSNUM(ARM_breakpoint) +SYSNUM(ARM_cacheflush) +SYSNUM(ARM_set_tls) +SYSNUM(ARM_usr26) +SYSNUM(ARM_usr32) +SYSNUM(X32_SYSCALL_BIT) +SYSNUM(_llseek) +SYSNUM(_newselect) +SYSNUM(_sysctl) +SYSNUM(accept) +SYSNUM(accept4) +SYSNUM(access) +SYSNUM(acct) +SYSNUM(add_key) +SYSNUM(adjtimex) +SYSNUM(afs_syscall) +SYSNUM(alarm) +SYSNUM(arch_prctl) +SYSNUM(arch_specific_syscall) +SYSNUM(arm_fadvise64_64) +SYSNUM(arm_sync_file_range) +SYSNUM(bdflush) +SYSNUM(bind) +SYSNUM(break) +SYSNUM(brk) +SYSNUM(cacheflush) +SYSNUM(capget) +SYSNUM(capset) +SYSNUM(chdir) +SYSNUM(chmod) +SYSNUM(chown) +SYSNUM(chown32) +SYSNUM(chroot) +SYSNUM(clock_adjtime) +SYSNUM(clock_getres) +SYSNUM(clock_gettime) +SYSNUM(clock_nanosleep) +SYSNUM(clock_settime) +SYSNUM(clone) +SYSNUM(close) +SYSNUM(connect) +SYSNUM(creat) +SYSNUM(create_module) +SYSNUM(delete_module) +SYSNUM(dup) +SYSNUM(dup2) +SYSNUM(dup3) +SYSNUM(epoll_create) +SYSNUM(epoll_create1) +SYSNUM(epoll_ctl) +SYSNUM(epoll_ctl_old) +SYSNUM(epoll_pwait) +SYSNUM(epoll_wait) +SYSNUM(epoll_wait_old) +SYSNUM(eventfd) +SYSNUM(eventfd2) +SYSNUM(execve) +SYSNUM(exit) +SYSNUM(exit_group) +SYSNUM(faccessat) +SYSNUM(fadvise64) +SYSNUM(fadvise64_64) +SYSNUM(fallocate) +SYSNUM(fanotify_init) +SYSNUM(fanotify_mark) +SYSNUM(fchdir) +SYSNUM(fchmod) +SYSNUM(fchmodat) +SYSNUM(fchown) +SYSNUM(fchown32) +SYSNUM(fchownat) +SYSNUM(fcntl) +SYSNUM(fcntl64) +SYSNUM(fdatasync) +SYSNUM(fgetxattr) +SYSNUM(finit_module) +SYSNUM(flistxattr) +SYSNUM(flock) +SYSNUM(fork) +SYSNUM(fremovexattr) +SYSNUM(fsetxattr) +SYSNUM(fstat) +SYSNUM(fstat64) +SYSNUM(fstatat64) +SYSNUM(fstatfs) +SYSNUM(fstatfs64) +SYSNUM(fsync) +SYSNUM(ftime) +SYSNUM(ftruncate) +SYSNUM(ftruncate64) +SYSNUM(futex) +SYSNUM(futimesat) +SYSNUM(get_kernel_syms) +SYSNUM(get_mempolicy) +SYSNUM(get_robust_list) +SYSNUM(get_thread_area) +SYSNUM(getcpu) +SYSNUM(getcwd) +SYSNUM(getdents) +SYSNUM(getdents64) +SYSNUM(getegid) +SYSNUM(getegid32) +SYSNUM(geteuid) +SYSNUM(geteuid32) +SYSNUM(getgid) +SYSNUM(getgid32) +SYSNUM(getgroups) +SYSNUM(getgroups32) +SYSNUM(getitimer) +SYSNUM(getpeername) +SYSNUM(getpgid) +SYSNUM(getpgrp) +SYSNUM(getpid) +SYSNUM(getpmsg) +SYSNUM(getppid) +SYSNUM(getpriority) +SYSNUM(getresgid) +SYSNUM(getresgid32) +SYSNUM(getresuid) +SYSNUM(getresuid32) +SYSNUM(getrlimit) +SYSNUM(getrusage) +SYSNUM(getsid) +SYSNUM(getsockname) +SYSNUM(getsockopt) +SYSNUM(gettid) +SYSNUM(gettimeofday) +SYSNUM(getuid) +SYSNUM(getuid32) +SYSNUM(getxattr) +SYSNUM(gtty) +SYSNUM(idle) +SYSNUM(init_module) +SYSNUM(inotify_add_watch) +SYSNUM(inotify_init) +SYSNUM(inotify_init1) +SYSNUM(inotify_rm_watch) +SYSNUM(io_cancel) +SYSNUM(io_destroy) +SYSNUM(io_getevents) +SYSNUM(io_setup) +SYSNUM(io_submit) +SYSNUM(ioctl) +SYSNUM(ioperm) +SYSNUM(iopl) +SYSNUM(ioprio_get) +SYSNUM(ioprio_set) +SYSNUM(ipc) +SYSNUM(kcmp) +SYSNUM(kexec_load) +SYSNUM(keyctl) +SYSNUM(kill) +SYSNUM(lchown) +SYSNUM(lchown32) +SYSNUM(lgetxattr) +SYSNUM(link) +SYSNUM(linkat) +SYSNUM(listen) +SYSNUM(listxattr) +SYSNUM(llistxattr) +SYSNUM(lock) +SYSNUM(lookup_dcookie) +SYSNUM(lremovexattr) +SYSNUM(lseek) +SYSNUM(lsetxattr) +SYSNUM(lstat) +SYSNUM(lstat64) +SYSNUM(madvise) +SYSNUM(mbind) +SYSNUM(migrate_pages) +SYSNUM(mincore) +SYSNUM(mkdir) +SYSNUM(mkdirat) +SYSNUM(mknod) +SYSNUM(mknodat) +SYSNUM(mlock) +SYSNUM(mlockall) +SYSNUM(mmap) +SYSNUM(mmap2) +SYSNUM(modify_ldt) +SYSNUM(mount) +SYSNUM(move_pages) +SYSNUM(mprotect) +SYSNUM(mpx) +SYSNUM(mq_getsetattr) +SYSNUM(mq_notify) +SYSNUM(mq_open) +SYSNUM(mq_timedreceive) +SYSNUM(mq_timedsend) +SYSNUM(mq_unlink) +SYSNUM(mremap) +SYSNUM(msgctl) +SYSNUM(msgget) +SYSNUM(msgrcv) +SYSNUM(msgsnd) +SYSNUM(msync) +SYSNUM(munlock) +SYSNUM(munlockall) +SYSNUM(munmap) +SYSNUM(name_to_handle_at) +SYSNUM(nanosleep) +SYSNUM(newfstatat) +SYSNUM(nfsservctl) +SYSNUM(nice) +SYSNUM(oldfstat) +SYSNUM(oldlstat) +SYSNUM(oldolduname) +SYSNUM(oldstat) +SYSNUM(olduname) +SYSNUM(open) +SYSNUM(open_by_handle_at) +SYSNUM(openat) +SYSNUM(pause) +SYSNUM(pciconfig_iobase) +SYSNUM(pciconfig_read) +SYSNUM(pciconfig_write) +SYSNUM(perf_event_open) +SYSNUM(personality) +SYSNUM(pipe) +SYSNUM(pipe2) +SYSNUM(pivot_root) +SYSNUM(poll) +SYSNUM(ppoll) +SYSNUM(prctl) +SYSNUM(pread64) +SYSNUM(preadv) +SYSNUM(prlimit64) +SYSNUM(process_vm_readv) +SYSNUM(process_vm_writev) +SYSNUM(prof) +SYSNUM(profil) +SYSNUM(pselect6) +SYSNUM(ptrace) +SYSNUM(putpmsg) +SYSNUM(pwrite64) +SYSNUM(pwritev) +SYSNUM(query_module) +SYSNUM(quotactl) +SYSNUM(read) +SYSNUM(readahead) +SYSNUM(readdir) +SYSNUM(readlink) +SYSNUM(readlinkat) +SYSNUM(readv) +SYSNUM(reboot) +SYSNUM(recv) +SYSNUM(recvfrom) +SYSNUM(recvmmsg) +SYSNUM(recvmsg) +SYSNUM(remap_file_pages) +SYSNUM(removexattr) +SYSNUM(rename) +SYSNUM(renameat) +SYSNUM(renameat2) +SYSNUM(request_key) +SYSNUM(restart_syscall) +SYSNUM(rmdir) +SYSNUM(rt_sigaction) +SYSNUM(rt_sigpending) +SYSNUM(rt_sigprocmask) +SYSNUM(rt_sigqueueinfo) +SYSNUM(rt_sigreturn) +SYSNUM(rt_sigsuspend) +SYSNUM(rt_sigtimedwait) +SYSNUM(rt_tgsigqueueinfo) +SYSNUM(sched_get_priority_max) +SYSNUM(sched_get_priority_min) +SYSNUM(sched_getaffinity) +SYSNUM(sched_getattr) +SYSNUM(sched_getparam) +SYSNUM(sched_getscheduler) +SYSNUM(sched_rr_get_interval) +SYSNUM(sched_setaffinity) +SYSNUM(sched_setattr) +SYSNUM(sched_setparam) +SYSNUM(sched_setscheduler) +SYSNUM(sched_yield) +SYSNUM(security) +SYSNUM(select) +SYSNUM(semctl) +SYSNUM(semget) +SYSNUM(semop) +SYSNUM(semtimedop) +SYSNUM(send) +SYSNUM(sendfile) +SYSNUM(sendfile64) +SYSNUM(sendmmsg) +SYSNUM(sendmsg) +SYSNUM(sendto) +SYSNUM(set_mempolicy) +SYSNUM(set_robust_list) +SYSNUM(set_thread_area) +SYSNUM(set_tid_address) +SYSNUM(setdomainname) +SYSNUM(setfsgid) +SYSNUM(setfsgid32) +SYSNUM(setfsuid) +SYSNUM(setfsuid32) +SYSNUM(setgid) +SYSNUM(setgid32) +SYSNUM(setgroups) +SYSNUM(setgroups32) +SYSNUM(sethostname) +SYSNUM(setitimer) +SYSNUM(setns) +SYSNUM(setpgid) +SYSNUM(setpriority) +SYSNUM(setregid) +SYSNUM(setregid32) +SYSNUM(setresgid) +SYSNUM(setresgid32) +SYSNUM(setresuid) +SYSNUM(setresuid32) +SYSNUM(setreuid) +SYSNUM(setreuid32) +SYSNUM(setrlimit) +SYSNUM(setsid) +SYSNUM(setsockopt) +SYSNUM(settimeofday) +SYSNUM(setuid) +SYSNUM(setuid32) +SYSNUM(setxattr) +SYSNUM(sgetmask) +SYSNUM(shmat) +SYSNUM(shmctl) +SYSNUM(shmdt) +SYSNUM(shmget) +SYSNUM(shutdown) +SYSNUM(sigaction) +SYSNUM(sigaltstack) +SYSNUM(signal) +SYSNUM(signalfd) +SYSNUM(signalfd4) +SYSNUM(sigpending) +SYSNUM(sigprocmask) +SYSNUM(sigreturn) +SYSNUM(sigsuspend) +SYSNUM(socket) +SYSNUM(socketcall) +SYSNUM(socketpair) +SYSNUM(splice) +SYSNUM(ssetmask) +SYSNUM(stat) +SYSNUM(stat64) +SYSNUM(statfs) +SYSNUM(statfs64) +SYSNUM(stime) +SYSNUM(stty) +SYSNUM(swapoff) +SYSNUM(swapon) +SYSNUM(symlink) +SYSNUM(symlinkat) +SYSNUM(sync) +SYSNUM(sync_file_range) +SYSNUM(sync_file_range2) +SYSNUM(syncfs) +SYSNUM(sysfs) +SYSNUM(sysinfo) +SYSNUM(syslog) +SYSNUM(tee) +SYSNUM(tgkill) +SYSNUM(time) +SYSNUM(timer_create) +SYSNUM(timer_delete) +SYSNUM(timer_getoverrun) +SYSNUM(timer_gettime) +SYSNUM(timer_settime) +SYSNUM(timerfd_create) +SYSNUM(timerfd_gettime) +SYSNUM(timerfd_settime) +SYSNUM(times) +SYSNUM(tkill) +SYSNUM(truncate) +SYSNUM(truncate64) +SYSNUM(tuxcall) +SYSNUM(ugetrlimit) +SYSNUM(ulimit) +SYSNUM(umask) +SYSNUM(umount) +SYSNUM(umount2) +SYSNUM(uname) +SYSNUM(unlink) +SYSNUM(unlinkat) +SYSNUM(unshare) +SYSNUM(uselib) +SYSNUM(ustat) +SYSNUM(utime) +SYSNUM(utimensat) +SYSNUM(utimes) +SYSNUM(vfork) +SYSNUM(vhangup) +SYSNUM(vm86) +SYSNUM(vm86old) +SYSNUM(vmsplice) +SYSNUM(vserver) +SYSNUM(wait4) +SYSNUM(waitid) +SYSNUM(waitpid) +SYSNUM(write) +SYSNUM(writev) +SYSNUM(x32_execve) +SYSNUM(x32_get_robust_list) +SYSNUM(x32_ioctl) +SYSNUM(x32_kexec_load) +SYSNUM(x32_move_pages) +SYSNUM(x32_mq_notify) +SYSNUM(x32_preadv) +SYSNUM(x32_process_vm_readv) +SYSNUM(x32_process_vm_writev) +SYSNUM(x32_ptrace) +SYSNUM(x32_pwritev) +SYSNUM(x32_readv) +SYSNUM(x32_recvfrom) +SYSNUM(x32_recvmmsg) +SYSNUM(x32_recvmsg) +SYSNUM(x32_rt_sigaction) +SYSNUM(x32_rt_sigpending) +SYSNUM(x32_rt_sigqueueinfo) +SYSNUM(x32_rt_sigreturn) +SYSNUM(x32_rt_sigtimedwait) +SYSNUM(x32_rt_tgsigqueueinfo) +SYSNUM(x32_sendmmsg) +SYSNUM(x32_sendmsg) +SYSNUM(x32_set_robust_list) +SYSNUM(x32_sigaltstack) +SYSNUM(x32_timer_create) +SYSNUM(x32_vmsplice) +SYSNUM(x32_waitid) +SYSNUM(x32_writev)
diff --git a/5.1.0/src/tracee/abi.h b/5.1.0/src/tracee/abi.h new file mode 100644 index 0000000..51191e9 --- /dev/null +++ b/5.1.0/src/tracee/abi.h
@@ -0,0 +1,131 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdbool.h> +#include <stddef.h> /* offsetof(), */ + +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "arch.h" + +#include "attribute.h" + +typedef enum { + ABI_DEFAULT = 0, + ABI_2, /* x86_32 on x86_64. */ + ABI_3, /* x32 on x86_64. */ + NB_MAX_ABIS, +} 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; + } +} + +/** + * Return true if @tracee is a 32-bit process running on a 64-bit + * kernel. + */ +static inline bool is_32on64_mode(const Tracee *tracee) +{ + /* Unlike the ABI, 32-bit/64-bit mode change is effective + * immediately, hence _regs[CURRENT].cs. */ + switch (tracee->_regs[CURRENT].cs) { + case 0x23: + return true; + + case 0x33: + if (tracee->_regs[CURRENT].ds == 0x2B) + return true; + /* Fall through. */ + default: + return false; + } +} +#else +static inline Abi get_abi(const Tracee *tracee UNUSED) +{ + return ABI_DEFAULT; +} + +static inline bool is_32on64_mode(const Tracee *tracee UNUSED) +{ + return false; +} +#endif + +/** + * 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 <sys/stat.h> + +/** + * 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 */
diff --git a/5.1.0/src/tracee/event.c b/5.1.0/src/tracee/event.c new file mode 100644 index 0000000..bafc3c5 --- /dev/null +++ b/5.1.0/src/tracee/event.c
@@ -0,0 +1,602 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sched.h> /* CLONE_*, */ +#include <sys/types.h> /* pid_t, */ +#include <sys/ptrace.h> /* ptrace(1), PTRACE_*, */ +#include <sys/types.h> /* waitpid(2), */ +#include <sys/wait.h> /* waitpid(2), */ +#include <sys/utsname.h> /* uname(2), */ +#include <unistd.h> /* fork(2), chdir(2), getpid(2), */ +#include <string.h> /* strcmp(3), */ +#include <errno.h> /* errno(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <assert.h> /* assert(3), */ +#include <stdlib.h> /* atexit(3), getenv(3), */ +#include <talloc.h> /* talloc_*, */ + +#include "tracee/event.h" +#include "cli/note.h" +#include "path/path.h" +#include "path/binding.h" +#include "syscall/syscall.h" +#include "syscall/seccomp.h" +#include "ptrace/wait.h" +#include "extension/extension.h" +#include "execve/elf.h" + +#include "attribute.h" +#include "compat.h" + +/** + * Start @tracee->exe with the given @argv[]. This function + * returns -errno if an error occurred, otherwise 0. + */ +int launch_process(Tracee *tracee, char *const argv[]) +{ + char *const default_argv[] = { "-sh", NULL }; + long status; + pid_t pid; + + /* Warn about open file descriptors. They won't be + * translated until they are closed. */ + if (tracee->verbose > 0) + list_open_fd(tracee); + + pid = fork(); + switch(pid) { + case -1: + note(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) { + note(tracee, ERROR, SYSTEM, "ptrace(TRACEME)"); + return -errno; + } + + /* 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); + + /* Improve performance by using seccomp mode 2, unless + * this support is explicitly disabled. */ + if (getenv("PROOT_NO_SECCOMP") == NULL) + (void) enable_syscall_filtering(tracee); + + /* 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, argv[0] != NULL ? argv : default_argv); + 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) +{ + note(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); +} + +/** + * Helper for print_talloc_hierarchy(). + */ +static 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, parent = %p)", + ((Tracee *)ptr)->pid, ((Tracee *)ptr)->parent); + } + 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"); +} + +/* Print on stderr the complete talloc hierarchy. */ +static void print_talloc_hierarchy(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED) +{ + 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; + } +} + +static int last_exit_status = -1; + +/** + * Check if this instance of PRoot can *technically* handle @tracee. + */ +static void check_architecture(Tracee *tracee) +{ + struct utsname utsname; + ElfHeader elf_header; + char path[PATH_MAX]; + int status; + + if (tracee->exe == NULL) + return; + + status = translate_path(tracee, path, AT_FDCWD, tracee->exe, false); + if (status < 0) + return; + + status = open_elf(path, &elf_header); + if (status < 0) + return; + close(status); + + if (!IS_CLASS64(elf_header) || sizeof(word_t) == sizeof(uint64_t)) + return; + + note(tracee, ERROR, USER, + "'%s' is a 64-bit program whereas this version of " + "%s handles 32-bit programs only", path, tracee->tool_name); + + status = uname(&utsname); + if (status < 0) + return; + + if (strcmp(utsname.machine, "x86_64") != 0) + return; + + note(tracee, INFO, USER, + "Get a 64-bit version that supports 32-bit binaries here: " + "http://static.proot.me/proot-x86_64"); +} + +/** + * Wait then handle any event from any tracee. This function returns + * the exit status of the last terminated program. + */ +int event_loop() +{ + struct sigaction signal_action; + long status; + int signum; + + /* Kill all tracees when exiting. */ + status = atexit(kill_all_tracees); + if (status != 0) + note(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) + note(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) + note(NULL, WARNING, SYSTEM, "sigaction(%d)", signum); + } + + while (1) { + int tracee_status; + Tracee *tracee; + int signal; + pid_t pid; + + /* This is the only safe place to free tracees. */ + free_terminated_tracees(); + + /* Wait for the next tracee's stop. */ + pid = waitpid(-1, &tracee_status, __WALL); + if (pid < 0) { + if (errno != ECHILD) { + note(NULL, ERROR, SYSTEM, "waitpid()"); + return EXIT_FAILURE; + } + break; + } + + /* Get information about this tracee. */ + tracee = get_tracee(NULL, pid, true); + assert(tracee != NULL); + + tracee->running = false; + + status = notify_extensions(tracee, NEW_STATUS, tracee_status, 0); + if (status != 0) + continue; + + if (tracee->as_ptracee.ptracer != NULL) { + bool keep_stopped = handle_ptracee_event(tracee, tracee_status); + if (keep_stopped) + continue; + } + + signal = handle_tracee_event(tracee, tracee_status); + (void) restart_tracee(tracee, signal); + } + + return last_exit_status; +} + +/** + * Handle the current event (@tracee_status) of the given @tracee. + * This function returns the "computed" signal that should be used to + * restart the given @tracee. + */ +int handle_tracee_event(Tracee *tracee, int tracee_status) +{ + static bool seccomp_detected = false; + pid_t pid = tracee->pid; + long status; + int signal; + + /* Don't overwrite restart_how if it is explicitly set + * elsewhere, i.e in the ptrace emulation when single + * stepping. */ + if (tracee->restart_how == 0) { + /* When seccomp is enabled, all events are restarted in + * non-stop mode, but this default choice could be overwritten + * later if necessary. The check against "sysexit_pending" + * ensures PTRACE_SYSCALL (used to hit the exit stage under + * seccomp) is not cleared due to an event that would happen + * before the exit stage, eg. PTRACE_EVENT_EXEC for the exit + * stage of execve(2). */ + if (tracee->seccomp == ENABLED && !tracee->sysexit_pending) + tracee->restart_how = PTRACE_CONT; + else + tracee->restart_how = PTRACE_SYSCALL; + } + + /* Not a signal-stop by default. */ + signal = 0; + + if (WIFEXITED(tracee_status)) { + last_exit_status = WEXITSTATUS(tracee_status); + VERBOSE(tracee, 1, "pid %d: exited with status %d", pid, last_exit_status); + tracee->terminated = true; + } + else if (WIFSIGNALED(tracee_status)) { + check_architecture(tracee); + VERBOSE(tracee, (int) (last_exit_status != -1), + "pid %d: terminated with signal %d", pid, WTERMSIG(tracee_status)); + tracee->terminated = true; + } + 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) { + note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)"); + exit(EXIT_FAILURE); + } + } + } + /* Fall through. */ + case SIGTRAP | 0x80: + signal = 0; + + /* This tracee got signaled then freed during the + sysenter stage but the kernel reports the sysexit + stage; just discard this spurious tracee/event. */ + if (tracee->exe == NULL) { + tracee->restart_how = PTRACE_CONT; + return 0; + } + + switch (tracee->seccomp) { + case ENABLED: + if (IS_IN_SYSENTER(tracee)) { + /* sysenter: ensure the sysexit + * stage will be hit under seccomp. */ + tracee->restart_how = PTRACE_SYSCALL; + tracee->sysexit_pending = true; + } + else { + /* sysexit: the next sysenter + * will be notified by seccomp. */ + tracee->restart_how = PTRACE_CONT; + tracee->sysexit_pending = false; + } + /* Fall through. */ + case DISABLED: + translate_syscall(tracee); + + /* This syscall has disabled seccomp. */ + if (tracee->seccomp == DISABLING) { + tracee->restart_how = PTRACE_SYSCALL; + tracee->seccomp = DISABLED; + } + + break; + + case DISABLING: + /* Seccomp was disabled by the + * previous syscall, but its sysenter + * stage was already handled. */ + tracee->seccomp = DISABLED; + if (IS_IN_SYSENTER(tracee)) + tracee->status = 1; + break; + } + break; + + case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: + case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: { + unsigned long flags = 0; + + signal = 0; + + if (!seccomp_detected) { + VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2) enabled"); + tracee->seccomp = ENABLED; + seccomp_detected = true; + } + + /* Use the common ptrace flow if seccomp was + * explicitely disabled for this tracee. */ + if (tracee->seccomp != ENABLED) + break; + + status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags); + if (status < 0) + break; + + /* Use the common ptrace flow when + * sysexit has to be handled. */ + if ((flags & FILTER_SYSEXIT) != 0) { + tracee->restart_how = PTRACE_SYSCALL; + break; + } + + /* Otherwise, handle the sysenter + * stage right now. */ + tracee->restart_how = PTRACE_CONT; + translate_syscall(tracee); + + /* This syscall has disabled seccomp, so move + * the ptrace flow back to the common path to + * ensure its sysexit will be handled. */ + if (tracee->seccomp == DISABLING) + tracee->restart_how = PTRACE_SYSCALL; + break; + } + + case SIGTRAP | PTRACE_EVENT_VFORK << 8: + signal = 0; + (void) new_child(tracee, CLONE_VFORK); + break; + + case SIGTRAP | PTRACE_EVENT_FORK << 8: + case SIGTRAP | PTRACE_EVENT_CLONE << 8: + signal = 0; + (void) new_child(tracee, 0); + 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; + } + } + + /* Clear the pending event, if any. */ + tracee->as_ptracee.event4.proot.pending = false; + + return signal; +} + +/** + * Restart the given @tracee with the specified @signal. This + * function returns false if the tracee was not restarted (error or + * put in the "waiting for ptracee" state), otherwise true. + */ +bool restart_tracee(Tracee *tracee, int signal) +{ + int status; + + /* Put in the "stopped"/"waiting for ptracee" state?. */ + if (tracee->as_ptracer.wait_pid != 0 || signal == -1) + return false; + + /* Restart the tracee and stop it at the next instruction, or + * at the next entry or exit of a system call. */ + status = ptrace(tracee->restart_how, tracee->pid, NULL, signal); + if (status < 0) + return false; /* The process likely died in a syscall. */ + + tracee->restart_how = 0; + tracee->running = true; + + return true; +}
diff --git a/5.1.0/src/tracee/event.h b/5.1.0/src/tracee/event.h new file mode 100644 index 0000000..3ff3602 --- /dev/null +++ b/5.1.0/src/tracee/event.h
@@ -0,0 +1,35 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <stdbool.h> + +#include "tracee/tracee.h" + +extern int launch_process(Tracee *tracee, char *const argv[]); +extern int event_loop(); +extern int handle_tracee_event(Tracee *tracee, int tracee_status); +extern bool restart_tracee(Tracee *tracee, int signal); + +#endif /* TRACEE_EVENT_H */
diff --git a/5.1.0/src/tracee/mem.c b/5.1.0/src/tracee/mem.c new file mode 100644 index 0000000..6f6c1bd --- /dev/null +++ b/5.1.0/src/tracee/mem.c
@@ -0,0 +1,570 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/ptrace.h> /* ptrace(2), PTRACE_*, */ +#include <sys/types.h> /* pid_t, size_t, */ +#include <stdlib.h> /* NULL, */ +#include <stddef.h> /* offsetof(), */ +#include <sys/user.h> /* struct user*, */ +#include <errno.h> /* errno, */ +#include <assert.h> /* assert(3), */ +#include <sys/wait.h> /* waitpid(2), */ +#include <string.h> /* memcpy(3), */ +#include <stdint.h> /* uint*_t, */ +#include <sys/uio.h> /* process_vm_*, struct iovec, */ +#include <unistd.h> /* sysconf(3), */ +#include <sys/mman.h> /* mmap(2), munmap(2), MAP_*, */ + +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "syscall/heap.h" +#include "arch.h" /* word_t, NO_MISALIGNED_ACCESS */ +#include "build.h" /* HAVE_PROCESS_VM, */ +#include "cli/note.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 (belongs_to_heap_prealloc(tracee, dest_tracee)) + return -EFAULT; + +#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) { + note(tracee, WARNING, SYSTEM, "ptrace(POKEDATA)"); + return -EFAULT; + } + } + + if (nb_trailing_bytes == 0) + return 0; + + /* 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) { + note(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) { + note(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 (belongs_to_heap_prealloc(tracee, dest_tracee)) + return -EFAULT; + +#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 (belongs_to_heap_prealloc(tracee, src_tracee)) + return -EFAULT; + +#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) { + note(tracee, WARNING, SYSTEM, "ptrace(PEEKDATA)"); + return -EFAULT; + } + store_word(&dest[i], word); + } + + if (nb_trailing_bytes == 0) + return 0; + + /* 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) { + note(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 (belongs_to_heap_prealloc(tracee, src_tracee)) + return -EFAULT; + +#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_word(const Tracee *tracee, word_t address) +{ + word_t result = 0; + + if (belongs_to_heap_prealloc(tracee, address)) { + errno = EFAULT; + return 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); + + /* From ptrace(2) manual: "Unfortunately, under Linux, + * different variations of this fault will return EIO or + * EFAULT more or less arbitrarily." */ + if (errno == EIO) + errno = EFAULT; + + /* 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_word(const Tracee *tracee, word_t address, word_t value) +{ + word_t tmp; + + if (belongs_to_heap_prealloc(tracee, address)) { + errno = EFAULT; + return; + } + +#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); + + /* From ptrace(2) manual: "Unfortunately, under Linux, + * different variations of this fault will return EIO or + * EFAULT more or less arbitrarily." */ + if (errno == EIO) + errno = EFAULT; + + 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; + + /* This function should be called in sysenter only since the + * stack pointer is systematically restored at the end of + * sysexit (except for execve, but in this case the stack + * pointer should be handled with care since it is used by the + * process to retrieve argc, argv, envp, and auxv). */ + assert(IS_IN_SYSENTER(tracee)); + + /* 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)) { + note(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; +} + +/** + * Clear @size bytes at the given @address in the @tracee's memory + * space. This function returns -errno if an error occured, otherwise + * 0. + */ +int clear_mem(const Tracee *tracee, word_t address, size_t size) +{ + int status; + void *zeros; + + if (belongs_to_heap_prealloc(tracee, address)) + return -EFAULT; + + zeros = mmap(NULL, size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (zeros == MAP_FAILED) + return -errno; + + status = write_data(tracee, address, zeros, size); + munmap(zeros, size); + return status; +}
diff --git a/5.1.0/src/tracee/mem.h b/5.1.0/src/tracee/mem.h new file mode 100644 index 0000000..01853ec --- /dev/null +++ b/5.1.0/src/tracee/mem.h
@@ -0,0 +1,112 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <limits.h> /* PATH_MAX, */ +#include <sys/types.h> /* pid_t, size_t, */ +#include <stdint.h> /* pid_t, size_t, */ +#include <sys/uio.h> /* struct iovec, */ +#include <errno.h> /* ENAMETOOLONG, */ + +#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_word(const Tracee *tracee, word_t address); +extern void poke_word(const Tracee *tracee, word_t address, word_t value); +extern word_t alloc_mem(Tracee *tracee, ssize_t size); +extern int clear_mem(const Tracee *tracee, word_t address, size_t size); + +/** + * Copy to @dest_tracer at most PATH_MAX bytes -- including the + * end-of-string terminator -- 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. + */ +static inline int read_path(const Tracee *tracee, char dest_tracer[PATH_MAX], word_t src_tracee) +{ + int status; + + status = read_string(tracee, dest_tracer, src_tracee, PATH_MAX); + if (status < 0) + return status; + if (status >= PATH_MAX) + return -ENAMETOOLONG; + + return status; +} + +/** + * Generate a function that returns the value of the @type at the + * given @address in the @tracee's memory space. The caller must test + * errno to check if an error occured. + */ +#define GENERATE_peek(type) \ +static inline type ## _t peek_ ## type(const Tracee *tracee, word_t address) \ +{ \ + type ## _t result; \ + errno = -read_data(tracee, &result, address, sizeof(type ## _t)); \ + return result; \ +} + +GENERATE_peek(uint8); +GENERATE_peek(uint16); +GENERATE_peek(uint32); +GENERATE_peek(uint64); + +GENERATE_peek(int8); +GENERATE_peek(int16); +GENERATE_peek(int32); +GENERATE_peek(int64); + +#undef GENERATE_peek + +/** + * Generate a function that set the @type 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. + */ +#define GENERATE_poke(type) \ +static inline void poke_ ## type(const Tracee *tracee, word_t address, type ## _t value) \ +{ \ + errno = -write_data(tracee, address, &value, sizeof(type ## _t)); \ +} + +GENERATE_poke(uint8); +GENERATE_poke(uint16); +GENERATE_poke(uint32); +GENERATE_poke(uint64); + +GENERATE_poke(int8); +GENERATE_poke(int16); +GENERATE_poke(int32); +GENERATE_poke(int64); + +#undef GENERATE_poke + +#endif /* TRACEE_MEM_H */
diff --git a/5.1.0/src/tracee/reg.c b/5.1.0/src/tracee/reg.c new file mode 100644 index 0000000..78dc5a5 --- /dev/null +++ b/5.1.0/src/tracee/reg.c
@@ -0,0 +1,327 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* off_t */ +#include <sys/user.h> /* struct user*, */ +#include <sys/ptrace.h> /* ptrace(2), PTRACE*, */ +#include <assert.h> /* assert(3), */ +#include <errno.h> /* errno(3), */ +#include <stddef.h> /* offsetof(), */ +#include <stdint.h> /* *int*_t(), */ +#include <limits.h> /* ULONG_MAX, */ +#include <string.h> /* memcpy(3), */ +#include <sys/uio.h> /* struct iovec, */ + +#include "arch.h" + +#if defined(ARCH_ARM64) +#include <linux/elf.h> /* NT_PRSTATUS */ +#endif + +#include "syscall/sysnum.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "cli/note.h" +#include "compat.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), + [INSTR_POINTER] = USER_REGS_OFFSET(rip), + [RTLD_FINI] = USER_REGS_OFFSET(rdx), + [STATE_FLAGS] = USER_REGS_OFFSET(eflags), + [USERARG_1] = USER_REGS_OFFSET(rdi), + }; + + 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), + [INSTR_POINTER] = USER_REGS_OFFSET(rip), + [RTLD_FINI] = USER_REGS_OFFSET(rdx), + [STATE_FLAGS] = USER_REGS_OFFSET(eflags), + [USERARG_1] = USER_REGS_OFFSET(rax), + }; + + #undef REG + #define REG(tracee, version, index) \ + (*(word_t*) (tracee->_regs[version].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]), + [INSTR_POINTER] = USER_REGS_OFFSET(uregs[15]), + [USERARG_1] = USER_REGS_OFFSET(uregs[0]), + }; + +#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), + [INSTR_POINTER] = USER_REGS_OFFSET(pc), + }; + +#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), + [INSTR_POINTER] = USER_REGS_OFFSET(eip), + [RTLD_FINI] = USER_REGS_OFFSET(edx), + [STATE_FLAGS] = USER_REGS_OFFSET(eflags), + [USERARG_1] = USER_REGS_OFFSET(eax), + }; + +#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]), + [INSTR_POINTER] = USER_REGS_OFFSET(pc), + [RTLD_FINI] = USER_REGS_OFFSET(r4), + }; + +#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; + + note(tracee, INFO, INTERNAL, + "pid %d: %s: %s(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx) = 0x%lx [0x%lx, %d]", + tracee->pid, message, + stringify_sysnum(get_sysnum(tracee, CURRENT)), + 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), + get_abi(tracee)); +} + +/** + * 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 +# if defined(ARCH_ARM_EABI) + /* On ARM, a special ptrace request is required to + * change effectively the syscall number during a + * ptrace-stop. */ + word_t current_sysnum = REG(tracee, CURRENT, SYSARG_NUM); + if (current_sysnum != REG(tracee, ORIGINAL, SYSARG_NUM)) { + status = ptrace(PTRACE_SET_SYSCALL, tracee->pid, 0, current_sysnum); + if (status < 0) + note(tracee, WARNING, SYSTEM, "can't set the syscall number"); + } +# endif + + status = ptrace(PTRACE_SETREGS, tracee->pid, NULL, &tracee->_regs[CURRENT]); +#endif + if (status < 0) + return status; + } + + return 0; +}
diff --git a/5.1.0/src/tracee/reg.h b/5.1.0/src/tracee/reg.h new file mode 100644 index 0000000..47f6d4e --- /dev/null +++ b/5.1.0/src/tracee/reg.h
@@ -0,0 +1,54 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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, + INSTR_POINTER, + RTLD_FINI, + STATE_FLAGS, + USERARG_1, +} 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 */
diff --git a/5.1.0/src/tracee/tracee.c b/5.1.0/src/tracee/tracee.c new file mode 100644 index 0000000..aa17294 --- /dev/null +++ b/5.1.0/src/tracee/tracee.c
@@ -0,0 +1,603 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sched.h> /* CLONE_*, */ +#include <sys/types.h> /* pid_t, size_t, */ +#include <stdlib.h> /* NULL, */ +#include <assert.h> /* assert(3), */ +#include <string.h> /* bzero(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <sys/queue.h> /* LIST_*, */ +#include <talloc.h> /* talloc_*, */ +#include <signal.h> /* kill(2), SIGKILL, */ +#include <sys/ptrace.h> /* ptrace(2), PTRACE_*, */ +#include <errno.h> /* E*, */ + +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "path/binding.h" +#include "syscall/sysnum.h" +#include "tracee/event.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "extension/extension.h" +#include "cli/note.h" + +#include "compat.h" + +typedef LIST_HEAD(tracees, tracee) Tracees; +static Tracees tracees; + + +/** + * Remove @zombie from its parent's list of zombies. Note: this is a + * talloc destructor. + */ +static int remove_zombie(Tracee *zombie) +{ + LIST_REMOVE(zombie, link); + return 0; +} + +/** + * Perform some specific treatments against @pointer according to its + * type, before it gets unlinked from @tracee_->life_context. + */ +static void clean_life_span_object(const void *pointer, int depth UNUSED, + int max_depth UNUSED, int is_ref UNUSED, void *tracee_) +{ + Binding *binding; + Tracee *tracee; + + tracee = talloc_get_type_abort(tracee_, Tracee); + + /* So far, only bindings need a special treatment. */ + binding = talloc_get_type(pointer, Binding); + if (binding != NULL) + remove_binding_from_all_lists(tracee, binding); +} + +/** + * Remove @tracee from the list of tracees and update all of its + * children & ptracees, and its ptracer. Note: this is a talloc + * destructor. + */ +static int remove_tracee(Tracee *tracee) +{ + Tracee *relative; + Tracee *ptracer; + int event; + + LIST_REMOVE(tracee, link); + + /* Clean objects that are linked to this tracee's life + * span. */ + talloc_report_depth_cb(tracee->life_context, 0, 100, clean_life_span_object, tracee); + + /* This could be optimize by using a dedicated list of + * children and ptracees. */ + LIST_FOREACH(relative, &tracees, link) { + /* Its children are now orphan. */ + if (relative->parent == tracee) + relative->parent = NULL; + + /* Its tracees are now free. */ + if (relative->as_ptracee.ptracer == tracee) { + /* Release the pending event, if any. */ + relative->as_ptracee.ptracer = NULL; + + if (relative->as_ptracee.event4.proot.pending) { + event = handle_tracee_event(relative, + relative->as_ptracee.event4.proot.value); + (void) restart_tracee(relative, event); + } + else if (relative->as_ptracee.event4.ptracer.pending) { + event = relative->as_ptracee.event4.proot.value; + (void) restart_tracee(relative, event); + } + + bzero(&relative->as_ptracee, sizeof(relative->as_ptracee)); + } + } + + /* Nothing else to do if it's not a ptracee. */ + ptracer = tracee->as_ptracee.ptracer; + if (ptracer == NULL) + return 0; + + /* Zombify this ptracee until its ptracer is notified about + * its death. */ + event = tracee->as_ptracee.event4.ptracer.value; + if (tracee->as_ptracee.event4.ptracer.pending + && (WIFEXITED(event) || WIFSIGNALED(event))) { + Tracee *zombie; + + zombie = new_dummy_tracee(ptracer); + if (zombie != NULL) { + LIST_INSERT_HEAD(&PTRACER.zombies, zombie, link); + talloc_set_destructor(zombie, remove_zombie); + + zombie->parent = tracee->parent; + zombie->clone = tracee->clone; + zombie->pid = tracee->pid; + + detach_from_ptracer(tracee); + attach_to_ptracer(zombie, ptracer); + + zombie->as_ptracee.event4.ptracer.pending = true; + zombie->as_ptracee.event4.ptracer.value = event; + zombie->as_ptracee.is_zombie = true; + + return 0; + } + /* Fallback to the common path. */ + } + + detach_from_ptracer(tracee); + + /* Wake its ptracer if there's nothing else to wait for. */ + if (PTRACER.nb_ptracees == 0 && PTRACER.wait_pid != 0) { + /* Update the return value of ptracer's wait(2). */ + poke_reg(ptracer, SYSARG_RESULT, -ECHILD); + + /* Don't forget to write its register cache back. */ + (void) push_regs(ptracer); + + PTRACER.wait_pid = 0; + (void) restart_tracee(ptracer, 0); + } + + return 0; +} + +/** + * Allocate a new entry for a dummy tracee (no pid, no destructor, not + * in the list of tracees, ...). The new allocated memory is attached + * to the given @context. This function returns NULL if an error + * occurred (ENOMEM), otherwise it returns the newly allocated + * structure. + */ +Tracee *new_dummy_tracee(TALLOC_CTX *context) +{ + Tracee *tracee; + + tracee = talloc_zero(context, Tracee); + if (tracee == NULL) + return NULL; + + /* 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 and heap. */ + tracee->fs = talloc_zero(tracee, FileSystemNameSpace); + tracee->heap = talloc_zero(tracee, Heap); + if (tracee->fs == NULL || tracee->heap == NULL) + goto no_mem; + + return tracee; + +no_mem: + TALLOC_FREE(tracee); + return NULL; +} + +/** + * Allocate a new entry for the tracee @pid, then set its destructor + * and add it to the list of tracees. This function returns NULL if + * an error occurred (ENOMEM), otherwise it returns the newly + * allocated structure. + */ +static Tracee *new_tracee(pid_t pid) +{ + Tracee *tracee; + + tracee = new_dummy_tracee(NULL); + if (tracee == NULL) + return NULL; + + talloc_set_destructor(tracee, remove_tracee); + + tracee->pid = pid; + LIST_INSERT_HEAD(&tracees, tracee, link); + + tracee->life_context = talloc_new(tracee); + + return tracee; +} + +/** + * Return the first [stopped?] tracee with the given + * @pid (-1 for any) which has the given @ptracer, and which has a + * pending event for its ptracer if @only_with_pevent is true. See + * wait(2) manual for the meaning of @wait_options. This function + * returns NULL if there's no such ptracee. + */ +static Tracee *get_ptracee(const Tracee *ptracer, pid_t pid, bool only_stopped, + bool only_with_pevent, word_t wait_options) +{ + Tracee *ptracee; + + /* Return zombies first. */ + LIST_FOREACH(ptracee, &PTRACER.zombies, link) { + /* Not the ptracee you're looking for? */ + if (pid != ptracee->pid && pid != -1) + continue; + + /* Not the expected kind of cloned process? */ + if (!EXPECTED_WAIT_CLONE(wait_options, ptracee)) + continue; + + return ptracee; + } + + LIST_FOREACH(ptracee, &tracees, link) { + /* Discard tracees that don't have this ptracer. */ + if (PTRACEE.ptracer != ptracer) + continue; + + /* Not the ptracee you're looking for? */ + if (pid != ptracee->pid && pid != -1) + continue; + + /* Not the expected kind of cloned process? */ + if (!EXPECTED_WAIT_CLONE(wait_options, ptracee)) + continue; + + /* No need to do more checks if its stopped state + * doesn't matter. Be careful when using such + * maybe-running tracee. */ + if (!only_stopped) + return ptracee; + + /* Is this tracee in the stopped state? */ + if (ptracee->running) + continue; + + /* Has a pending event for its ptracer? */ + if (PTRACEE.event4.ptracer.pending || !only_with_pevent) + return ptracee; + + /* No need to go further if the specific tracee isn't + * in the expected state? */ + if (pid == ptracee->pid) + return NULL; + } + + return NULL; +} + +/** + * Wrapper for get_ptracee(), this ensures only a stopped tracee is + * returned (or NULL). + */ +Tracee *get_stopped_ptracee(const Tracee *ptracer, pid_t pid, + bool only_with_pevent, word_t wait_options) +{ + return get_ptracee(ptracer, pid, true, only_with_pevent, wait_options); +} + +/** + * Wrapper for get_ptracee(), this ensures no running tracee is + * returned. + */ +bool has_ptracees(const Tracee *ptracer, pid_t pid, word_t wait_options) +{ + return (get_ptracee(ptracer, pid, false, false, wait_options) != 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); +} + +/** + * Free all tracees marked as terminated. + */ +void free_terminated_tracees() +{ + Tracee *next; + + /* Items can't be deleted when using LIST_FOREACH. */ + next = tracees.lh_first; + while (next != NULL) { + Tracee *tracee = next; + next = tracee->link.le_next; + + if (tracee->terminated) + TALLOC_FREE(tracee); + } +} + +/** + * 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) +{ + int ptrace_options; + unsigned long pid; + Tracee *child; + int status; + + /* If the tracee calls clone(2) with the CLONE_VFORK flag, + * PTRACE_EVENT_VFORK will be delivered instead [...]; + * otherwise if the tracee calls clone(2) with the exit signal + * set to SIGCHLD, PTRACE_EVENT_FORK will be delivered [...] + * + * -- ptrace(2) man-page + * + * That means we have to check if it's actually a clone(2) in + * order to get the right flags. + */ + status = fetch_regs(parent); + if (status >= 0 && get_sysnum(parent, CURRENT) == PR_clone) + clone_flags = peek_reg(parent, CURRENT, SYSARG_1); + + /* Get the pid of the parent's new child. */ + status = ptrace(PTRACE_GETEVENTMSG, parent->pid, NULL, &pid); + if (status < 0 || pid == 0) { + note(parent, WARNING, SYSTEM, "ptrace(GETEVENTMSG)"); + return status; + } + + child = get_tracee(parent, (pid_t) pid, true); + if (child == NULL) { + note(parent, WARNING, SYSTEM, "running out of memory"); + return -ENOMEM; + } + + /* Sanity checks. */ + assert(child != NULL + && child->exe == 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->parent == NULL + && child->as_ptracee.ptracer == NULL); + + child->verbose = parent->verbose; + child->seccomp = parent->seccomp; + child->sysexit_pending = parent->sysexit_pending; + + /* If CLONE_VM is set, the calling process and the child + * process run in the same memory space [...] any memory + * mapping or unmapping performed with mmap(2) or munmap(2) by + * the child or calling process also affects the other + * process. + * + * If CLONE_VM is not set, the child process runs in a + * separate copy of the memory space of the calling process at + * the time of clone(). Memory writes or file + * mappings/unmappings performed by one of the processes do + * not affect the other, as with fork(2). + * + * -- clone(2) man-page + */ + TALLOC_FREE(child->heap); + child->heap = ((clone_flags & CLONE_VM) != 0) + ? talloc_reference(child, parent->heap) + : talloc_memdup(child, parent->heap, sizeof(Heap)); + if (child->heap == NULL) + return -ENOMEM; + + /* If CLONE_PARENT is set, then the parent of the new child + * (as returned by getppid(2)) will be the same as that of the + * calling process. + * + * If CLONE_PARENT is not set, then (as with fork(2)) the + * child's parent is the calling process. + * + * -- clone(2) man-page + */ + if ((clone_flags & CLONE_PARENT) != 0) + child->parent = parent->parent; + else + child->parent = parent; + + /* Remember if this child belongs to the same thread group as + * its parent. This is currently useful for ptrace emulation + * only but it deserves to be extended to support execve(2) + * specificity (ie. when a thread calls execve(2), its pid + * gets replaced by the pid of its thread group leader). */ + child->clone = ((clone_flags & CLONE_THREAD) != 0); + + /* Depending on how the new process is created, it may be + * automatically traced by the parent's tracer. */ + ptrace_options = ( clone_flags == 0 ? PTRACE_O_TRACEFORK + : (clone_flags & 0xFF) == SIGCHLD ? PTRACE_O_TRACEFORK + : (clone_flags & CLONE_VFORK) != 0 ? PTRACE_O_TRACEVFORK + : PTRACE_O_TRACECLONE); + if (parent->as_ptracee.ptracer != NULL + && ( (ptrace_options & parent->as_ptracee.options) != 0 + || (clone_flags & CLONE_PTRACE) != 0)) { + attach_to_ptracer(child, parent->as_ptracee.ptracer); + + /* All these flags are inheritable, no matter why this + * child is being traced. */ + child->as_ptracee.options |= (parent->as_ptracee.options + & ( PTRACE_O_TRACECLONE + | PTRACE_O_TRACEEXEC + | PTRACE_O_TRACEEXIT + | PTRACE_O_TRACEFORK + | PTRACE_O_TRACESYSGOOD + | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEVFORKDONE)); + } + + /* 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 is unshared only once the child + * process does a call to execve(2). */ + child->exe = talloc_reference(child, parent->exe); + + child->qemu = talloc_reference(child, parent->qemu); + child->glue = talloc_reference(child, parent->glue); + + child->host_ldso_paths = talloc_reference(child, parent->host_ldso_paths); + child->guest_ldso_paths = talloc_reference(child, parent->guest_ldso_paths); + + child->tool_name = parent->tool_name; + + inherit_extensions(child, parent, clone_flags); + + /* Restart the child tracee if it was already alive but + * stopped until that moment. */ + if (child->sigstop == SIGSTOP_PENDING) { + bool keep_stopped = false; + + child->sigstop = SIGSTOP_ALLOWED; + + /* Notify its ptracer if it is ready to be traced. */ + if (child->as_ptracee.ptracer != NULL) { + /* Sanity check. */ + assert(!child->as_ptracee.tracing_started); + + keep_stopped = handle_ptracee_event(child, __W_STOPCODE(SIGSTOP)); + + /* Note that this event was already handled by + * PRoot since child->as_ptracee.ptracer was + * NULL up to now. */ + child->as_ptracee.event4.proot.pending = false; + child->as_ptracee.event4.proot.value = 0; + } + + if (!keep_stopped) + (void) restart_tracee(child, 0); + } + + return 0; +} + +/** + * Helper for swap_config(). + */ +static void reparent_config(Tracee *new_parent, Tracee *old_parent) +{ + new_parent->verbose = old_parent->verbose; + +#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(qemu); + REPARENT(glue); + REPARENT(extensions); + +#undef REPARENT +} + +/** + * 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; + + reparent_config(tmp, tracee1); + reparent_config(tracee1, tracee2); + reparent_config(tracee2, tmp); + + return 0; +} + +/* Send the KILL signal to all tracees. */ +void kill_all_tracees() +{ + Tracee *tracee; + + LIST_FOREACH(tracee, &tracees, link) + kill(tracee->pid, SIGKILL); +}
diff --git a/5.1.0/src/tracee/tracee.h b/5.1.0/src/tracee/tracee.h new file mode 100644 index 0000000..f4c3a23 --- /dev/null +++ b/5.1.0/src/tracee/tracee.h
@@ -0,0 +1,273 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2014 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 <sys/types.h> /* pid_t, size_t, */ +#include <sys/user.h> /* struct user*, */ +#include <stdbool.h> /* bool, */ +#include <sys/queue.h> /* LIST_*, */ +#include <sys/ptrace.h>/* enum __ptrace_request */ +#include <talloc.h> /* 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 load_info; +struct extensions; +struct chained_syscalls; + +/* 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; + +/* Virtual heap, emulated with a regular memory mapping. */ +typedef struct { + word_t base; + size_t size; + size_t prealloc_size; + bool disabled; +} Heap; + +/* 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; + + /* Is it currently running or not? */ + bool running; + + /* Is this tracee ready to be freed? TODO: move to a list + * dedicated to terminated tracees instead. */ + bool terminated; + + /* Parent of this tracee, NULL if none. */ + struct tracee *parent; + + /* Is it a "clone", i.e has the same parent as its creator. */ + bool clone; + + /* Support for ptrace emulation (tracer side). */ + struct { + size_t nb_ptracees; + LIST_HEAD(zombies, tracee) zombies; + + pid_t wait_pid; + word_t wait_options; + + enum { + DOESNT_WAIT = 0, + WAITS_IN_KERNEL, + WAITS_IN_PROOT + } waits_in; + } as_ptracer; + + /* Support for ptrace emulation (tracee side). */ + struct { + struct tracee *ptracer; + + struct { + #define STRUCT_EVENT struct { int value; bool pending; } + + STRUCT_EVENT proot; + STRUCT_EVENT ptracer; + } event4; + + bool tracing_started; + bool ignore_loader_syscalls; + bool ignore_syscalls; + word_t options; + bool is_zombie; + } as_ptracee; + + /* Current status: + * 0: enter syscall + * 1: exit syscall no error + * -errno: exit syscall with error. */ + int status; + +#define IS_IN_SYSENTER(tracee) ((tracee)->status == 0) +#define IS_IN_SYSEXIT(tracee) (!IS_IN_SYSENTER(tracee)) +#define IS_IN_SYSEXIT2(tracee, sysnum) (IS_IN_SYSEXIT(tracee) \ + && get_sysnum((tracee), ORIGINAL) == sysnum) + + /* 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; + + /* Context used to collect all dynamic memory allocations that + * should be released once this tracee is freed. */ + TALLOC_CTX *life_context; + + /* Note: I could rename "ctx" in "event_span" and + * "life_context" in "life_span". */ + + /* 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; + + /* Unrequested syscalls inserted by PRoot after an actual + * syscall. */ + struct { + struct chained_syscalls *syscalls; + bool force_final_result; + word_t final_result; + } chain; + + /* Load info generated during execve sysenter and used during + * execve sysexit. */ + struct load_info *load_info; + + + /********************************************************************** + * Private but inherited resources * + **********************************************************************/ + + /* Verbose level. */ + int verbose; + + /* State of the seccomp acceleration for this tracee. */ + enum { DISABLED = 0, DISABLING, ENABLED } seccomp; + + /* Ensure the sysexit stage is always hit under seccomp. */ + bool sysexit_pending; + + + /********************************************************************** + * Shared or private resources, depending on the CLONE_FS/VM flags. * + **********************************************************************/ + + /* Information related to a file-system name-space. */ + FileSystemNameSpace *fs; + + /* Virtual heap, emulated with a regular memory mapping. */ + Heap *heap; + + + /********************************************************************** + * Shared resources until the tracee makes a call to execve(). * + **********************************************************************/ + + /* Path to the executable, à la /proc/self/exe. */ + char *exe; + char *new_exe; + + + /********************************************************************** + * Shared or private resources, depending on the (re-)configuration * + **********************************************************************/ + + /* Runner command-line. */ + char **qemu; + + /* Path to glue between the guest rootfs and the host rootfs. */ + const char *glue; + + /* List of extensions enabled for this tracee. */ + struct extensions *extensions; + + + /********************************************************************** + * Shared but read-only resources * + **********************************************************************/ + + /* For the mixed-mode, the guest LD_LIBRARY_PATH is saved + * during the "guest -> host" transition, in order to be + * restored during the "host -> guest" transition (only if the + * host LD_LIBRARY_PATH hasn't changed). */ + const char *host_ldso_paths; + const char *guest_ldso_paths; + + /* For diagnostic purpose. */ + const char *tool_name; + +} 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 Tracee *get_stopped_ptracee(const Tracee *ptracer, pid_t pid, + bool only_with_pevent, word_t wait_options); +extern bool has_ptracees(const Tracee *ptracer, pid_t pid, word_t wait_options); +extern int new_child(Tracee *parent, word_t clone_flags); +extern Tracee *new_dummy_tracee(TALLOC_CTX *context); +extern void free_terminated_tracees(); +extern int swap_config(Tracee *tracee1, Tracee *tracee2); +extern void kill_all_tracees(); + +#endif /* TRACEE_H */
diff --git a/5.1.0/tests/GNUmakefile b/5.1.0/tests/GNUmakefile new file mode 100644 index 0000000..d0eae50 --- /dev/null +++ b/5.1.0/tests/GNUmakefile
@@ -0,0 +1,201 @@ +DIR = $(dir $(abspath $(firstword $(MAKEFILE_LIST)))) +ROOTFS = $(DIR)/rootfs +PROOT = $(DIR)/../src/proot +CARE = $(DIR)/../src/care +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 CARE="$(CARE)" PROOT_RAW="$(PROOT_RAW)" PROOT="$(PROOT)" ROOTFS=$(ROOTFS) sh -ex $< $(silently); $(call check,$*) + +check-%.c: $(ROOTFS)/bin/% setup + $(call check_c,$*,$(PROOT) -b /proc -r $(ROOTFS) /bin/$*) + +# Special cases. +check-test-bdc90417.c: test-bdc90417 + $(call check_c,$<,$(PROOT) -w . ./$<) + +# Not supported anymore. +# check-test-af062114.c: test-af062114 +# $(call check_c,$<,$(PROOT) -v -1 -q /bin/true -b . -b /lib -b /lib64 -b /proc -r $(ROOTFS) ./$< | grep -- --inhibit-rpath) +# $(call check_c,$<,$(PROOT) -v -1 -q /bin/true / ./$< | grep '^./$<') deprecated + +check-test-5bed7141.c: test-5bed7141 + $(call check_c,$<,$(PROOT) ./$<) + +check-test-5bed7143.c: test-5bed7143 + $(call check_c,$<,$(PROOT) -r $(ROOTFS) -b . ./$<) + $(call check_c,$<,$(PROOT) ./$<) + +check-test-16573e73.c: test-16573e73 + $(call check_c,$<,$(PROOT) ./$<) + $(call check_c,$<,$(PROOT) ./$< 1) + +check-test-82ba4ba1.c: test-82ba4ba1 + $(call check_c,$<,$(PROOT) -0 ./$<) + $(call check_c,$<,! $(PROOT) -i 123:456 ./$<) + +check-test-kkkkkkkk.c: test-kkkkkkkk + $(call check_c,$<,$(PROOT) ./$<) + +check-test-25069c12.c: test-25069c12 + $(call check_c,$<,$(PROOT) ./$<) + +check-test-25069c13.c: test-25069c13 + $(call check_c,$<,$(PROOT) ./$<) + +check-test-1ffc8309.c: test-1ffc8309 + $(call check_c,$<,env PROOT_FORCE_KOMPAT=1 $(PROOT) -k $(shell uname -r) ./$<) + +check-test-c5a7a0f0.c: test-c5a7a0f0 + $(call check_c,$<,$(PROOT) -0 ./$<) + $(call check_c,$<,! $(PROOT) -i 123:456 ./$<) + +check-test-a3e68988.c: test-a3e68988 + @which gdb >/dev/null 2>&1 || rm -f $< + $(call check_c,$<,$(PROOT) gdb -return-child-result -ex run -ex quit ./$<) + +check-test-c47aeb7d.c: test-c47aeb7d + @which gdb >/dev/null 2>&1 || rm -f $< + $(call check_c,$<,$(PROOT) gdb -return-child-result -ex run -ex quit ./$<) + +check-test-fdf487a0.c: test-fdf487a0 + $(call check_c,$<,echo test | $(PROOT) ./$<) + +check-test-iiiiiiii.c: test-iiiiiiii + $(call check_c,$<,echo test | env PROOT_DONT_POLLUTE_ROOTFS=1 $(PROOT) -b /bin:/this_shall_not_exist_outside_proot ./$<) + +check-test-9c07fad8.c: test-9c07fad8 + $(call check_c,$<,$(PROOT) ./$<) + +check-test-fa205b56.c: test-fa205b56 + $(call check_c,$<,$(PROOT) ./$<) + +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 + +ROOTFS_BIN = $(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)/bin/chdir_getcwd $(ROOTFS)/bin/fchdir_getcwd $(ROOTFS)/bin/argv \ + $(ROOTFS)/bin/fork-wait $(ROOTFS)/bin/ptrace $(ROOTFS)/bin/ptrace-2 \ + $(ROOTFS)/bin/puts_proc_self_exe $(ROOTFS)/bin/exec $(ROOTFS)/bin/exec-m32 + +ROOTFS_DIR = $(ROOTFS)/bin $(ROOTFS)/tmp + +$(ROOTFS_BIN): | $(ROOTFS_DIR) + +$(ROOTFS_DIR): + @mkdir -p $@ + +setup: $(ROOTFS_BIN) + +$(ROOTFS)/bin/abs-true: + @ln -fs /bin/true $@ + +$(ROOTFS)/bin/rel-true: + @ln -fs ./true $@ + +$(ROOTFS)/bin/exec-m32: exec.c + $(Q)$(CC) -m32 -static $^ -o $@ $(silently) || true + +.SECONDARY: $(patsubst %.c,$(ROOTFS)/bin/%, $(wildcard test-*.c)) +$(ROOTFS)/bin/%: %.c + $(Q)$(CC) -static $*.c -o $@ $(silently) || true + +# Special cases. +test-bdc90417: test-bdc90417.c + $(Q)$(CC) $< -o $@ $(silently) || true + +# Not supported anymore. +# 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 + +test-82ba4ba1: test-82ba4ba1.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-kkkkkkkk: test-kkkkkkkk.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-25069c12: test-25069c12.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-25069c13: test-25069c13.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-5bed7143: test-5bed7143.c + $(Q)$(CC) $< -static -o $@ $(silently) || true + +test-1ffc8309: test-1ffc8309.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-c5a7a0f0: test-c5a7a0f0.c + $(Q)$(CC) $< -pthread -static -o $@ $(silently) || true + +test-a3e68988: test-a3e68988.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-fdf487a0: test-fdf487a0.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-iiiiiiii: test-iiiiiiii.c + $(Q)$(CC) $< -o $@ $(silently) || true + +test-9c07fad8: test-9c07fad8.c + $(Q)$(CC) -fPIE -pie $< -o $@ $(silently) || true + +test-fa205b56: test-fa205b56.c + $(Q)$(CC) $< -pthread -o $@ $(silently) || true + +test-c47aeb7d: test-c47aeb7d.c + $(Q)$(CC) $< -pthread -o $@ $(silently) || true + +###################################################################### +# Beautified output + +V = 0 +ifeq ($(V), 0) + quiet = quiet_ + Q = @ + silently = >/dev/null 2>&1 +else + quiet = + Q = + silently = +endif
diff --git a/5.1.0/tests/argv.c b/5.1.0/tests/argv.c new file mode 100644 index 0000000..79920a6 --- /dev/null +++ b/5.1.0/tests/argv.c
@@ -0,0 +1,11 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 0; i < argc; i++) + printf("%s ", argv[i]); + + return 0; +}
diff --git a/5.1.0/tests/argv0.c b/5.1.0/tests/argv0.c new file mode 100644 index 0000000..cdb3af7 --- /dev/null +++ b/5.1.0/tests/argv0.c
@@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(int argc, char **argv) +{ + puts(argv[0]); + return 0; +}
diff --git a/5.1.0/tests/cat.c b/5.1.0/tests/cat.c new file mode 100644 index 0000000..fe341e0 --- /dev/null +++ b/5.1.0/tests/cat.c
@@ -0,0 +1,36 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +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); +}
diff --git a/5.1.0/tests/chdir_getcwd.c b/5.1.0/tests/chdir_getcwd.c new file mode 100644 index 0000000..26baa8a --- /dev/null +++ b/5.1.0/tests/chdir_getcwd.c
@@ -0,0 +1,34 @@ +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +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); +}
diff --git a/5.1.0/tests/echo.c b/5.1.0/tests/echo.c new file mode 100644 index 0000000..2a2ae6b --- /dev/null +++ b/5.1.0/tests/echo.c
@@ -0,0 +1,11 @@ +#include <stdio.h> + +int main(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) + puts(argv[i]); + + return 0; +}
diff --git a/5.1.0/tests/exec.c b/5.1.0/tests/exec.c new file mode 100644 index 0000000..370765a --- /dev/null +++ b/5.1.0/tests/exec.c
@@ -0,0 +1,16 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +int main(int argc, char *argv[], char *envp[]) +{ + if (argc < 2) { + puts("not enough parameter: filename argv[0] argv[1] ... argv[n]"); + exit(EXIT_FAILURE); + } + + execve(argv[1], &argv[2], envp); + perror("execve"); + + exit(EXIT_FAILURE); +}
diff --git a/5.1.0/tests/false.c b/5.1.0/tests/false.c new file mode 100644 index 0000000..98c444a --- /dev/null +++ b/5.1.0/tests/false.c
@@ -0,0 +1 @@ +int main(void) { return 1; }
diff --git a/5.1.0/tests/fchdir_getcwd.c b/5.1.0/tests/fchdir_getcwd.c new file mode 100644 index 0000000..4269b8f --- /dev/null +++ b/5.1.0/tests/fchdir_getcwd.c
@@ -0,0 +1,40 @@ +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +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); +}
diff --git a/5.1.0/tests/fork-wait.c b/5.1.0/tests/fork-wait.c new file mode 100644 index 0000000..5d64e68 --- /dev/null +++ b/5.1.0/tests/fork-wait.c
@@ -0,0 +1,26 @@ +#include <stdlib.h> /* exit(3), */ +#include <unistd.h> /* fork(2), sleep(3), */ +#include <sys/types.h> /* wait(2), */ +#include <sys/wait.h> /* wait(2), */ + +int main(int argc, char *argv[]) +{ + int child_status; + int status; + + switch (fork()) { + case -1: + exit(EXIT_FAILURE); + + case 0: /* Child */ + argc > 1 && sleep(2); + exit(EXIT_SUCCESS); + + default: /* Parent */ + argc <= 1 && sleep(2); + status = wait(&child_status); + if (status < 0 || !WIFEXITED(child_status)) + exit(EXIT_FAILURE); + exit(WEXITSTATUS(child_status)); + } +}
diff --git a/5.1.0/tests/ptrace-2.c b/5.1.0/tests/ptrace-2.c new file mode 100644 index 0000000..7bcf817 --- /dev/null +++ b/5.1.0/tests/ptrace-2.c
@@ -0,0 +1,407 @@ +/* -*- 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 <sys/types.h> /* pid_t, waitpid(2), */ +#include <sys/ptrace.h> /* ptrace(3), PTRACE_*, */ +#include <sys/user.h> /* struct user*, */ +#include <sys/wait.h> /* waitpid(2), */ +#include <stddef.h> /* offsetof(), */ +#include <unistd.h> /* fork(2), getpid(2), */ +#include <errno.h> /* errno(3), */ +#include <stdbool.h> /* bool, true, false, */ +#include <stdlib.h> /* exit, EXIT_*, */ +#include <stdio.h> /* fprintf(3), stderr, */ +#include <stdint.h> /* *int*_t, */ + +#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 + +/** + * 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(regs, index) \ + (*(unsigned long*) (((uint8_t *) ®s) + reg_offset[index])) + +typedef enum { + SYSARG_NUM = 0, + SYSARG_1, + SYSARG_2, + SYSARG_3, + SYSARG_4, + SYSARG_5, + SYSARG_6, + SYSARG_RESULT, + STACK_POINTER, + INSTR_POINTER, +} Reg; + +/* 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), + [INSTR_POINTER] = USER_REGS_OFFSET(rip), + }; + + 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), + [INSTR_POINTER] = USER_REGS_OFFSET(rip), + }; + + #undef REG + #define REG(regs, index) \ + (*(unsigned long*) (regs.cs == 0x23 \ + ? (((uint8_t *) ®s) + reg_offset_x86[index]) \ + : (((uint8_t *) ®s) + 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]), + [INSTR_POINTER] = USER_REGS_OFFSET(uregs[15]), + }; + +#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), + [INSTR_POINTER] = USER_REGS_OFFSET(pc), + }; + +#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), + [INSTR_POINTER] = USER_REGS_OFFSET(eip), + }; + +#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]), + [INSTR_POINTER] = USER_REGS_OFFSET(pc), + }; + +#else + + #error "Unsupported architecture" + +#endif + +int main(int argc, char *argv[]) +{ + enum __ptrace_request restart_how; + int last_exit_status = -1; + pid_t *pids = NULL; + long status; + int signal; + pid_t pid; + + if (argc <= 1) { + fprintf(stderr, "Usage: %s /path/to/exe [args]\n", argv[0]); + exit(EXIT_FAILURE); + } + + pid = fork(); + switch(pid) { + case -1: + perror("fork()"); + exit(EXIT_FAILURE); + + case 0: /* child */ + status = ptrace(PTRACE_TRACEME, 0, NULL, NULL); + if (status < 0) { + perror("ptrace(TRACEME)"); + exit(EXIT_FAILURE); + } + + /* Synchronize with the tracer's event loop. */ + kill(getpid(), SIGSTOP); + + execvp(argv[1], &argv[1]); + exit(EXIT_FAILURE); + + default: /* parent */ + break; + } + + restart_how = (getenv("PTRACER_BEHAVIOR_1") == NULL + ? PTRACE_SYSCALL + : PTRACE_CONT); + + pids = calloc(1, sizeof(pid_t)); + if (pids == NULL) { + perror("calloc()"); + exit(EXIT_FAILURE); + } + + signal = 0; + while (1) { + int tracee_status; + pid_t pid; + pid_t sid; + int i; + + /* Wait for the next tracee's stop. */ + pid = waitpid(-1, &tracee_status, __WALL); + if (pid < 0) { + perror("waitpid()"); + if (errno != ECHILD) + exit(EXIT_FAILURE); + break; + } + + sid = 0; + for (i = 0; pids[i] != 0; i++) { + if (pid == pids[i]) { + sid = i + 1; + break; + } + } + if (sid == 0) { + pids = realloc(pids, (i + 1 + 1) * sizeof(pid_t)); + if (pids == NULL) { + perror("realloc()"); + exit(EXIT_FAILURE); + } + pids[i + 1] = 0; + pids[i] = pid; + sid = i + 1; + fprintf(stderr, "sid %d -> pid %d\n", sid, pid); + } + + if (WIFEXITED(tracee_status)) { + fprintf(stderr, "sid %d: exited with status %d\n", + sid, WEXITSTATUS(tracee_status)); + last_exit_status = WEXITSTATUS(tracee_status); + continue; /* Skip the call to ptrace(SYSCALL). */ + } + else if (WIFSIGNALED(tracee_status)) { + fprintf(stderr, "sid %d: terminated with signal %d\n", + sid, WTERMSIG(tracee_status)); + continue; /* Skip the call to ptrace(SYSCALL). */ + } + else if (WIFCONTINUED(tracee_status)) { + fprintf(stderr, "sid %d: continued\n", sid); + signal = SIGCONT; + } + else if (WIFSTOPPED(tracee_status)) { + struct user_regs_struct regs; + + /* Don't use WSTOPSIG() to extract the signal + * since it clears the PTRACE_EVENT_* bits. */ + signal = (tracee_status & 0xfff00) >> 8; + + switch (signal) { + static bool skip_bare_sigtrap = false; + long ptrace_options; + + case SIGTRAP: + fprintf(stderr, "sid %d: SIGTRAP\n", sid); + + status = ptrace(PTRACE_GETREGS, pid, NULL, ®s); + if (status < 0) { + fprintf(stderr, + "sigtrap: ?, ?\n"); + } else { + fprintf(stderr, "sigtrap: %ld == 0 ? %d\n", + REG(regs, SYSARG_NUM), + REG(regs, SYSARG_RESULT) == 0); + } + + /* PTRACER_BEHAVIOR_1 */ + if (restart_how != PTRACE_SYSCALL) { + restart_how = PTRACE_SYSCALL; + signal = 0; + break; + } + + /* 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 (skip_bare_sigtrap) { + signal = 0; + break; + } + skip_bare_sigtrap = true; + + ptrace_options = PTRACE_O_TRACESYSGOOD + | PTRACE_O_TRACEFORK + | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEVFORKDONE + | PTRACE_O_TRACECLONE + | PTRACE_O_TRACEEXIT; + + if (getenv("PTRACER_BEHAVIOR_2") == NULL) + ptrace_options |= PTRACE_O_TRACEEXEC; + + status = ptrace(PTRACE_SETOPTIONS, pid, NULL, ptrace_options); + if (status < 0) { + perror("ptrace(PTRACE_SETOPTIONS)"); + exit(EXIT_FAILURE); + } + /* Fall through. */ + case SIGTRAP | 0x80: + fprintf(stderr, "sid %d: PTRACE_EVENT_SYSGOOD\n", sid); + signal = 0; + + status = ptrace(PTRACE_GETREGS, pid, NULL, ®s); + if (status < 0) { + fprintf(stderr, + "syscall(?) = ?\n"); + } else { + fprintf(stderr, "syscall(%ld) == 0 ? %d\n", + REG(regs, SYSARG_NUM), + REG(regs, SYSARG_RESULT) == 0); + } + break; + + case SIGTRAP | PTRACE_EVENT_VFORK << 8: + fprintf(stderr, "sid %d: PTRACE_EVENT_VFORK\n", sid); + signal = 0; + break; + + case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8: + fprintf(stderr, "sid %d: PTRACE_EVENT_VFORK\n", sid); + signal = 0; + break; + + case SIGTRAP | PTRACE_EVENT_FORK << 8: + fprintf(stderr, "sid %d: PTRACE_EVENT_FORK\n", sid); + signal = 0; + break; + + case SIGTRAP | PTRACE_EVENT_CLONE << 8: + fprintf(stderr, "sid %d: PTRACE_EVENT_CLONE\n", sid); + signal = 0; + break; + + case SIGTRAP | PTRACE_EVENT_EXEC << 8: + fprintf(stderr, "sid %d: PTRACE_EVENT_EXEC\n", sid); + signal = 0; + break; + + case SIGTRAP | PTRACE_EVENT_EXIT << 8: + fprintf(stderr, "sid %d: PTRACE_EVENT_EXIT\n", sid); + signal = 0; + break; + + case SIGSTOP: + fprintf(stderr, "sid %d: SIGSTOP\n", sid); + signal = 0; + break; + + default: + break; + } + } + else { + fprintf(stderr, "sid %d: unknown trace event\n", sid); + signal = 0; + } + + /* Restart the tracee and stop it at the next entry or + * exit of a system call. */ + status = ptrace(restart_how, pid, NULL, signal); + if (status < 0) + fprintf(stderr, "ptrace(<restart_how>, %d, %d): %s\n", sid, signal, strerror(errno)); + } + + return last_exit_status; +}
diff --git a/5.1.0/tests/ptrace.c b/5.1.0/tests/ptrace.c new file mode 100644 index 0000000..3613139 --- /dev/null +++ b/5.1.0/tests/ptrace.c
@@ -0,0 +1,54 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ptrace.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> + +int main(int argc, char **argv) +{ + int child_status; + long status; + pid_t pid; + + pid = (argc <= 1 ? fork() : vfork()); + switch(pid) { + case -1: + perror("fork()"); + exit(EXIT_FAILURE); + + case 0: /* child */ + sleep(2); + status = ptrace(PTRACE_TRACEME, 0, NULL, NULL); + if (status < 0) { + perror("ptrace(TRACEME)"); + exit(EXIT_FAILURE); + } + + if (argc <= 1) { + kill(getpid(), SIGSTOP); + exit(EXIT_SUCCESS); + } + else { + execl("true", "true", NULL); + exit(EXIT_FAILURE); + } + + default: /* parent */ + pid = waitpid(-1, &child_status, __WALL); + if (pid < 0) { + perror("waitpid()"); + exit(EXIT_FAILURE); + } + + if (argc <= 1) { + if (!WIFSTOPPED(child_status)) + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); + } + + return 0; +}
diff --git a/5.1.0/tests/puts_proc_self_exe.c b/5.1.0/tests/puts_proc_self_exe.c new file mode 100644 index 0000000..74b920f --- /dev/null +++ b/5.1.0/tests/puts_proc_self_exe.c
@@ -0,0 +1,18 @@ +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> + +int main(void) +{ + char path[PATH_MAX]; + ssize_t status; + + status = readlink("/proc/self/exe", path, PATH_MAX - 1); + if (status < 0 || status >= PATH_MAX) + exit(EXIT_FAILURE); + path[status] = '\0'; + + puts(path); + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/pwd.c b/5.1.0/tests/pwd.c new file mode 100644 index 0000000..31211c9 --- /dev/null +++ b/5.1.0/tests/pwd.c
@@ -0,0 +1,22 @@ +#include <unistd.h> /* syscall(2), */ +#include <stdio.h> /* perror(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <stdlib.h> /* exit(3), */ +#include <sys/syscall.h> /* 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); +}
diff --git a/5.1.0/tests/readdir.c b/5.1.0/tests/readdir.c new file mode 100644 index 0000000..13ca8ec --- /dev/null +++ b/5.1.0/tests/readdir.c
@@ -0,0 +1,49 @@ +#include <sys/types.h> +#include <dirent.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +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); +}
diff --git a/5.1.0/tests/readlink.c b/5.1.0/tests/readlink.c new file mode 100644 index 0000000..58b0f90 --- /dev/null +++ b/5.1.0/tests/readlink.c
@@ -0,0 +1,27 @@ +#include <unistd.h> /* syscall(2), */ +#include <stdio.h> /* perror(3), fprintf(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <stdlib.h> /* exit(3), */ +#include <sys/syscall.h> /* 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); +}
diff --git a/5.1.0/tests/symlink.c b/5.1.0/tests/symlink.c new file mode 100644 index 0000000..1e8c195 --- /dev/null +++ b/5.1.0/tests/symlink.c
@@ -0,0 +1,22 @@ +#include <unistd.h> /* syscall(2), */ +#include <stdio.h> /* perror(3), fprintf(3), */ +#include <stdlib.h> /* exit(3), */ +#include <sys/syscall.h> /* 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); +}
diff --git a/5.1.0/tests/test-00000000.sh b/5.1.0/tests/test-00000000.sh new file mode 100644 index 0000000..3bb77e9 --- /dev/null +++ b/5.1.0/tests/test-00000000.sh
@@ -0,0 +1,5 @@ +if [ ! -x ${ROOTFS}/bin/true ]; then + exit 125; +fi + +${PROOT} -r ${ROOTFS} /bin/true
diff --git a/5.1.0/tests/test-0228fbe7.sh b/5.1.0/tests/test-0228fbe7.sh new file mode 100644 index 0000000..647418b --- /dev/null +++ b/5.1.0/tests/test-0228fbe7.sh
@@ -0,0 +1,26 @@ +if [ -z `which mcookie` ] || [ -z `which env` ] || [ -z `which head` ] || [ -z `which cmp` ] || [ -z `which rm` ] || [ ! -x ${ROOTFS}/bin/ptrace-2 ] || [ ! -x ${ROOTFS}/bin/true ]; then + exit 125; +fi + +TMP1=/tmp/$(mcookie) +TMP2=/tmp/$(mcookie) + +${ROOTFS}/bin/ptrace-2 ${ROOTFS}/bin/true 2>&1 >${TMP1} + +${PROOT} ${ROOTFS}/bin/ptrace-2 ${ROOTFS}/bin/true 2>&1 >${TMP2} + +cmp ${TMP1} ${TMP2} + +env PTRACER_BEHAVIOR_1=1 ${ROOTFS}/bin/ptrace-2 ${ROOTFS}/bin/true 2>&1 >${TMP1} + +env PTRACER_BEHAVIOR_1=1 ${PROOT} ${ROOTFS}/bin/ptrace-2 ${ROOTFS}/bin/true 2>&1 >${TMP2} + +cmp ${TMP1} ${TMP2} + +env PTRACER_BEHAVIOR_2=1 ${ROOTFS}/bin/ptrace-2 ${ROOTFS}/bin/true 2>&1 >${TMP1} + +env PTRACER_BEHAVIOR_2=1 ${PROOT} ${ROOTFS}/bin/ptrace-2 ${ROOTFS}/bin/true 2>&1 >${TMP2} + +cmp ${TMP1} ${TMP2} + +rm -f ${TMP1} ${TMP2}
diff --git a/5.1.0/tests/test-0238c7f1.sh b/5.1.0/tests/test-0238c7f1.sh new file mode 100644 index 0000000..e40751e --- /dev/null +++ b/5.1.0/tests/test-0238c7f1.sh
@@ -0,0 +1,7 @@ +if [ ! -x ${ROOTFS}/bin/pwd ] || [ -z `which grep` ]; then + exit 125; +fi + +${PROOT} -m /:/hostfs -w /hostfs/etc -r ${ROOTFS} pwd | grep '^/hostfs/etc$' +${PROOT} -m /:/hostfs -w /hostfs -r ${ROOTFS} pwd | grep '^/hostfs$' +${PROOT} -m /:/hostfs -w / -r ${ROOTFS} pwd | grep '^/$'
diff --git a/5.1.0/tests/test-03969e70.sh b/5.1.0/tests/test-03969e70.sh new file mode 100644 index 0000000..332fb54 --- /dev/null +++ b/5.1.0/tests/test-03969e70.sh
@@ -0,0 +1,43 @@ +if [ ! -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 +[ $? -eq 0 ] + +env PATH='' ${PROOT} -r ${ROOTFS} -w /bin ./true + +env PATH='' ${PROOT} -r ${ROOTFS} -w / bin/true + +env PATH='' ${PROOT} -r ${ROOTFS} -w / bin/./true + +env PATH='' ${PROOT} -r ${ROOTFS} -w / ../bin/true + +! env PATH='' ${PROOT} -r ${ROOTFS} -w /bin/true ../true +[ $? -eq 0 ] + +! env --unset PATH ${PROOT} -r ${ROOTFS} true +[ $? -eq 0 ] + +! env --unset PATH ${PROOT} -r ${ROOTFS} -w /bin true +[ $? -eq 0 ] + +env --unset PATH ${PROOT} -r ${ROOTFS} -w /bin ./true + +env --unset PATH ${PROOT} -r ${ROOTFS} -w / /bin/true + +env --unset PATH ${PROOT} -r ${ROOTFS} -w / /bin/./true + +env --unset PATH ${PROOT} -r ${ROOTFS} -w / ../bin/true + +! env --unset PATH ${PROOT} -r ${ROOTFS} -w /bin/true ../true +[ $? -eq 0 ]
diff --git a/5.1.0/tests/test-071599da.sh b/5.1.0/tests/test-071599da.sh new file mode 100644 index 0000000..97658f5 --- /dev/null +++ b/5.1.0/tests/test-071599da.sh
@@ -0,0 +1,16 @@ +if [ -z `which uname` ] || [ -z `which true` ] || [ -z `which env` ] || [ -z `which grep` ] || [ -z `which tail` ] || [ ! -x ${ROOTFS}/bin/true ]; then + exit 125; +fi + +${PROOT} -k $(uname -r) true +env PROOT_FORCE_KOMPAT=1 ${PROOT} -k $(uname -r) true + +${PROOT} -k $(uname -r) ${ROOTFS}/bin/true +env PROOT_FORCE_KOMPAT=1 ${PROOT} -k $(uname -r) ${ROOTFS}/bin/true + +if env LD_SHOW_AUXV=1 true | grep -q ^AT_RANDOM; then + env PROOT_FORCE_KOMPAT=1 ${PROOT} -k $(uname -r) env LD_SHOW_AUXV=1 true | tail -1 | grep ^AT_RANDOM +fi + +! ${PROOT} -k $(uname -r) env LD_SHOW_AUXV=1 true | grep AT_SYSINFO +[ $? -eq 0 ]
diff --git a/5.1.0/tests/test-0830d8a8.sh b/5.1.0/tests/test-0830d8a8.sh new file mode 100644 index 0000000..73e9ea9 --- /dev/null +++ b/5.1.0/tests/test-0830d8a8.sh
@@ -0,0 +1,5 @@ +if [ ! -x ${ROOTFS}/bin/exec-m32 ] || [ ! -x ${ROOTFS}/bin/true ]; then + exit 125; +fi + +${PROOT} ${ROOTFS}/bin/exec-m32 ${ROOTFS}/bin/true
diff --git a/5.1.0/tests/test-092c5e26.sh b/5.1.0/tests/test-092c5e26.sh new file mode 100644 index 0000000..4a6d509 --- /dev/null +++ b/5.1.0/tests/test-092c5e26.sh
@@ -0,0 +1,41 @@ +if [ ! -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} <<EOF +#!/bin/argv0 +test +EOF +chmod +x ${ROOTFS}/${TMP_ABS} + +env PATH=/tmp ${PROOT} -r ${ROOTFS} ${TMP} | grep '^/bin/argv0$' +${PROOT} -r ${ROOTFS} ${TMP_ABS} | grep '^/bin/argv0$' + +# Valgrind uses LD_PRELOAD. +if $(echo ${PROOT} | grep -q valgrind); then + EXTRA='-E LD_PRELOAD=.*' +fi + +unset LD_LIBRARY_PATH + +env PROOT_FORCE_FOREIGN_BINARY=1 PATH=/tmp:/bin:/usr/bin ${PROOT} -r ${ROOTFS} -q echo ${TMP} | grep "^-U LD_LIBRARY_PATH ${EXTRA}-0 /bin/argv0 /bin/argv0 ${TMP_ABS}$" +env PROOT_FORCE_FOREIGN_BINARY=1 ${PROOT} -r ${ROOTFS} -q echo ${TMP_ABS} | grep "^-U LD_LIBRARY_PATH ${EXTRA}-0 /bin/argv0 /bin/argv0 ${TMP_ABS}$" + +cat > ${ROOTFS}/${TMP_ABS} <<EOF +FOREIGN BINARY FORMAT +EOF +chmod +x ${ROOTFS}/${TMP_ABS} + +# Valgrind prepends "/bin/sh" in front of foreign binaries. +if ! $(echo ${PROOT} | grep -q valgrind); then + env PATH=/tmp:/bin:/usr/bin ${PROOT} -r ${ROOTFS} -q echo ${TMP} | grep "^-U LD_LIBRARY_PATH -0 ${TMP} ${TMP_ABS}$" + ${PROOT} -r ${ROOTFS} -q echo ${TMP_ABS} | grep "^-U LD_LIBRARY_PATH -0 ${TMP_ABS} ${TMP_ABS}$" +fi + +rm -f ${TMP_ABS}
diff --git a/5.1.0/tests/test-0cf405b0.c b/5.1.0/tests/test-0cf405b0.c new file mode 100644 index 0000000..2566409 --- /dev/null +++ b/5.1.0/tests/test-0cf405b0.c
@@ -0,0 +1,12 @@ +#include <unistd.h> /* execlp(2), */ +#include <stdlib.h> /* exit(3), */ +#include <string.h> /* 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); +}
diff --git a/5.1.0/tests/test-11111111.sh b/5.1.0/tests/test-11111111.sh new file mode 100644 index 0000000..7538832 --- /dev/null +++ b/5.1.0/tests/test-11111111.sh
@@ -0,0 +1,67 @@ +#!/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
diff --git a/5.1.0/tests/test-16573e73.c b/5.1.0/tests/test-16573e73.c new file mode 100644 index 0000000..c1c1876 --- /dev/null +++ b/5.1.0/tests/test-16573e73.c
@@ -0,0 +1,29 @@ +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <stdlib.h> + +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); +}
diff --git a/5.1.0/tests/test-1743dd3d.sh b/5.1.0/tests/test-1743dd3d.sh new file mode 100644 index 0000000..3fde250 --- /dev/null +++ b/5.1.0/tests/test-1743dd3d.sh
@@ -0,0 +1,17 @@ +if [ ! -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} -r ${ROOTFS} ${TMP} + +chmod +x ${ROOTFS}/${TMP} +${PROOT} -r ${ROOTFS} ${TMP} + +rm -f ${ROOTFS}/${TMP}
diff --git a/5.1.0/tests/test-1c68c218.c b/5.1.0/tests/test-1c68c218.c new file mode 100644 index 0000000..8de82aa --- /dev/null +++ b/5.1.0/tests/test-1c68c218.c
@@ -0,0 +1,25 @@ +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +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); +}
diff --git a/5.1.0/tests/test-1cd9d8f9.sh b/5.1.0/tests/test-1cd9d8f9.sh new file mode 100644 index 0000000..12bede4 --- /dev/null +++ b/5.1.0/tests/test-1cd9d8f9.sh
@@ -0,0 +1,5 @@ +if ! `which pwd` -P || [ -z `which grep` ] ; then + exit 125; +fi + +${PROOT} -w /tmp pwd -P | grep '^/tmp$'
diff --git a/5.1.0/tests/test-1fedd9a3.sh b/5.1.0/tests/test-1fedd9a3.sh new file mode 100644 index 0000000..3253188 --- /dev/null +++ b/5.1.0/tests/test-1fedd9a3.sh
@@ -0,0 +1,62 @@ +if [ -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` -eq 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} -i 123:456 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} -i 123:456 stat -c %a ${TMP}/foo | grep '^0$' +[ $? -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} -i 123:456 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} + +mkdir -p ${TMP}/foo +chmod -rwx ${TMP} + +! rm -fr ${TMP} +[ $? -eq 0 ] + +! ${PROOT} -i 123:456 rm -fr ${TMP} +[ $? -eq 0 ] + +${PROOT} -0 rm -fr ${TMP}
diff --git a/5.1.0/tests/test-1ffc8309.c b/5.1.0/tests/test-1ffc8309.c new file mode 100644 index 0000000..b5c01f5 --- /dev/null +++ b/5.1.0/tests/test-1ffc8309.c
@@ -0,0 +1,30 @@ +#define _GNU_SOURCE +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> + +int main() +{ + int fds[2]; + int status; + uint8_t buffer; + + status = pipe2(fds, O_NONBLOCK); + if (status < 0) { + perror("pipe2"); + exit(EXIT_FAILURE); + } + + (void) alarm(5); + + (void) read(fds[0], &buffer, 1); + if (errno != EAGAIN && errno != EWOULDBLOCK) { + perror("read"); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-1ffc8309.sh b/5.1.0/tests/test-1ffc8309.sh new file mode 100644 index 0000000..c270641 --- /dev/null +++ b/5.1.0/tests/test-1ffc8309.sh
@@ -0,0 +1,9 @@ +if [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which env` ] || [ -z `which uname` ] || [ -z `which rm` ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) + +mkdir ${TMP} + +env PROOT_FORCE_KOMPAT=1 ${PROOT} -k $(uname -r) rm -r ${TMP}
diff --git a/5.1.0/tests/test-22222222.sh b/5.1.0/tests/test-22222222.sh new file mode 100644 index 0000000..3e47524 --- /dev/null +++ b/5.1.0/tests/test-22222222.sh
@@ -0,0 +1,21 @@ +if [ ! -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 -r ${ROOTFS} /bin/readlink /tmp/${SYMLINK} | grep ^/tmp/${REGULAR}$ +${PROOT} -b /tmp:/ced -r ${ROOTFS} /bin/readlink /ced/${SYMLINK} | grep ^/ced/${REGULAR}$ + +rm -f /tmp/${REGULAR} +rm -f /tmp/${SYMLINK} +rm -f ${ROOTFS}/tmp/${REGULAR} +
diff --git a/5.1.0/tests/test-230f47cf.sh b/5.1.0/tests/test-230f47cf.sh new file mode 100644 index 0000000..c6f7a4c --- /dev/null +++ b/5.1.0/tests/test-230f47cf.sh
@@ -0,0 +1,6 @@ +! ${PROOT} ${PROOT_RAW} /bin/true +if [ $? -eq 0 ]; then + exit 125; +fi + +echo exit | ${PROOT} -v 0 ${PROOT_RAW} -v 0
diff --git a/5.1.0/tests/test-230f47cg.sh.deprecated b/5.1.0/tests/test-230f47cg.sh.deprecated new file mode 100644 index 0000000..e81015a --- /dev/null +++ b/5.1.0/tests/test-230f47cg.sh.deprecated
@@ -0,0 +1,40 @@ +if [ ! -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}
diff --git a/5.1.0/tests/test-230f47ch.sh b/5.1.0/tests/test-230f47ch.sh new file mode 100644 index 0000000..fed0ac3 --- /dev/null +++ b/5.1.0/tests/test-230f47ch.sh
@@ -0,0 +1,25 @@ +if [ -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 + +${PROOT} ${PROOT_RAW} -0 id -u | grep ^0$ +${PROOT} ${PROOT_RAW} -i 123:456 id -u | grep ^123$ +${PROOT} ${PROOT_RAW} -k 3.33.333 uname -r | grep ^3\.33\.333$ + +${PROOT} -0 ${PROOT_RAW} id -u | grep ^0$ +${PROOT} -i 123:456 ${PROOT_RAW} id -u | grep ^123$ +${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} -i 123:456 ${PROOT_RAW} -k 3.33.333 id -u | grep ^123$ +${PROOT} -k 3.33.333 ${PROOT_RAW} -i 123:456 id -u | grep ^123$
diff --git a/5.1.0/tests/test-2401b850.sh b/5.1.0/tests/test-2401b850.sh new file mode 100644 index 0000000..e53460e --- /dev/null +++ b/5.1.0/tests/test-2401b850.sh
@@ -0,0 +1,67 @@ +if [ -z `which mcookie` ] || [ -z `which echo` ] || [ -z `which rm` ] || [ -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=$(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}" + TEST5="-- -E LD_PRELOAD= -E LD_LIBRARY_PATH=test5 -0 sh /bin/sh -c ${TMP}" + TEST52="-- -E LD_PRELOAD= -E LD_LIBRARY_PATH=test5 -0 sh /bin/sh -c sh -c ${TMP}" + TEST6="-- -E LD_PRELOAD= -E LD_LIBRARY_PATH=test5 -0 env ${ENV} LD_LIBRARY_PATH=test6 ${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}" + TEST5="-- -E LD_LIBRARY_PATH=test5 ${COMMAND1}" + TEST52=${TEST5} + TEST6="-- -E LD_LIBRARY_PATH=test6 ${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}$" + +env LD_LIBRARY_PATH=test5 ${PROOT} -q 'echo --' sh -c ${TMP} | grep -- "^${TEST5}$" +env LD_LIBRARY_PATH=test5 ${PROOT} -q 'echo --' sh -c "sh -c ${TMP}" | grep -- "^${TEST52}$" +env LD_LIBRARY_PATH=test5 ${PROOT} -q 'echo --' env LD_LIBRARY_PATH=test6 ${TMP} | grep -- "^${TEST6}$" + +rm -f ${TMP2} +echo "#!${TMP}" > ${TMP2} +chmod +x ${TMP2} +${PROOT} -q 'echo --' ${TMP2} | grep -- "^-- -U LD_LIBRARY_PATH ${COMMAND2}$" + +rm -fr ${TMP} ${TMP2}
diff --git a/5.1.0/tests/test-25069c12.c b/5.1.0/tests/test-25069c12.c new file mode 100644 index 0000000..1f6cd52 --- /dev/null +++ b/5.1.0/tests/test-25069c12.c
@@ -0,0 +1,14 @@ +#include <unistd.h> /* execve(2), */ +#include <stdlib.h> /* exit(3), */ +#include <string.h> /* strcmp(3), */ + +int main(int argc, char *argv[]) +{ + char *void_array[] = { NULL }; + + if (argc == 0) + exit(EXIT_SUCCESS); + + execve("/proc/self/exe", void_array, void_array); + exit(EXIT_FAILURE); +}
diff --git a/5.1.0/tests/test-25069c13.c b/5.1.0/tests/test-25069c13.c new file mode 100644 index 0000000..dc14733 --- /dev/null +++ b/5.1.0/tests/test-25069c13.c
@@ -0,0 +1,12 @@ +#include <unistd.h> /* execve(2), */ +#include <stdlib.h> /* exit(3), */ +#include <string.h> /* strcmp(3), */ + +int main(int argc, char *argv[]) +{ + if (argc == 0) + exit(EXIT_SUCCESS); + + execve("/proc/self/exe", NULL, NULL); + exit(EXIT_FAILURE); +}
diff --git a/5.1.0/tests/test-2db65cd2.sh b/5.1.0/tests/test-2db65cd2.sh new file mode 100644 index 0000000..6c726f0 --- /dev/null +++ b/5.1.0/tests/test-2db65cd2.sh
@@ -0,0 +1,28 @@ +if [ ! -x ${ROOTFS}/bin/true ] || [ -z `which gdb` ]; then + exit 125; +fi + +TMP1=/tmp/$(mcookie) +TMP2=/tmp/$(mcookie) +TMP3=/tmp/$(mcookie) +TMP4=/tmp/$(mcookie) +TMP5=/tmp/$(mcookie) + +cat > ${TMP5} <<EOF +break main +run +cont +EOF + +COMMAND="gdb ${ROOTFS}/bin/true -batch -n -x ${TMP5}" + +${COMMAND} > ${TMP1} +! grep -v 'process' ${TMP1} > ${TMP2} + +${PROOT} ${COMMAND} > ${TMP4} +! grep -v 'process' ${TMP4} > ${TMP3} +! grep -v '^proot warning: ' ${TMP3} > ${TMP4} + +cmp ${TMP2} ${TMP4} + +rm -f ${TMP1} ${TMP2} ${TMP3} ${TMP4} ${TMP5}
diff --git a/5.1.0/tests/test-305ae31d.c b/5.1.0/tests/test-305ae31d.c new file mode 100644 index 0000000..f201f2d --- /dev/null +++ b/5.1.0/tests/test-305ae31d.c
@@ -0,0 +1,60 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +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); +}
diff --git a/5.1.0/tests/test-305ae31d.sh b/5.1.0/tests/test-305ae31d.sh new file mode 100644 index 0000000..b7a2fb0 --- /dev/null +++ b/5.1.0/tests/test-305ae31d.sh
@@ -0,0 +1,9 @@ +if [ -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} +
diff --git a/5.1.0/tests/test-311b7a95.sh b/5.1.0/tests/test-311b7a95.sh new file mode 100644 index 0000000..cd93c71 --- /dev/null +++ b/5.1.0/tests/test-311b7a95.sh
@@ -0,0 +1,18 @@ +if [ -z `which mcookie` ] || [ -z `which rm` ] || [ -z `which chmod` ] || [ -z `which echo` ]; then + exit 125; +fi + +TMP1=/tmp/$(mcookie) +TMP2=/tmp/$(mcookie) + +echo "#! ${TMP2} -a" > ${TMP1} +echo "#! $(which echo) -b" > ${TMP2} + +chmod +x ${TMP1} ${TMP2} + +RESULT=$(${PROOT} ${TMP1}) +EXPECTED=$(${TMP1}) + +test "${RESULT}" = "${EXPECTED}" + +rm -f ${TMP1} ${TMP2}
diff --git a/5.1.0/tests/test-33333333.c b/5.1.0/tests/test-33333333.c new file mode 100644 index 0000000..84f56df --- /dev/null +++ b/5.1.0/tests/test-33333333.c
@@ -0,0 +1,23 @@ +/* Check a child is traced even if its parent doesn't call wait(2). + * + * Reported-by: Clément BAZIN <clement.bazin@st.com> + * on Ubuntu 11.10 x86_64 + */ + +#include <stdlib.h> /* exit(3), */ +#include <unistd.h> /* 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; + } +}
diff --git a/5.1.0/tests/test-33333334.c b/5.1.0/tests/test-33333334.c new file mode 100644 index 0000000..7e097e3 --- /dev/null +++ b/5.1.0/tests/test-33333334.c
@@ -0,0 +1,31 @@ +#include <stdlib.h> /* exit(3), */ +#include <unistd.h> /* fork(2), */ + +int main(void) +{ + int child_status; + int status; + + switch (fork()) { + case -1: + exit(EXIT_FAILURE); + + case 0: /* child */ + return 13; + + default: /* parent */ + status = wait(&child_status); + if (status < 0) { + perror("wait()"); + exit(EXIT_FAILURE); + } + + if (!WIFEXITED(child_status)) + exit(EXIT_FAILURE); + + if (WEXITSTATUS(child_status) != 13) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); + } +}
diff --git a/5.1.0/tests/test-3624be91.sh b/5.1.0/tests/test-3624be91.sh new file mode 100644 index 0000000..a3e8a67 --- /dev/null +++ b/5.1.0/tests/test-3624be91.sh
@@ -0,0 +1,5 @@ +if [ -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 :)'
diff --git a/5.1.0/tests/test-3dec4597.sh b/5.1.0/tests/test-3dec4597.sh new file mode 100644 index 0000000..bc4eb89 --- /dev/null +++ b/5.1.0/tests/test-3dec4597.sh
@@ -0,0 +1,5 @@ +if [ ! -x ${ROOTFS}/bin/pwd ]; then + exit 125; +fi + +${PROOT} -m /tmp:/longer-tmp -w /longer-tmp -r ${ROOTFS} /bin/pwd
diff --git a/5.1.0/tests/test-44444444.c b/5.1.0/tests/test-44444444.c new file mode 100644 index 0000000..d6f1fdc --- /dev/null +++ b/5.1.0/tests/test-44444444.c
@@ -0,0 +1,21 @@ +#include <unistd.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> + +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); +}
diff --git a/5.1.0/tests/test-517e1d6a.sh b/5.1.0/tests/test-517e1d6a.sh new file mode 100644 index 0000000..818c72d --- /dev/null +++ b/5.1.0/tests/test-517e1d6a.sh
@@ -0,0 +1,33 @@ +if [ ! -x ${ROOTFS}/bin/argv ] || [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which chmod` ] || [ -z `which env` ] || [ -z `which rm` ] || [ -z `which grep` ] || [ -z `which env` ] || [ -z `which ln` ]; then + exit 125; +fi + +BIN_DIR=/tmp/$(mcookie) +TMP=$(mcookie) +TMP2=$(mcookie) + +mkdir ${BIN_DIR} +echo "#! ${ROOTFS}/bin/argv -x" > ${BIN_DIR}/${TMP} +chmod +x ${BIN_DIR}/${TMP} +ln -s ${BIN_DIR}/${TMP} ${BIN_DIR}/${TMP2} + +${PROOT} env ${BIN_DIR}/${TMP} | grep "${ROOTFS}/bin/argv -x ${BIN_DIR}/${TMP}" + +${PROOT} env PATH=${BIN_DIR} ${TMP} | grep "${ROOTFS}/bin/argv -x ${BIN_DIR}/${TMP}" + +(cd ${BIN_DIR}; ${PROOT} env ./${TMP}) | grep "${ROOTFS}/bin/argv -x ./${TMP}" + +${PROOT} env ${BIN_DIR}/${TMP2} | grep "${ROOTFS}/bin/argv -x ${BIN_DIR}/${TMP2}" + +${PROOT} env PATH=${BIN_DIR} ${TMP2} | grep "${ROOTFS}/bin/argv -x ${BIN_DIR}/${TMP2}" + +(cd ${BIN_DIR}; ${PROOT} env ./${TMP2}) | grep "${ROOTFS}/bin/argv -x ./${TMP2}" + +${PROOT} ${BIN_DIR}/${TMP} | grep "${ROOTFS}/bin/argv -x ${BIN_DIR}/${TMP}" + +env PATH=${BIN_DIR} ${PROOT} ${TMP} | grep "${ROOTFS}/bin/argv -x ${BIN_DIR}/${TMP}" + +# TODO: (cd ${BIN_DIR}; ${PROOT} ./${TMP}) | grep "${ROOTFS}/bin/argv -x ./${TMP}" +(cd ${BIN_DIR}; ${PROOT} sh -c "true; ./${TMP}") | grep "${ROOTFS}/bin/argv -x ./${TMP}" + +rm -fr ${BIN_DIR}
diff --git a/5.1.0/tests/test-517e1d6b.sh b/5.1.0/tests/test-517e1d6b.sh new file mode 100644 index 0000000..94e17ae --- /dev/null +++ b/5.1.0/tests/test-517e1d6b.sh
@@ -0,0 +1,7 @@ +if [ -z `which true` ] || [ -z `which realpath` ] || [ -z `which grep` ] || [ -z `which env` ] || [ ! -x ${ROOTFS}/bin/puts_proc_self_exe ]; then + exit 125; +fi + +TRUE=$(realpath $(which true)) + +env PROOT_FORCE_FOREIGN_BINARY=1 ${PROOT} -q ${ROOTFS}/bin/puts_proc_self_exe ${TRUE} | grep ^${TRUE}$
diff --git a/5.1.0/tests/test-51943658.c b/5.1.0/tests/test-51943658.c new file mode 100644 index 0000000..d8a42cc --- /dev/null +++ b/5.1.0/tests/test-51943658.c
@@ -0,0 +1,64 @@ +#include <unistd.h> /* syscall(2), */ +#include <stdio.h> /* perror(3), fprintf(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <stdlib.h> /* exit(3), */ +#include <fcntl.h> /* 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); +}
diff --git a/5.1.0/tests/test-53355a5b.sh b/5.1.0/tests/test-53355a5b.sh new file mode 100644 index 0000000..5ae021d --- /dev/null +++ b/5.1.0/tests/test-53355a5b.sh
@@ -0,0 +1,13 @@ +if [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which chmod` ] || [ -z `which rm` ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) + +mkdir ${TMP} +chmod a-x ${TMP} +! ${PROOT} sh -c "cd $TMP" +[ $? -eq 0 ] + +chmod a+x ${TMP} +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-5467b986.sh b/5.1.0/tests/test-5467b986.sh new file mode 100644 index 0000000..c6ac71a --- /dev/null +++ b/5.1.0/tests/test-5467b986.sh
@@ -0,0 +1,37 @@ +if [ -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} -r ${ROOTFS} readlink /proc/self/cwd | grep '^/$' + +${PROOT} -v -1 -w /a -b /tmp:/a -b /tmp:/b -r ${ROOTFS} pwd | grep '^/a$' +${PROOT} -v -1 -w /a -b /tmp:/b -b /tmp:/a -r ${ROOTFS} pwd | grep '^/a$' +${PROOT} -v -1 -w /b -b /tmp:/a -b /tmp:/b -r ${ROOTFS} pwd | grep '^/b$' +${PROOT} -v -1 -w /b -b /tmp:/b -b /tmp:/a -r ${ROOTFS} pwd | grep '^/b$' + +${PROOT} -v -1 -b /tmp:/a -b /tmp:/b -r ${ROOTFS} chdir_getcwd /a | grep '^/[ab]$' +${PROOT} -v -1 -b /tmp:/b -b /tmp:/a -r ${ROOTFS} chdir_getcwd /a | grep '^/[ab]$' +${PROOT} -v -1 -b /tmp:/a -b /tmp:/b -r ${ROOTFS} chdir_getcwd /b | grep '^/[ab]$' +${PROOT} -v -1 -b /tmp:/b -b /tmp:/a -r ${ROOTFS} chdir_getcwd /b | grep '^/[ab]$' + +${PROOT} -v -1 -b /tmp:/a -b /tmp:/b -r ${ROOTFS} fchdir_getcwd /a | grep '^/[ab]$' +${PROOT} -v -1 -b /tmp:/b -b /tmp:/a -r ${ROOTFS} fchdir_getcwd /a | grep '^/[ab]$' +${PROOT} -v -1 -b /tmp:/a -b /tmp:/b -r ${ROOTFS} fchdir_getcwd /b | grep '^/[ab]$' +${PROOT} -v -1 -b /tmp:/b -b /tmp:/a -r ${ROOTFS} fchdir_getcwd /b | grep '^/[ab]$' + +! ${PROOT} -r ${ROOTFS} chdir_getcwd /bin/true +[ $? -eq 0 ] +! ${PROOT} -r ${ROOTFS} fchdir_getcwd /bin/true +[ $? -eq 0 ] + +! ${PROOT} -w /bin -r ${ROOTFS} chdir_getcwd true +[ $? -eq 0 ] +! ${PROOT} -w /bin -r ${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$')
diff --git a/5.1.0/tests/test-55b731d3.sh b/5.1.0/tests/test-55b731d3.sh new file mode 100644 index 0000000..27f26ed --- /dev/null +++ b/5.1.0/tests/test-55b731d3.sh
@@ -0,0 +1,5 @@ +if ! `which pwd` -P; then + exit 125; +fi + +${PROOT} pwd -P
diff --git a/5.1.0/tests/test-55fd1da5.sh b/5.1.0/tests/test-55fd1da5.sh new file mode 100644 index 0000000..b10fb65 --- /dev/null +++ b/5.1.0/tests/test-55fd1da5.sh
@@ -0,0 +1,5 @@ +if [ -z `which ls` ]; then + exit 125; +fi + +${PROOT} -b /etc:/x ls -la /x
diff --git a/5.1.0/tests/test-5996858d.sh b/5.1.0/tests/test-5996858d.sh new file mode 100644 index 0000000..217f6c2 --- /dev/null +++ b/5.1.0/tests/test-5996858d.sh
@@ -0,0 +1,16 @@ +if [ -z `which uname` ] || [ -z `which grep` ] || [ -z `which domainname` ] || [ -z `which hostname` ]|| [ -z `which env` ] || [ -z `which true`]; then + exit 125; +fi + +UTSNAME="\\sysname\\nodename\\$(uname -r)\\version\\machine\\domainname\\0\\" + +${PROOT} -k ${UTSNAME} uname -s | grep ^sysname$ +${PROOT} -k ${UTSNAME} uname -n | grep ^nodename$ +${PROOT} -k ${UTSNAME} uname -v | grep ^version$ +${PROOT} -k ${UTSNAME} uname -m | grep ^machine$ +${PROOT} -k ${UTSNAME} domainname | grep ^domainname$ +${PROOT} -k ${UTSNAME} env LD_SHOW_AUXV=1 true | grep -E '^AT_HWCAP:[[:space:]]*0?$' + +${PROOT} -0 -k ${UTSNAME} sh -c 'domainname domainname2; domainname' | grep ^domainname2$ +${PROOT} -0 -k ${UTSNAME} sh -c 'hostname hostname2; hostname' | grep ^hostname2$ +${PROOT} -0 -k ${UTSNAME} sh -c 'hostname hostname2; uname -n' | grep ^hostname2$
diff --git a/5.1.0/tests/test-5bed7141.c b/5.1.0/tests/test-5bed7141.c new file mode 100644 index 0000000..f6a7e74 --- /dev/null +++ b/5.1.0/tests/test-5bed7141.c
@@ -0,0 +1,102 @@ +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> + +static 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); +} +
diff --git a/5.1.0/tests/test-5bed7142.sh b/5.1.0/tests/test-5bed7142.sh new file mode 100644 index 0000000..82574d6 --- /dev/null +++ b/5.1.0/tests/test-5bed7142.sh
@@ -0,0 +1,11 @@ +if [ ! -x ${ROOTFS}/bin/pwd ] || [ -z `which mkdir` ] || [ -z `which grep` ] || [ -z `which mcookie` ] || [ -z `which pwd` ]; then + exit 125; +fi + +mkdir -p ${ROOTFS}/${PWD} +${PROOT} -v 1 -w . -r ${ROOTFS} pwd | grep ^${PWD}$ + +TMP=/tmp/$(mcookie) +mkdir ${TMP} +! ${PROOT} sh -c "cd ${TMP}; rmdir ${TMP}; $(which pwd) -P" +[ $? -eq 0 ]
diff --git a/5.1.0/tests/test-5bed7143.c b/5.1.0/tests/test-5bed7143.c new file mode 100644 index 0000000..a17fe22 --- /dev/null +++ b/5.1.0/tests/test-5bed7143.c
@@ -0,0 +1,73 @@ +#define _GNU_SOURCE +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +#define TEMPLATE "/tmp/proot-test-5bed7143-XXXXXX" +#define COOKIE1 "2fde3df3558fa30bec1b8ebad42df20f" +#define COOKIE2 "2ba90289e48d1896e0601239ac25f764" + +int main() +{ + char *path1; + char *path2; + char *cwd; + int status; + + path1 = mkdtemp(strdup(TEMPLATE)); + if (path1 == NULL) + exit(EXIT_FAILURE); + + status = chdir(path1); + if (status < 0) + exit(EXIT_FAILURE); + + status = mkdir(COOKIE1, 0777); + if (status < 0) + exit(EXIT_FAILURE); + + status = chdir(COOKIE1); + if (status < 0) + exit(EXIT_FAILURE); + + status = creat(COOKIE2, O_RDWR); + if (status < 0) + exit(EXIT_FAILURE); + close(status); + + path2 = mktemp(strdup(TEMPLATE)); + status = rename(path1, path2); + if (status < 0) + exit(EXIT_FAILURE); + + status = access(COOKIE2, F_OK); + if (status < 0) + exit(EXIT_FAILURE); + + cwd = get_current_dir_name(); + if (cwd == NULL || memcmp(cwd, path2, strlen(path2)) != 0) + exit(EXIT_FAILURE); + + status = unlink(COOKIE2); + if (status < 0) + exit(EXIT_FAILURE); + + status = rmdir(cwd); + if (status < 0) + exit(EXIT_FAILURE); + + if (get_current_dir_name() != NULL || errno != ENOENT) + exit(EXIT_FAILURE); + + status = rmdir(path2); + if (status < 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-654decce.sh b/5.1.0/tests/test-654decce.sh new file mode 100644 index 0000000..6f25b7f --- /dev/null +++ b/5.1.0/tests/test-654decce.sh
@@ -0,0 +1,59 @@ +if [ ! -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` -eq 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}
diff --git a/5.1.0/tests/test-66666666.c b/5.1.0/tests/test-66666666.c new file mode 100644 index 0000000..1439842 --- /dev/null +++ b/5.1.0/tests/test-66666666.c
@@ -0,0 +1,46 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdbool.h> +#include <signal.h> +#include <errno.h> + +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); +}
diff --git a/5.1.0/tests/test-67972fbe.sh b/5.1.0/tests/test-67972fbe.sh new file mode 100644 index 0000000..96f3d6c --- /dev/null +++ b/5.1.0/tests/test-67972fbe.sh
@@ -0,0 +1,13 @@ +if [ ! -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 -r ${ROOTFS} readdir ${TMP}/var/run/dbus/ | grep true + +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-691786c8.sh b/5.1.0/tests/test-691786c8.sh new file mode 100644 index 0000000..a09fda6 --- /dev/null +++ b/5.1.0/tests/test-691786c8.sh
@@ -0,0 +1,45 @@ +if [ ! -x /usr/bin/echo ] || [ -z `which mcookie` ] || [ -z `which chmod` ] || [ -z `which env` ] || [ -z `which rm` ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) + +echo '#!/usr/bin/echo XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' > ${TMP} + +chmod +x ${TMP} + +RESULT=$(${PROOT} ${TMP}) +EXPECTED=$(${TMP}) + +[ "${RESULT}" = "${EXPECTED}" ] + +echo '#!//../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../usr/bin/echo XXXXXXXXX' > ${TMP} + +RESULT=$(${PROOT} ${TMP}) +EXPECTED=$(${TMP}) + +[ "${RESULT}" = "${EXPECTED}" ] +[ "${RESULT}" = "${TMP}" ] + +echo '#!/../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../usr/bin/echo XXXXXXXXX' > ${TMP} + +! ${PROOT} ${TMP} +[ $? -eq 0 ] + +! ${TMP} +[ $? -eq 0 ] + +echo '#! ' > ${TMP} + +${PROOT} ${TMP} + +echo '#!' > ${TMP} + +${PROOT} ${TMP} + +/usr/bin/echo "#!${TMP}" > ${TMP} + +env LANG=C ${PROOT} ${TMP} 2>&1 | grep 'Too many levels of symbolic links' +[ $? -eq 0 ] + +rm -f ${TMP}
diff --git a/5.1.0/tests/test-6b5a254a.sh b/5.1.0/tests/test-6b5a254a.sh new file mode 100644 index 0000000..f43ae3f --- /dev/null +++ b/5.1.0/tests/test-6b5a254a.sh
@@ -0,0 +1,25 @@ +if [ -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}
diff --git a/5.1.0/tests/test-6d1e2650.sh b/5.1.0/tests/test-6d1e2650.sh new file mode 100644 index 0000000..f5358a0 --- /dev/null +++ b/5.1.0/tests/test-6d1e2650.sh
@@ -0,0 +1,8 @@ +if [ ! -x ${ROOTFS}/bin/true ] || [ -z `which env` ]; then + exit 125; +fi + +! env PATH=/nib ${PROOT} -r ${ROOTFS} true +[ $? -eq 0 ] + +env PATH=/bin ${PROOT} -r ${ROOTFS} true
diff --git a/5.1.0/tests/test-6fb08ce1.sh b/5.1.0/tests/test-6fb08ce1.sh new file mode 100644 index 0000000..63deef7 --- /dev/null +++ b/5.1.0/tests/test-6fb08ce1.sh
@@ -0,0 +1,10 @@ +if [ -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}
diff --git a/5.1.0/tests/test-713b6910.sh b/5.1.0/tests/test-713b6910.sh new file mode 100644 index 0000000..82e01fd --- /dev/null +++ b/5.1.0/tests/test-713b6910.sh
@@ -0,0 +1,51 @@ +if [ -z `which mcookie` ] || [ -z `which rm` ] || [ -z `which cat` ] || [ -z `which chmod` ] || [ -z `which ln` ] || [ -z `which grep` ] || [ -z `which mkdir` ] || [ ! -x ${ROOTFS}/bin/readlink ]; then + exit 125; +fi + +###################################################################### + +TMP1=/tmp/$(mcookie) +TMP2=/tmp/$(mcookie) +TMP3=/tmp/$(mcookie) +TMP4=/tmp/$(mcookie) + +rm -fr ${TMP1} ${TMP2} ${TMP3} ${TMP4} + +###################################################################### + +cat > ${TMP1} <<'EOF' +#!/bin/sh +echo $0 +EOF + +chmod +x ${TMP1} +ln -s ${TMP1} ${TMP2} + +${PROOT} ${TMP2} | grep -v ${TMP1} +${PROOT} ${TMP2} | grep ${TMP2} + +###################################################################### + +mkdir -p ${TMP3} +cd ${TMP3} + +ln -s $(which true) false +! ${PROOT} false + +echo "#!$(which false)" > true +chmod a-x true +${PROOT} true + +###################################################################### + +ln -s ${ROOTFS}/bin/readlink ${TMP4} + +TEST1=$(${PROOT} ${ROOTFS}/bin/readlink /proc/self/exe) +TEST2=$(${PROOT} ${TMP4} /proc/self/exe) + +test "${TEST1}" = "${TEST2}" + +###################################################################### + +cd / +rm -fr ${TMP1} ${TMP2} ${TMP3} ${TMP4}
diff --git a/5.1.0/tests/test-7601199b.sh b/5.1.0/tests/test-7601199b.sh new file mode 100644 index 0000000..fc489b4 --- /dev/null +++ b/5.1.0/tests/test-7601199b.sh
@@ -0,0 +1,5 @@ +if [ ! -x /bin/sh ] || [ -z `which grep` ]; then + exit 125; +fi + +${PROOT} -w /tmp /bin/sh -c 'echo $PWD' | grep '^/tmp$'
diff --git a/5.1.0/tests/test-77777777.c.unreliable b/5.1.0/tests/test-77777777.c.unreliable new file mode 100644 index 0000000..09683cb --- /dev/null +++ b/5.1.0/tests/test-77777777.c.unreliable
@@ -0,0 +1,51 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <unistd.h> + +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); + } +} +
diff --git a/5.1.0/tests/test-79cf6614.c b/5.1.0/tests/test-79cf6614.c new file mode 100644 index 0000000..cdb8d81 --- /dev/null +++ b/5.1.0/tests/test-79cf6614.c
@@ -0,0 +1,32 @@ +/* + * Submitted-by: Thomas P. HIGDON <thomas.p.higdon@gmail.com> + * Ref.: https://groups.google.com/d/msg/proot_me/4WbUndy-aXI/lmKiDfoIK_IJ + */ + +#include <sys/time.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +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); +} +
diff --git a/5.1.0/tests/test-82ba4ba1.c b/5.1.0/tests/test-82ba4ba1.c new file mode 100644 index 0000000..fd78dc6 --- /dev/null +++ b/5.1.0/tests/test-82ba4ba1.c
@@ -0,0 +1,59 @@ +#define _GNU_SOURCE +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +int main(void) +{ + uid_t ruid = 13, euid = 13, suid = 13; + gid_t rgid = 13, egid = 13, sgid = 13; + int status; + + status = getresuid(&ruid, &euid, &suid); + if (status != 0 || ruid != 0 || euid != 0 || suid != 0) { + perror("getresuid"); + fprintf(stderr, "%ld %ld %ld\n", (unsigned long) ruid, (unsigned long) euid, (unsigned long) suid); + exit(EXIT_FAILURE); + } + + status = getresgid(&rgid, &egid, &sgid); + if (status != 0 || rgid != 0 || egid != 0 || sgid != 0) { + perror("getresgid"); + fprintf(stderr, "%ld %ld %ld\n", (unsigned long) ruid, (unsigned long) euid, (unsigned long) suid); + exit(EXIT_FAILURE); + } + + status = setresgid(1, 1, 1); + if (status != 0) { + perror("setresgid"); + exit(EXIT_FAILURE); + } + + status = getresgid(&rgid, &egid, &sgid); + if (status != 0 || rgid != 1 || egid != 1 || sgid != 1) { + perror("getresgid"); + fprintf(stderr, "%ld %ld %ld\n", (unsigned long) rgid, (unsigned long) egid, (unsigned long) sgid); + exit(EXIT_FAILURE); + } + + if (status != 0 || rgid != 1 || egid != 1 || sgid != 1) { + perror("getresgid"); + fprintf(stderr, "%ld %ld %ld\n", (unsigned long) ruid, (unsigned long) euid, (unsigned long) suid); + exit(EXIT_FAILURE); + } + + status = setresuid(1, 1, 1); + if (status != 0) { + perror("setresuid"); + exit(EXIT_FAILURE); + } + + status = getresuid(&ruid, &euid, &suid); + if (status != 0 || ruid != 1 || euid != 1 || suid != 1) { + perror("getresuid"); + fprintf(stderr, "%ld %ld %ld\n", (unsigned long) ruid, (unsigned long) euid, (unsigned long) suid); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-82ba4ba1.sh b/5.1.0/tests/test-82ba4ba1.sh new file mode 100644 index 0000000..7d4241a --- /dev/null +++ b/5.1.0/tests/test-82ba4ba1.sh
@@ -0,0 +1,41 @@ +if [ ! -x /bin/true ] || [ -z `which id` ] || [ -z `which grep` ] || [ -z `which env` ] || [ -z `which chown` ] || [ -z `which chroot` ]; then + exit 125; +fi + +if [ `id -u` -eq 0 ]; then + exit 125; +fi + +${PROOT} -i 123:456 id -u | grep ^123$ +${PROOT} -i 123:456 id -g | grep ^456$ + +${PROOT} -i 123:456 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_UID:[[:space:]]*123$' +${PROOT} -i 123:456 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_EUID:[[:space:]]*123$' +${PROOT} -i 123:456 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_GID:[[:space:]]*456$' +${PROOT} -i 123:456 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_EGID:[[:space:]]*456$' + +! ${PROOT} -i 123:456 chown root.root /root +[ $? -eq 0 ] + +! chroot / /bin/true +EXPECTED=$? + +! ${PROOT} -i 123:456 chroot / /bin/true +[ $? -eq ${EXPECTED} ] + +! ${PROOT} -i 123:456 chroot /tmp/.. /bin/true +[ $? -eq ${EXPECTED} ] + +! ${PROOT} -i 123:456 chroot /tmp /bin/true +[ $? -eq 0 ] + +${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} -0 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_UID:[[:space:]]*0$' +${PROOT} -0 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_EUID:[[:space:]]*0$' +${PROOT} -0 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_GID:[[:space:]]*0$' +${PROOT} -0 env LD_SHOW_AUXV=1 /bin/true | grep '^AT_EGID:[[:space:]]*0$'
diff --git a/5.1.0/tests/test-88888888.c b/5.1.0/tests/test-88888888.c new file mode 100644 index 0000000..0960955 --- /dev/null +++ b/5.1.0/tests/test-88888888.c
@@ -0,0 +1,73 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> + +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); +}
diff --git a/5.1.0/tests/test-8a83376a.sh b/5.1.0/tests/test-8a83376a.sh new file mode 100644 index 0000000..dd1b4ba --- /dev/null +++ b/5.1.0/tests/test-8a83376a.sh
@@ -0,0 +1,5 @@ +if [ ! -e /bin/true ] || [ -z `which ldd` ]; then + exit 125; +fi + +${PROOT} ldd /bin/true
diff --git a/5.1.0/tests/test-8e5fa256.sh b/5.1.0/tests/test-8e5fa256.sh new file mode 100644 index 0000000..259ea37 --- /dev/null +++ b/5.1.0/tests/test-8e5fa256.sh
@@ -0,0 +1,43 @@ +if [ ! -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} -r ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ +${PROOT} -b /tmp -r ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-host$ +${PROOT} -b /tmp:/foo -r ${ROOTFS} readlink /foo/${LINK_NAME1} | grep ^/foo/ced-host$ +${PROOT} -b /tmp:/foo -r ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ + +${PROOT} -b /:/host-rootfs -r ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ +${PROOT} -b /:/host-rootfs -b /tmp:/foo -r ${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 -r ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-guest$ +${PROOT} -b /:/host-rootfs -b /tmp -r ${ROOTFS} readlink /tmp/${LINK_NAME1} | grep ^/tmp/ced-host$ +${PROOT} -b /:/host-rootfs -b /tmp:/foo -r ${ROOTFS} readlink /foo/${LINK_NAME1} | grep ^/foo/ced-host$ +${PROOT} -b /:/host-rootfs -b /tmp -r ${ROOTFS} readlink /host-rootfs/tmp/${LINK_NAME1} | grep ^/tmp/ced-host$ +${PROOT} -b /:/host-rootfs -b /tmp:/foo -r ${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 -r ${ROOTFS} symlink /bin/bar /bin/${LINK_NAME1} +${PROOT} -b /:/host-rootfs -b /tmp -w /bin -r ${ROOTFS} readlink ${LINK_NAME1} | grep ^/bin/bar$ +rm ${ROOTFS}/bin/${LINK_NAME1} + +${PROOT} -b /:/host-rootfs -b /tmp -w /tmp -r ${ROOTFS} symlink /bin/bar /tmp/${LINK_NAME1} +${PROOT} -b /:/host-rootfs -b /tmp -w /tmp -r ${ROOTFS} readlink ${LINK_NAME1} | grep ^/bin/bar$ +${PROOT} -b /:/host-rootfs -b /tmp:/foo -w /foo -r ${ROOTFS} symlink /foo/bar /foo/${LINK_NAME2} +${PROOT} -b /:/host-rootfs -b /tmp:/foo -w /foo -r ${ROOTFS} readlink ${LINK_NAME2} | grep ^/foo/bar$ +${PROOT} -b /:/host-rootfs -b /tmp -w /host-rootfs/tmp -r ${ROOTFS} readlink ${LINK_NAME2} | grep ^/foo/bar$ +rm /tmp/${LINK_NAME2} +rm /tmp/${LINK_NAME1}
diff --git a/5.1.0/tests/test-99999999.sh b/5.1.0/tests/test-99999999.sh new file mode 100644 index 0000000..f0a8c84 --- /dev/null +++ b/5.1.0/tests/test-99999999.sh
@@ -0,0 +1,52 @@ +if [ ! -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 -r ${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 -r ${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 -r ${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}
diff --git a/5.1.0/tests/test-9c07fad8.c b/5.1.0/tests/test-9c07fad8.c new file mode 100644 index 0000000..a852025 --- /dev/null +++ b/5.1.0/tests/test-9c07fad8.c
@@ -0,0 +1,16 @@ +#include <linux/unistd.h> + +int check = 0; + +static void __attribute__((constructor)) init(void) +{ + if (check > 0) + _exit(1); + + check++; +} + +int main(void) +{ + return 0; +}
diff --git a/5.1.0/tests/test-9f5eeb72.sh b/5.1.0/tests/test-9f5eeb72.sh new file mode 100644 index 0000000..79999e4 --- /dev/null +++ b/5.1.0/tests/test-9f5eeb72.sh
@@ -0,0 +1,157 @@ +if [ -z `which mkdir` ] || [ -z `which chmod` ] || [ -z `which touch` ] || [ -z `which ln` ] || [ -z `which cpio` ] || [ -z `which stat` ] || [ -z `which cat` ] || [ -z `which readlink` ] || [ -z `which mcookie` ] || [ -z `which mknod` ]; then + exit 125; +fi + +if [ ! -e $CARE ]; then + exit 125; +fi +unset PROOT + +TMP=/tmp/$(mcookie) +mkdir ${TMP} +cd ${TMP} + +export LANG=en_US.UTF-8 +mkdir a +echo "I'm a bee" > a/b +echo "I'm a sea" > a/c +chmod -w a +touch Å‚ +ln Å‚ d +ln -s dangling_symlink e +mknod f p +mkdir -p x/y +chmod -rwx x + +for BUNCH in \ + "FORMAT=/ EXTRACT=''" \ + "FORMAT=.raw EXTRACT='${CARE} -x'" \ + "FORMAT=.cpio EXTRACT='${CARE} -x'" \ + "FORMAT=.cpio.gz EXTRACT='${CARE} -x'" \ + "FORMAT=.cpio.lzo EXTRACT='${CARE} -x'" \ + "FORMAT=.tar EXTRACT='${CARE} -x'" \ + "FORMAT=.tgz EXTRACT='${CARE} -x'" \ + "FORMAT=.tar.gz EXTRACT='${CARE} -x'" \ + "FORMAT=.tzo EXTRACT='${CARE} -x'" \ + "FORMAT=.tar.lzo EXTRACT='${CARE} -x'" \ + "FORMAT=.bin EXTRACT='${CARE} -x'" \ + "FORMAT=.bin EXTRACT='sh -c'" \ + "FORMAT=.gz.bin EXTRACT='sh -c'" \ + "FORMAT=.lzo.bin EXTRACT='sh -c'" \ + "FORMAT=.cpio.bin EXTRACT='sh -c'" \ + "FORMAT=.cpio.gz.bin EXTRACT='sh -c'" \ + "FORMAT=.cpio.lzo.bin EXTRACT='sh -c'" \ + "FORMAT=.tar.bin EXTRACT='sh -c'" \ + "FORMAT=.tgz.bin EXTRACT='sh -c'" \ + "FORMAT=.tzo.bin EXTRACT='sh -c'" \ + "FORMAT=.tar.gz.bin EXTRACT='sh -c'" \ + "FORMAT=.tar.lzo.bin EXTRACT='sh -c'" +do + eval $BUNCH + CWD=${PWD} + + if echo ${FORMAT} | grep '.bin' && ${CARE} -V | grep '(.bin): no'; then + continue + fi + + # Check: permissions, unordered archive, UTF-8, hard-links + ${CARE} -o test${FORMAT} cat a/b Å‚ d a/c + + if [ -n "${EXTRACT}" ]; then + ! chmod +rwx -R test-${FORMAT}-1 + rm -fr test-${FORMAT}-1 + mkdir test-${FORMAT}-1 + cd test-${FORMAT}-1 + ${EXTRACT} ../test${FORMAT} + fi + + test -d test/rootfs/${CWD}/a + test -f test/rootfs/${CWD}/a/b + test -f test/rootfs/${CWD}/a/c + test -f test/rootfs/${CWD}/Å‚ + test -f test/rootfs/${CWD}/d + + INODE1=$(stat -c %i test/rootfs/${CWD}/d) + INODE2=$(stat -c %i test/rootfs/${CWD}/Å‚) + [ $INODE1 -eq $INODE2 ] + + PERM1=$(stat -c %a ${CWD}/a) + PERM2=$(stat -c %a test/rootfs/${CWD}/a) + [ $PERM1 -eq $PERM2 ] + + if [ -n "${EXTRACT}" ]; then + cd .. + else + ! chmod +rwx -R test + rm -fr test + fi + + # Check: last archived version wins, symlinks + ${CARE} -o test${FORMAT} sh -c 'ls a; ls a/b; ls -l e' + + if [ -n "${EXTRACT}" ]; then + ! chmod +rwx -R test-${FORMAT}-2 + rm -fr test-${FORMAT}-2 + mkdir test-${FORMAT}-2 + cd test-${FORMAT}-2 + ${EXTRACT} ../test${FORMAT} + fi + + B=$(cat test/rootfs/${CWD}/a/b) + [ x"$B" != x ] + [ "$B" = "I'm a bee" ] + + test -L test/rootfs/${CWD}/e + + F=$(readlink test/rootfs/${CWD}/e) + [ x"$F" != x ] + [ "$F" = "dangling_symlink" ] + + if [ -n "${EXTRACT}" ]; then + cd .. + else + ! chmod +rwx -R test + rm -fr test + fi + + # Check: non-regular files are archived/extractable + ${CARE} -d -p /dev -p /proc -o test${FORMAT} sh -c 'ls -l f' + + if [ -n "${EXTRACT}" ]; then + ! chmod +rwx -R test-${FORMAT}-1 + rm -fr test-${FORMAT}-1 + mkdir test-${FORMAT}-1 + cd test-${FORMAT}-1 + ${EXTRACT} ../test${FORMAT} + fi + + [ "fifo" = "$(stat -c %F test/rootfs/${CWD}/f)" ] + + if [ -n "${EXTRACT}" ]; then + cd .. + else + ! chmod +rwx -R test + rm -fr test + fi + + # Check: extractable archive + ${CARE} -o test${FORMAT} chmod -R +rwx x + + if [ -n "${EXTRACT}" ]; then + ! chmod +rwx -R test-${FORMAT}-3 + rm -fr test-${FORMAT}-3 + mkdir test-${FORMAT}-3 + cd test-${FORMAT}-3 + ${EXTRACT} ../test${FORMAT} + + cd .. + else + ! chmod +rwx -R test + rm -fr test + fi +done + +cd .. +chmod +rwx -R ${TMP} +rm -fr ${TMP} +
diff --git a/5.1.0/tests/test-a3e68988.c b/5.1.0/tests/test-a3e68988.c new file mode 100644 index 0000000..5f74412 --- /dev/null +++ b/5.1.0/tests/test-a3e68988.c
@@ -0,0 +1,114 @@ +#include <linux/auxvec.h> /* AT_*, */ +#include <stdio.h> /* printf(3), */ +#include <sys/types.h> /* open(2), */ +#include <sys/stat.h> /* open(2), */ +#include <fcntl.h> /* open(2), */ +#include <unistd.h> /* read(2), close(2), */ +#include <stdlib.h> /* exit(3), EXIT_*, realloc(3), free(3), */ + +struct auxv { + long type; + long value; +} __attribute__((packed)); + +void print_auxv(struct auxv *auxv) +{ +#define CASE(a) \ + case (a): \ + printf("%s = 0x%lx\n", #a, auxv->value); \ + break; + + switch (auxv->type) { + CASE(AT_NULL) + CASE(AT_IGNORE) + CASE(AT_EXECFD) + CASE(AT_PHDR) + CASE(AT_PHENT) + CASE(AT_PHNUM) + CASE(AT_PAGESZ) + CASE(AT_BASE) + CASE(AT_FLAGS) + CASE(AT_ENTRY) + CASE(AT_NOTELF) + CASE(AT_UID) + CASE(AT_EUID) + CASE(AT_GID) + CASE(AT_EGID) + CASE(AT_PLATFORM) + CASE(AT_HWCAP) + CASE(AT_CLKTCK) + CASE(AT_SECURE) + CASE(AT_BASE_PLATFORM) + CASE(AT_RANDOM) +#if defined(AT_HWCAP2) + CASE(AT_HWCAP2) +#endif + CASE(AT_EXECFN) +#if defined(AT_SYSINFO) + CASE(AT_SYSINFO) +#endif +#if defined(AT_SYSINFO_EHDR) + CASE(AT_SYSINFO_EHDR) +#endif + default: + printf("unknown (%ld) = 0x%lx\n", auxv->type, auxv->value); + break; + } + +#undef CASE +} + +extern char **environ; + +int main() +{ + long at_base_proc = 0; + long at_base_mem = 0; + struct auxv *auxv; + void *data = NULL; + size_t size = 0; + void **pointer; + int status; + int fd; + + for (pointer = (void **) environ; *pointer != NULL; pointer++) + /* Nothing */; + + for (auxv = (void *) ++pointer; auxv->type != AT_NULL; auxv++) { + if (auxv->type == AT_BASE) + at_base_mem = auxv->value; + + print_auxv(auxv); + } + + printf("----------------------------------------------------------------------\n"); + + fd = open("/proc/self/auxv", O_RDONLY); + if (fd < 0) + exit(EXIT_FAILURE); + +#define CHUNK_SIZE 1024 + + do { + data = realloc(data, size + CHUNK_SIZE); + if (data == NULL) + exit(EXIT_FAILURE); + + status = read(fd, data + size, CHUNK_SIZE); + size += CHUNK_SIZE; + } while (status > 0); + + for (auxv = data; auxv->type != AT_NULL; auxv++) { + if (auxv->type == AT_BASE) + at_base_proc = auxv->value; + + print_auxv(auxv); + } + + (void) close(fd); + (void) free(data); + + exit((at_base_proc != 0 && at_base_mem == at_base_proc) + ? EXIT_SUCCESS + : EXIT_FAILURE); +}
diff --git a/5.1.0/tests/test-a4d7ed70.sh b/5.1.0/tests/test-a4d7ed70.sh new file mode 100644 index 0000000..6008043 --- /dev/null +++ b/5.1.0/tests/test-a4d7ed70.sh
@@ -0,0 +1,16 @@ +if [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which ln` ] || [ -z `which ls` ] || [ -z `which rm` ] || [ -z `which cat` ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) +TMP2=/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$ + +echo OK > ${TMP2} +${PROOT} cat ${TMP}/stdin < ${TMP2} | grep ^OK$ + +rm -fr ${TMP} ${TMP2}
diff --git a/5.1.0/tests/test-a8e69d6f.c b/5.1.0/tests/test-a8e69d6f.c new file mode 100644 index 0000000..3f5f96a --- /dev/null +++ b/5.1.0/tests/test-a8e69d6f.c
@@ -0,0 +1,24 @@ +#include <unistd.h> /* syscall(2), */ +#include <stdio.h> /* perror(3), fprintf(3), */ +#include <stdlib.h> /* exit(3), */ +#include <sys/syscall.h> /* SYS_lstat, */ +#include <sys/stat.h> /* 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); +}
diff --git a/5.1.0/tests/test-aaaaaaaa.sh b/5.1.0/tests/test-aaaaaaaa.sh new file mode 100644 index 0000000..7cbfd43 --- /dev/null +++ b/5.1.0/tests/test-aaaaaaaa.sh
@@ -0,0 +1,63 @@ +if [ ! -x ${ROOTFS}/bin/true ] || [ -z `which id` ] || [ -z `which mcookie` ] || [ -z `which ln` ] || [ -z `which rm` ]; then + exit 125; +fi + +if [ `id -u` -eq 0 ]; then + exit 125; +fi + +DONT_EXIST=/$(mcookie) + +${PROOT} -r ${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}
diff --git a/5.1.0/tests/test-af062114.c b/5.1.0/tests/test-af062114.c new file mode 100644 index 0000000..f4a4b62 --- /dev/null +++ b/5.1.0/tests/test-af062114.c
@@ -0,0 +1,37 @@ +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdbool.h> + +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); +}
diff --git a/5.1.0/tests/test-b161bc0a.sh b/5.1.0/tests/test-b161bc0a.sh new file mode 100644 index 0000000..9f9bc90 --- /dev/null +++ b/5.1.0/tests/test-b161bc0a.sh
@@ -0,0 +1,6 @@ +if [ -z `which pwd` ] || [ -z `which grep` ]; then + exit 125; +fi + +${PROOT} -w /tmp/a -m /etc:/tmp/a pwd | grep '^/tmp/a$' +
diff --git a/5.1.0/tests/test-b6df3cbe.sh b/5.1.0/tests/test-b6df3cbe.sh new file mode 100644 index 0000000..3020e5e --- /dev/null +++ b/5.1.0/tests/test-b6df3cbe.sh
@@ -0,0 +1,16 @@ +if [ -z `which mcookie` ] || [ -z `which cat` ] || [ -z `which tr` ] || [ -z `which grep` ] || [ -z `which grep` ] || [ -z `which chmod` ]; then + exit 125; +fi + +TMP=$(mcookie) + +cat > /tmp/${TMP} <<EOF +#!/bin/sh + +cat /proc/\$$/cmdline +EOF + +chmod +x /tmp/${TMP} +(cd /tmp; ${PROOT} sh -c "./${TMP}") | tr '\000' ' ' | grep "^/bin/sh ./${TMP} $" + +rm /tmp/${TMP}
diff --git a/5.1.0/tests/test-b94dd86a.sh b/5.1.0/tests/test-b94dd86a.sh new file mode 100644 index 0000000..65e8743 --- /dev/null +++ b/5.1.0/tests/test-b94dd86a.sh
@@ -0,0 +1,6 @@ +if [ ! -x ${ROOTFS}/bin/true ]; then + exit 125; +fi + +${PROOT} -w /bin -r ${ROOTFS} ./true +
diff --git a/5.1.0/tests/test-bbbbbbbb.sh b/5.1.0/tests/test-bbbbbbbb.sh new file mode 100644 index 0000000..0591eb9 --- /dev/null +++ b/5.1.0/tests/test-bbbbbbbb.sh
@@ -0,0 +1,24 @@ +if [ -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}
diff --git a/5.1.0/tests/test-bdc90417.c b/5.1.0/tests/test-bdc90417.c new file mode 100644 index 0000000..fa02349 --- /dev/null +++ b/5.1.0/tests/test-bdc90417.c
@@ -0,0 +1,34 @@ +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include <unistd.h> /* execv(3), syscall(2), */ +#include <sys/syscall.h> /* SYS_*, */ +#include <sys/time.h> /* *rlimit(2), */ +#include <sys/resource.h> /* *rlimit(2), */ +#include <stdlib.h> /* EXIT_*, exit(3), */ + +int main(int argc, char *argv[]) +{ + char *const dummy_argv[] = { "test", "stage2", NULL }; + long brk1, brk2; + int 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); + } +}
diff --git a/5.1.0/tests/test-c10e2073.c b/5.1.0/tests/test-c10e2073.c new file mode 100644 index 0000000..83c4821 --- /dev/null +++ b/5.1.0/tests/test-c10e2073.c
@@ -0,0 +1,37 @@ +#include <unistd.h> /* syscall(2), */ +#include <stdio.h> /* perror(3), fprintf(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <stdlib.h> /* exit(3), */ +#include <string.h> /* strlen(3), */ +#include <sys/syscall.h> /* 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); +}
diff --git a/5.1.0/tests/test-c15999f9.sh b/5.1.0/tests/test-c15999f9.sh new file mode 100644 index 0000000..9c25b13 --- /dev/null +++ b/5.1.0/tests/test-c15999f9.sh
@@ -0,0 +1,12 @@ +if [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which test` ] || [ -z `which grep` ] || [ -z `which rm` ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) +mkdir ${TMP} + +${PROOT} -b /bin/true:${TMP}/true /bin/true +! test -e ${TMP}/true +[ $? -eq 0 ] + +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-c47aeb7d.c b/5.1.0/tests/test-c47aeb7d.c new file mode 100644 index 0000000..255c9ac --- /dev/null +++ b/5.1.0/tests/test-c47aeb7d.c
@@ -0,0 +1,33 @@ +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +void *print_hello(void *id) +{ + pthread_exit(id); +} + +int main(void) +{ + const int nb_threads = 10; + pthread_t threads[nb_threads]; + int status; + long i; + + for(i = 0; i < nb_threads; i++) { + status = pthread_create(&threads[i], NULL, print_hello, (void *) i); + if (status != 0) + exit(EXIT_FAILURE); + } + + for(i = 0; i < nb_threads; i++) { + intptr_t result; + + status = pthread_join(threads[i], (void **) &result); + if (status != 0 || (int) result != i) + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-c5a7a0f0.c b/5.1.0/tests/test-c5a7a0f0.c new file mode 100644 index 0000000..652c0e6 --- /dev/null +++ b/5.1.0/tests/test-c5a7a0f0.c
@@ -0,0 +1,93 @@ +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <pthread.h> + +static void *setuid_124(void *unused) +{ + int status; + + status = setuid(124); + if (status < 0) { + perror("setuid"); + pthread_exit((void *)-1); + } + + if (getuid() != 124) { + perror("getuid"); + pthread_exit((void *)-1); + } + + pthread_exit(NULL); +} + +static void pterror(const char *message, int error) +{ + fprintf(stderr, "%s: %s\n", message, strerror(error)); +} + +int main(void) +{ + pthread_t thread; + int child_status; + void *result; + int status; + + switch(fork()) { + case -1: + perror("fork"); + exit(EXIT_FAILURE); + + case 0: /* child */ + status = setuid(123); + if (status < 0) { + perror("setuid"); + exit(EXIT_FAILURE); + } + + if (getuid() != 123) { + perror("getuid"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + + default: /* parent */ + break; + } + + status = wait(&child_status); + if (status < 0 || child_status != 0) { + perror("wait()"); + exit(EXIT_FAILURE); + } + + if (getuid() != 0) { + fprintf(stderr, "getuid() == %d != 0\n", getuid()); + exit(EXIT_FAILURE); + } + + status = pthread_create(&thread, NULL, setuid_124, NULL); + 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) { + fprintf(stderr, "result != NULL\n"); + exit(EXIT_FAILURE); + } + + if (getuid() != 124) { + fprintf(stderr, "getuid() == %d != 124\n", getuid()); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-c68d18dc.sh b/5.1.0/tests/test-c68d18dc.sh new file mode 100644 index 0000000..6c1727c --- /dev/null +++ b/5.1.0/tests/test-c68d18dc.sh
@@ -0,0 +1,29 @@ +if [ -z `which mkdir` ] || [ -z `which rm` ] || [ -z `which mcookie` ] || [ -z `which chmod` ] || [ -z `which ln` ]; then + exit 125; +fi + +if [ ! -e $CARE ]; then + exit 125; +fi +unset PROOT + +SYMLINK=/tmp/$(mcookie) +FOLDER=/tmp/$(mcookie) +SCRIPT=${FOLDER}/script.sh +ARCHIVE=/tmp/$(mcookie)/ + +mkdir ${FOLDER} + +echo "true" > ${SCRIPT} +chmod +x ${SCRIPT} + +ln -s ${FOLDER} ${SYMLINK} + +cd ${SYMLINK} +${CARE} -r ${FOLDER} -o ${ARCHIVE} sh ./script.sh + +test -e ${ARCHIVE}/rootfs${SCRIPT} + +${ARCHIVE}/re-execute.sh + +rm -fr ${SYMLINK} ${FOLDER} ${ARCHIVE}
diff --git a/5.1.0/tests/test-c6b77b77.mk b/5.1.0/tests/test-c6b77b77.mk new file mode 100644 index 0000000..ebdd56e --- /dev/null +++ b/5.1.0/tests/test-c6b77b77.mk
@@ -0,0 +1,5 @@ +SHELL=/bin/bash +FOO:=$(shell test -e /dev/null && echo OK) + +all: + @/usr/bin/test -n "$(FOO)"
diff --git a/5.1.0/tests/test-c6b77b77.sh b/5.1.0/tests/test-c6b77b77.sh new file mode 100644 index 0000000..8d9797c --- /dev/null +++ b/5.1.0/tests/test-c6b77b77.sh
@@ -0,0 +1,5 @@ +if [ -z `which make` ]; then + exit 125; +fi + +${PROOT} make -f ${PWD}/test-c6b77b77.mk
diff --git a/5.1.0/tests/test-careauth.sh b/5.1.0/tests/test-careauth.sh new file mode 100644 index 0000000..dd4f986 --- /dev/null +++ b/5.1.0/tests/test-careauth.sh
@@ -0,0 +1,26 @@ +if [ -z `which env` ] || [ -z `which rm` ] || [ -z `which mcookie` ] || [ -z `which true` ]; then + exit 125; +fi + +if [ ! -e $CARE ]; then + exit 125; +fi +unset PROOT + +TMP=$(mcookie) + +cd /tmp +env ICEAUTHORITY= ${CARE} -o ${TMP} true +${CARE} -x ./${TMP} +./${TMP}/re-execute.sh +rm -fr ${TMP} + +env XAUTHORITY= ${CARE} -o ${TMP} true +${CARE} -x ./${TMP} +./${TMP}/re-execute.sh +rm -fr ${TMP} + +${CARE} -o ${TMP} -p ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../ true +${CARE} -x ./${TMP} +./${TMP}/re-execute.sh +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-careexit.sh b/5.1.0/tests/test-careexit.sh new file mode 100644 index 0000000..ff1b543 --- /dev/null +++ b/5.1.0/tests/test-careexit.sh
@@ -0,0 +1,25 @@ +if [ -z `which cpio` ] || [ -z `which rm` ] || [ -z `which mcookie` ]; then + exit 125; +fi + +if [ ! -e $CARE ]; then + exit 125; +fi +unset PROOT + +TMP=/tmp/$(mcookie) + +${CARE} -o ${TMP}.cpio sh -c 'exit 0' + +cd /tmp +cpio -idmuvF ${TMP}.cpio + +${TMP}/re-execute.sh + +set +e +${TMP}/re-execute.sh sh -c 'exit 132' +status=$? +set -e +[ $status -eq 132 ] + +rm -f ${TMP}.cpio
diff --git a/5.1.0/tests/test-carehwcp.sh b/5.1.0/tests/test-carehwcp.sh new file mode 100644 index 0000000..926970a --- /dev/null +++ b/5.1.0/tests/test-carehwcp.sh
@@ -0,0 +1,15 @@ +if [ ! -x /bin/true ] || [ -z `which grep` ] || [ -z `which env` ] || [ -z `which mcookie`] || [ -z `which rm` ]; then + exit 125; +fi + +if [ ! -e $CARE ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) + +${CARE} -o ${TMP}/ env LD_SHOW_AUXV=1 true | grep '^AT_HWCAP:[[:space:]]*0$' +${TMP}/re-execute.sh | grep '^AT_HWCAP:[[:space:]]*0$' +${TMP}/re-execute.sh env LD_SHOW_AUXV=1 true | grep '^AT_HWCAP:[[:space:]]*0$' + +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-carequot.sh b/5.1.0/tests/test-carequot.sh new file mode 100644 index 0000000..da764b4 --- /dev/null +++ b/5.1.0/tests/test-carequot.sh
@@ -0,0 +1,18 @@ +if [ -z `which env` ] || [ -z `which rm` ] || [ -z `which mcookie` ] || [ -z `which true` ]; then + exit 125; +fi + +if [ ! -e $CARE ]; then + exit 125; +fi +unset PROOT + +TMP=/tmp/$(mcookie) + +env 'COMP_WORDBREAKS= +"'\''><;|&(:' ${CARE} -o ${TMP}.raw true + +cd /tmp; ${CARE} -x ${TMP}.raw +${TMP}/re-execute.sh + +rm -fr ${TMP}.raw ${TMP}
diff --git a/5.1.0/tests/test-cb1143ab.sh b/5.1.0/tests/test-cb1143ab.sh new file mode 100644 index 0000000..60da868 --- /dev/null +++ b/5.1.0/tests/test-cb1143ab.sh
@@ -0,0 +1,58 @@ +if [ -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}
diff --git a/5.1.0/tests/test-cccccccc.sh b/5.1.0/tests/test-cccccccc.sh new file mode 100644 index 0000000..c528312 --- /dev/null +++ b/5.1.0/tests/test-cccccccc.sh
@@ -0,0 +1,14 @@ +if [ -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}
diff --git a/5.1.0/tests/test-cdd39012.sh b/5.1.0/tests/test-cdd39012.sh new file mode 100644 index 0000000..1e86c5c --- /dev/null +++ b/5.1.0/tests/test-cdd39012.sh
@@ -0,0 +1,11 @@ +if [ ! -x ${ROOTFS}/bin/ptrace ] || [ ! -x ${ROOTFS}/bin/ptrace-2 ] || [ ! -x ${ROOTFS}/bin/true ]; then + exit 125; +fi + +${PROOT} -r ${ROOTFS} ptrace +${PROOT} -r ${ROOTFS} ptrace 2 + +${PROOT} -r ${ROOTFS} ptrace-2 /bin/true + +${PROOT} -r ${ROOTFS} ptrace-2 /bin/fork-wait +${PROOT} -r ${ROOTFS} ptrace-2 /bin/fork-wait 2
diff --git a/5.1.0/tests/test-cea75343.sh b/5.1.0/tests/test-cea75343.sh new file mode 100644 index 0000000..0fb5b91 --- /dev/null +++ b/5.1.0/tests/test-cea75343.sh
@@ -0,0 +1,40 @@ +if [ -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}
diff --git a/5.1.0/tests/test-commmmmm.sh b/5.1.0/tests/test-commmmmm.sh new file mode 100644 index 0000000..9bae7f3 --- /dev/null +++ b/5.1.0/tests/test-commmmmm.sh
@@ -0,0 +1,25 @@ +if [ -z `which mcookie` ] || [ -z `which cat` ] || [ -z `which grep` ] || [ -z `which chmod` ] || [ -z `which cut` ] || [ -z `which rm` ] || [ -z `which ln` ] || [ -z `which env` ]; then + exit 125; +fi + +TMP=$(mcookie) +TMP2=$(echo ${TMP} | cut -b 1-15) +TMP3=$(mcookie) +TMP4=$(echo ${TMP3} | cut -b 1-15) + +${PROOT} cat /proc/self/comm | grep cat +${PROOT} $(which cat) /proc/self/comm | grep cat + +echo '#!/bin/sh' > /tmp/${TMP} +chmod +x /tmp/${TMP} + +# TODO: (cd /tmp; ${PROOT} env LD_SHOW_AUXV=1 ./${TMP}) | grep ^AT_EXECFN:[[:space:]]*./${TMP}$ + +echo 'cat /proc/$$/comm' >> /tmp/${TMP} + +${PROOT} /tmp/${TMP} | grep ^${TMP2}$ + +ln -s /tmp/${TMP} /tmp/${TMP3} +${PROOT} /tmp/${TMP3} /proc/self/comm | grep ^${TMP4}$ + +rm -f /tmp/${TMP} /tmp/${TMP3}
diff --git a/5.1.0/tests/test-d1be631a.sh b/5.1.0/tests/test-d1be631a.sh new file mode 100644 index 0000000..c3cb425 --- /dev/null +++ b/5.1.0/tests/test-d1be631a.sh
@@ -0,0 +1,13 @@ +if [ -z `which mknod`] || [ `id -u` -eq 0 ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) + +! ${PROOT} mknod ${TMP} b 1 1 +[ $? -eq 0 ] + +! ${PROOT} -i 123:456 mknod ${TMP} b 1 1 +[ $? -eq 0 ] + +${PROOT} -0 mknod ${TMP} b 1 1
diff --git a/5.1.0/tests/test-d1da0d8d.sh b/5.1.0/tests/test-d1da0d8d.sh new file mode 100644 index 0000000..8dd6931 --- /dev/null +++ b/5.1.0/tests/test-d1da0d8d.sh
@@ -0,0 +1,6 @@ +if [ ! -x ${ROOTFS}/bin/readlink ] || [ ! -e /proc/self/cwd ] || [ -z `which grep` ]; then + exit 125; +fi + +${PROOT} -m /proc -m /tmp:/asym -w /asym -r ${ROOTFS} /bin/readlink /proc/self/cwd | grep '^/asym$' +${PROOT} -m /proc -m /tmp:/asym -w /tmp -r ${ROOTFS} /bin/readlink /proc/self/cwd | grep '^/tmp$'
diff --git a/5.1.0/tests/test-d2175fc3.sh b/5.1.0/tests/test-d2175fc3.sh new file mode 100644 index 0000000..d43b266 --- /dev/null +++ b/5.1.0/tests/test-d2175fc3.sh
@@ -0,0 +1,9 @@ +if [ ! -x ${ROOTFS}/bin/readlink ] || [ -z `which grep` ]; then + exit 125; +fi + +${PROOT} -r ${ROOTFS} /bin/readlink /bin/abs-true | grep '^/bin/true$' +${PROOT} -r ${ROOTFS} /bin/readlink /bin/rel-true | grep '^\./true$' + +${PROOT} -b /:/host-rootfs -r ${ROOTFS} /bin/readlink /bin/abs-true | grep '^/bin/true$' +${PROOT} -b /:/host-rootfs -r ${ROOTFS} /bin/readlink /bin/rel-true | grep '^./true$'
diff --git a/5.1.0/tests/test-d2175fc4.c b/5.1.0/tests/test-d2175fc4.c new file mode 100644 index 0000000..07e1d8b --- /dev/null +++ b/5.1.0/tests/test-d2175fc4.c
@@ -0,0 +1,31 @@ +#include <unistd.h> /* syscall(2), */ +#include <stdio.h> /* perror(3), fprintf(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <stdlib.h> /* exit(3), */ +#include <strings.h> /* bzero(3), */ +#include <sys/syscall.h> /* 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); +}
diff --git a/5.1.0/tests/test-d92b57ca.sh b/5.1.0/tests/test-d92b57ca.sh new file mode 100644 index 0000000..ddb108a --- /dev/null +++ b/5.1.0/tests/test-d92b57ca.sh
@@ -0,0 +1,5 @@ +if [ -z `which env` ] || [ -z `which true` ]; then + exit 125; +fi + +env PROOT_NO_SUBRECONF=1 ${PROOT} ${PROOT} -v 1 true
diff --git a/5.1.0/tests/test-dddddddd.sh b/5.1.0/tests/test-dddddddd.sh new file mode 100644 index 0000000..122cc50 --- /dev/null +++ b/5.1.0/tests/test-dddddddd.sh
@@ -0,0 +1,36 @@ +if [ -z `which mcookie` ] || [ -z `which rm` ] || [ -z `which ln` ] || [ -z `which realpath` ] || [ -z `which mkdir` ] || [ -z `which rmdir` ]; then + exit 125; +fi + +CHECK1=$(realpath -e /proc/self/exe) +CHECK2=$(realpath /proc/self/exe) + +if [ "${CHECK1}" != "${CHECK2}" ]; then + exit 125; +fi + +TMP="/tmp/$(mcookie)" +TMP2="/tmp/$(mcookie)" + +RMDIR=$(realpath -e $(which rmdir)) +MKDIR=$(realpath -e $(which mkdir)) + +export LANG=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
diff --git a/5.1.0/tests/test-de756935.sh b/5.1.0/tests/test-de756935.sh new file mode 100644 index 0000000..8cafa40 --- /dev/null +++ b/5.1.0/tests/test-de756935.sh
@@ -0,0 +1,12 @@ +if [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which bash` ] || [ -z `which grep` ] || [ -z `which rm` ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) + +mkdir -p ${TMP} +cd ${TMP} + +${PROOT} -b ${PWD}:/foo -w /foo bash -c 'pwd' | grep '^/foo$' + +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-df4de4db.sh b/5.1.0/tests/test-df4de4db.sh new file mode 100644 index 0000000..8b362fe --- /dev/null +++ b/5.1.0/tests/test-df4de4db.sh
@@ -0,0 +1,10 @@ +if [ ! -x ${ROOTFS}/bin/fork-wait ] || [ -z `which strace` ]; then + exit 125; +fi + +${PROOT} strace ${ROOTFS}/bin/fork-wait +${PROOT} strace ${ROOTFS}/bin/fork-wait 2 + +${PROOT} strace -f ${ROOTFS}/bin/fork-wait +${PROOT} strace -f ${ROOTFS}/bin/fork-wait 2 +
diff --git a/5.1.0/tests/test-dfb0c3b6.sh b/5.1.0/tests/test-dfb0c3b6.sh new file mode 100644 index 0000000..e0013e0 --- /dev/null +++ b/5.1.0/tests/test-dfb0c3b6.sh
@@ -0,0 +1,27 @@ +if [ -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}
diff --git a/5.1.0/tests/test-e87b34ae.c b/5.1.0/tests/test-e87b34ae.c new file mode 100644 index 0000000..d99c029 --- /dev/null +++ b/5.1.0/tests/test-e87b34ae.c
@@ -0,0 +1,34 @@ +#include <unistd.h> /* syscall(2), fork(2), usleep(3), */ +#include <stdio.h> /* perror(3), printf(3), */ +#include <limits.h> /* PATH_MAX, */ +#include <stdlib.h> /* exit(3), */ +#include <sys/syscall.h> /* SYS_readlink, SYS_getcwd, */ +#include <errno.h> /* 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); +}
diff --git a/5.1.0/tests/test-e87ca6ca.sh b/5.1.0/tests/test-e87ca6ca.sh new file mode 100644 index 0000000..0056d2e --- /dev/null +++ b/5.1.0/tests/test-e87ca6ca.sh
@@ -0,0 +1,18 @@ +if [ -z `which mcookie` ] || [ -z `which cp` ] || [ -z `which true` ] || [ -z `which setcap` ] || [ -z `which rm` ]; then + exit 125; +fi + +if [ `id -u` -eq 0 ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) + +cp $(which true) ${TMP} + +! ${PROOT} -i 123:456 setcap cap_setuid+ep ${TMP} +[ $? -eq 0 ] + +${PROOT} -0 setcap cap_setuid+ep ${TMP} + +rm -f ${TMP}
diff --git a/5.1.0/tests/test-e940896f.sh b/5.1.0/tests/test-e940896f.sh new file mode 100644 index 0000000..fbc07a1 --- /dev/null +++ b/5.1.0/tests/test-e940896f.sh
@@ -0,0 +1,22 @@ +if [ ! -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}
diff --git a/5.1.0/tests/test-e99993c8.sh b/5.1.0/tests/test-e99993c8.sh new file mode 100644 index 0000000..91cd8b9 --- /dev/null +++ b/5.1.0/tests/test-e99993c8.sh
@@ -0,0 +1,8 @@ +if [ -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$
diff --git a/5.1.0/tests/test-eddeba0e.sh b/5.1.0/tests/test-eddeba0e.sh new file mode 100644 index 0000000..f3bf1e3 --- /dev/null +++ b/5.1.0/tests/test-eddeba0e.sh
@@ -0,0 +1,5 @@ +if ! `which pwd` -P || [ -z `which grep` ]; then + exit 125; +fi + +${PROOT} pwd -P | grep "^$PWD$"
diff --git a/5.1.0/tests/test-f7089d4f.sh b/5.1.0/tests/test-f7089d4f.sh new file mode 100644 index 0000000..d90c88e --- /dev/null +++ b/5.1.0/tests/test-f7089d4f.sh
@@ -0,0 +1,5 @@ +if [ -z `which timeout` ] || [ -z `which msgmerge` ] || [ ! -e /dev/null ]; then + exit 125; +fi + +timeout 5s ${PROOT} msgmerge -q /dev/null /dev/null
diff --git a/5.1.0/tests/test-fa205b56.c b/5.1.0/tests/test-fa205b56.c new file mode 100644 index 0000000..94c814d --- /dev/null +++ b/5.1.0/tests/test-fa205b56.c
@@ -0,0 +1,37 @@ +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define NUM_THREADS 5 + +void *exec(void *id) +{ + char *const argv[] = { "true", NULL }; + + if ((long) id == NUM_THREADS - 1) + execve("/usr/bin/true", argv, NULL); + else + sleep(50); + + pthread_exit(NULL); +} + +int main() +{ + pthread_t threads[NUM_THREADS]; + int status; + long i; + + exit(125); /* NYI */ + + for(i = 0; i < NUM_THREADS ; i++) { + status = pthread_create(&threads[i], NULL, exec, (void *) i); + if (status) + exit(EXIT_FAILURE); + sleep(1); + } + + sleep(50); + exit(EXIT_FAILURE); +}
diff --git a/5.1.0/tests/test-fbca9cc2.sh b/5.1.0/tests/test-fbca9cc2.sh new file mode 100644 index 0000000..a35b550 --- /dev/null +++ b/5.1.0/tests/test-fbca9cc2.sh
@@ -0,0 +1,8 @@ +if [ -z `which strace` ] || [ -z `which true` ] || [ -z `which grep` ] || [ -z `which wc` ]; then + exit 125; +fi + +${PROOT} strace -e trace=execve true 2>&1 | grep '^execve.*= 0$' + +RESULT=$(${PROOT} strace -e trace=execve true 2>&1 | grep '^execve' | wc -l) +test "${RESULT}" = "1"
diff --git a/5.1.0/tests/test-fdf487a0.c b/5.1.0/tests/test-fdf487a0.c new file mode 100644 index 0000000..964158b --- /dev/null +++ b/5.1.0/tests/test-fdf487a0.c
@@ -0,0 +1,31 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +int main() +{ + int fd; + + fd = openat(0, "foo", O_RDONLY); + if (fd >= 0 || errno != ENOTDIR) { + printf("1. %d %d\n", fd, (int) errno); + exit(EXIT_FAILURE); + } + + fd = openat(0, "", O_RDONLY); + if (fd >= 0 || errno != ENOENT) { + printf("2. %d %d\n", fd, (int) errno); + exit(EXIT_FAILURE); + } + + fd = openat(0, NULL, O_RDONLY); + if (fd >= 0 || errno != EFAULT) { + printf("3. %d %d\n", fd, (int) errno); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-ffffffff.sh b/5.1.0/tests/test-ffffffff.sh new file mode 100644 index 0000000..c003c3f --- /dev/null +++ b/5.1.0/tests/test-ffffffff.sh
@@ -0,0 +1,12 @@ +if [ -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 + +${PROOT} -i 123:456 stat -c %u:%g ${TMP} | grep 123:456 + +rm ${TMP}
diff --git a/5.1.0/tests/test-gggggggg.sh b/5.1.0/tests/test-gggggggg.sh new file mode 100644 index 0000000..af511de --- /dev/null +++ b/5.1.0/tests/test-gggggggg.sh
@@ -0,0 +1,19 @@ +if [ -z `which mcookie` ] || [ -z `which env` ] || [ -z `which mkdir` ] || [ -z `which rm` ] || [ ! -x ${ROOTFS}/bin/readdir ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) +mkdir ${TMP} + +! env PROOT_DONT_POLLUTE_ROOTFS=1 ${PROOT} -b /bin:${TMP}/dont/create ${ROOTFS}/bin/readdir ${TMP} | grep -w dont +[ $? -eq 0 ] + +env PROOT_DONT_POLLUTE_ROOTFS=1 ${PROOT} -b /bin:${TMP}/dont/create test -e ${TMP}/dont + +${PROOT} -b /bin:${TMP}/dont/create test -e ${TMP}/dont + +! test -e ${TMP}/dont +[ $? -eq 0 ] + +chmod +rx -R ${TMP} +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-hhhhhhhh.sh b/5.1.0/tests/test-hhhhhhhh.sh new file mode 100644 index 0000000..5f89cb3 --- /dev/null +++ b/5.1.0/tests/test-hhhhhhhh.sh
@@ -0,0 +1,23 @@ +if [ ! -x ${ROOTFS}/bin/true ] || [ -h /bin/true ] || [ -h /bin ] || [ -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 -r ${ROOTFS}/${TMP}/${A} +! ln -s ${TMP}/${A} -r ${ROOTFS}/${TMP}/${B} + +if [ ! -e ${ROOTFS}/${TMP}/${A} ]; then + exit 125; +fi + +env PATH=${TMP} ${PROOT} -r ${ROOTFS} ${B} + +rm -f ${TMP}/${B} # just in case it also exists in the host env. +${PROOT} -r ${ROOTFS} /${TMP}/${B} + +rm -fr ${ROOTFS}/${TMP}
diff --git a/5.1.0/tests/test-iiiiiiii.c b/5.1.0/tests/test-iiiiiiii.c new file mode 100644 index 0000000..f772a34 --- /dev/null +++ b/5.1.0/tests/test-iiiiiiii.c
@@ -0,0 +1,49 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <stdlib.h> +#include <string.h> + +int main(void) +{ + int result; + int status; + char *path; + int fd; + + path = strdup("/tmp/proot-test-iiiiiiii-XXXXXX"); + if (path == NULL) { + result = 125; + goto end; + } + + mktemp(path); + if (path[0] == '\0') { + result = 125; + goto end; + } + + status = symlink("/this_shall_not_exist_outside_proot", path); + if (status < 0) { + result = 125; + goto end; + } + + /* For faccessat(2) and fchmodat(2) syscalls, the fourth + * parameter is *not* used by the kernel, only the libc uses + * it. As a consequence, PRoot shall ignore this flag. + * + * To be sure this parameter is really ignored by PRoot, we + * set it to NOFOLLOW when performing a direct faccessat(2) to + * a symlink which is broken from the host point-of-view, but + * valid from a guest point-of-view. When PRoot does not + * honor this flag, the faccessat(2) is performed against the + * referee anyway. + */ + status = syscall(SYS_faccessat, AT_FDCWD, path, X_OK, AT_SYMLINK_NOFOLLOW); + result = (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE); + +end: + (void) unlink(path); + exit(result); +}
diff --git a/5.1.0/tests/test-kkkkkkkk.c b/5.1.0/tests/test-kkkkkkkk.c new file mode 100644 index 0000000..2747c11 --- /dev/null +++ b/5.1.0/tests/test-kkkkkkkk.c
@@ -0,0 +1,142 @@ +#include <unistd.h> /* syscall(2), sysconf(3), */ +#include <sys/syscall.h> /* SYS_brk, */ +#include <stdio.h> /* puts(3), */ +#include <stdlib.h> /* exit(3), EXIT_*, */ +#include <stdint.h> /* uint*_t, */ +#include <sys/mman.h> /* mmap(2), MAP_*, */ +#include <string.h> /* memset(3), */ + +int main() +{ + int exit_status = EXIT_SUCCESS; + uint8_t *current_brk = 0; + uint8_t *initial_brk; + uint8_t *new_brk; + uint8_t *old_brk; + int failure = 0; + long page_size; + int i; + + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size <= 0) + return 125; + + void test_brk(int increment, int expected_result) { + new_brk = (uint8_t *) syscall(SYS_brk, current_brk + increment); + if ((new_brk == current_brk) == expected_result) + failure = 1; + current_brk = (uint8_t *) syscall(SYS_brk, 0); + } + + void test_result() { + if (!failure) + puts("OK"); + else { + puts("failure"); + exit_status = EXIT_FAILURE; + } + } + + void test_title(const char *title) { + failure = 0; + printf("%-45s : ", title); + fflush(stdout); + } + + test_title("Initialization"); + test_brk(0, 1); + initial_brk = current_brk; + test_result(); + + test_title("Don't set the \"brk\" below its initial value"); + test_brk(page_size, 1); + test_brk(-2 * page_size, 0); + test_brk(-page_size, 1); + test_result(); + + test_title("Don't overlap \"brk\" pages"); + test_brk(page_size, 1); + test_brk(page_size, 1); + test_result(); + + /* Preparation for the test "Re-allocated heap is initialized". */ + old_brk = current_brk - page_size; + memset(old_brk, 0xFF, page_size); + + test_title("Don't allocate the same \"brk\" page twice"); + test_brk(-page_size, 1); + test_brk(page_size, 1); + test_result(); + + test_title("Re-allocated \"brk\" pages are initialized"); + for (i = 0; i < page_size; i++) { + if (old_brk[i] != 0) { + printf("(index = %d, value = 0x%x) ", i, old_brk[i]); + failure = 1; + break; + } + } + test_result(); + +#if 0 + test_title("Don't allocate \"brk\" pages over \"mmap\" pages"); + new_brk = mmap(current_brk, page_size / 2, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (new_brk == (void *) -1) + puts("unknown"); + else { + test_brk(page_size, 0); + test_result(); + } +#endif + + test_title("All \"brk\" pages are writable (please wait)"); +#if 0 + if (munmap(current_brk, page_size / 2) != 0) + puts("unknown"); + else { +#endif + while (current_brk - initial_brk < 512*1024*1024UL) { + old_brk = current_brk; + + test_brk(page_size, -1); + if (old_brk == current_brk) + break; + + for (i = 0; i < page_size; i++) + old_brk[i] = 0xAA; + } + test_result(); +#if 0 + } +#endif + + test_title("Maximum size of the heap >= 1MB"); + failure = (current_brk - initial_brk) < 1024 * 1024; + test_result(); + + test_title("All \"brk\" pages are cleared (please wait)"); + test_brk(initial_brk - current_brk, 1); + if (current_brk != initial_brk) + puts("unknown"); + else { + while (current_brk - initial_brk < 1024*1024*1024UL) { + old_brk = current_brk; + + test_brk(3 * page_size, -1); + if (old_brk == current_brk) + break; + + for (i = 0; i < 3 * page_size; i++) { + if (old_brk[i] != 0) { + printf("(index = %d, value = 0x%x) ", i, old_brk[i]); + failure = 1; + goto end; + } + } + } + end: + test_result(); + } + + exit(exit_status); +}
diff --git a/5.1.0/tests/test-mmmmmmmm.sh b/5.1.0/tests/test-mmmmmmmm.sh new file mode 100644 index 0000000..94c380a --- /dev/null +++ b/5.1.0/tests/test-mmmmmmmm.sh
@@ -0,0 +1,15 @@ +if [ -z `which mcookie` ] || [ -z `which rmdir` ] || [ -z `which mkdir` ]; then + exit 125; +fi + +TMP=$(mcookie) +cd /tmp + +${PROOT} mkdir ./${TMP} +${PROOT} rmdir ./${TMP} + +${PROOT} mkdir ${TMP}/ +${PROOT} rmdir ${TMP}/ + +${PROOT} mkdir ./${TMP}/ +${PROOT} rmdir ./${TMP}/
diff --git a/5.1.0/tests/test-nnnnnnnn.c b/5.1.0/tests/test-nnnnnnnn.c new file mode 100644 index 0000000..723186c --- /dev/null +++ b/5.1.0/tests/test-nnnnnnnn.c
@@ -0,0 +1,81 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <stdlib.h> +#include <strings.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +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); +}
diff --git a/5.1.0/tests/test-oooooooo.c b/5.1.0/tests/test-oooooooo.c new file mode 100755 index 0000000..98cf0d9 --- /dev/null +++ b/5.1.0/tests/test-oooooooo.c
@@ -0,0 +1,42 @@ +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> + +int main(void) +{ + int i; + + for (i = 0; i < 999; i++) { + int status; + int fds[2]; + pid_t pid; + + status = pipe(fds); + if (status < 0) { + perror("pipe"); + break; + } + + pid = fork(); + switch (pid) { + case -1: + perror("fork"); + exit(EXIT_FAILURE); + + case 0: /* child */ + do status = write(fds[1], "!", 1); while (status > 0); + perror("write"); + exit(EXIT_FAILURE); + + default: /* parent */ + status = kill(pid, SIGKILL); + if (status < 0) { + perror("kill"); + exit(EXIT_FAILURE); + } + } + } + + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-pppppppp.sh b/5.1.0/tests/test-pppppppp.sh new file mode 100644 index 0000000..4aacdbe --- /dev/null +++ b/5.1.0/tests/test-pppppppp.sh
@@ -0,0 +1,18 @@ +if [ -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} +
diff --git a/5.1.0/tests/test-proocare.sh b/5.1.0/tests/test-proocare.sh new file mode 100644 index 0000000..7f69416 --- /dev/null +++ b/5.1.0/tests/test-proocare.sh
@@ -0,0 +1,27 @@ +if [ ! -e /boot ] || [ -z `which mcookie` ] || [ -z `which ln` ] || [ -z `which mkdir` ] || [ -z `which rm` ]; then + exit 125; +fi + +if [ ! -e $CARE ]; then + exit 125; +fi +unset PROOT + +TMP_PROOT=/tmp/$(mcookie) +TMP_OUTPUT=/tmp/$(mcookie)/ + +ln -s ${CARE} ${TMP_PROOT} +mkdir ${TMP_OUTPUT} + +${TMP_PROOT} -b /boot:/toob ${CARE} -o ${TMP_OUTPUT}/ ls /toob + +test -e ${TMP_OUTPUT}/rootfs/toob + +! test -e ${TMP_OUTPUT}/rootfs/boot +[ $? -eq 0 ] + +${TMP_OUTPUT}/re-execute.sh + +rm -fr ${TMP_PROOT} ${TMP_OUTPUT} + +
diff --git a/5.1.0/tests/test-ptrace00.c b/5.1.0/tests/test-ptrace00.c new file mode 100644 index 0000000..6a17239 --- /dev/null +++ b/5.1.0/tests/test-ptrace00.c
@@ -0,0 +1,85 @@ +/* Extracted from strace-4.8/strace.c:test_ptrace_setoptions_for_all. */ + +#include <sys/ptrace.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <stdbool.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> + +int main(void) +{ + bool does_work = false; + int status; + pid_t pid; + + switch(pid = fork()) { + case -1: + perror("fork"); + exit(EXIT_FAILURE); + + case 0: + status = ptrace(PTRACE_TRACEME, 0, 0, 0); + if (status < 0) { + perror("ptrace(PTRACE_TRACEME)"); + exit(EXIT_FAILURE); + } + + kill(getpid(), SIGSTOP); + exit(EXIT_SUCCESS); + + default: + break; + } + + while (1) { + fprintf(stderr, ">>> pid = wait(&status)\n"); + pid = wait(&status); + if (pid < 0) { + perror("wait"); + exit(EXIT_FAILURE); + } + else if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) { + fprintf(stderr, ">>> EXITSTATUS(status) == 0\n"); + break; + } + fprintf(stderr, "WEXITSTATUS != 0\n"); + exit(EXIT_FAILURE); + } + else if (WIFSIGNALED(status)) { + fprintf(stderr, "WIFSIGNALED\n"); + exit(EXIT_FAILURE); + } + else if (!WIFSTOPPED(status)) { + fprintf(stderr, "!WIFSTOPPED\n"); + exit(EXIT_FAILURE); + } + else if (WSTOPSIG(status) == SIGSTOP) { + fprintf(stderr, ">>> ptrace(PTRACE_SETOPTIONS, ...)\n"); + status = ptrace(PTRACE_SETOPTIONS, pid, 0, + PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC); + if (status < 0) { + perror("ptrace(PTRACE_SETOPTIONS)"); + exit(EXIT_FAILURE); + } + } + else if (WSTOPSIG(status) == (SIGTRAP | 0x80)) { + fprintf(stderr, ">>> does_work = true\n"); + does_work = true; + } + + fprintf(stderr, ">>> ptrace(PTRACE_SYSCALL, ...)\n"); + status = ptrace(PTRACE_SYSCALL, pid, 0, 0); + if (status < 0) { + perror("ptrace(PTRACE_SYSCALL)"); + exit(EXIT_FAILURE); + } + } + + fprintf(stderr, ">>> exit(...)\n"); + exit(does_work ? EXIT_SUCCESS : EXIT_FAILURE); +}
diff --git a/5.1.0/tests/test-ptrace01.c b/5.1.0/tests/test-ptrace01.c new file mode 100644 index 0000000..6fe7113 --- /dev/null +++ b/5.1.0/tests/test-ptrace01.c
@@ -0,0 +1,45 @@ +#include <unistd.h> /* fork(2), */ +#include <stdio.h> /* perror(3), fprintf(3), */ +#include <stdlib.h> /* exit(3), */ +#include <sys/ptrace.h> /* ptrace(2), */ +#include <sys/types.h> /* waitpid(2), */ +#include <sys/wait.h> /* waitpid(2), */ + +int main(void) +{ + int child_status, status; + pid_t pid; + + pid = fork(); + switch (pid) { + case -1: + perror("fork()"); + exit(EXIT_FAILURE); + + case 0: /* child */ + status = ptrace(PTRACE_TRACEME, 0, NULL, NULL); + if (status < 0) { + perror("ptrace(TRACEME)"); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); + + default: /* parent */ + status = waitpid(pid, &child_status, 0); + if (status < 0) { + perror("waitpid()"); + exit(EXIT_FAILURE); + } + + if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) { + perror("unexpected child status\n"); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); + } + + /* Unreachable. */ + exit(EXIT_FAILURE); +}
diff --git a/5.1.0/tests/test-rrrrrrrr.sh b/5.1.0/tests/test-rrrrrrrr.sh new file mode 100644 index 0000000..8fb3f39 --- /dev/null +++ b/5.1.0/tests/test-rrrrrrrr.sh
@@ -0,0 +1,7 @@ +if [ ! -x ${ROOTFS}/bin/readlink ] || [ -z `which realpath` ] || [ -z `which grep` ]; then + exit 125; +fi + +RESULT=$(realpath ${ROOTFS}) + +${PROOT} -b /proc -r ${ROOTFS} readlink /proc/self/root | grep ^${RESULT}$
diff --git a/5.1.0/tests/test-ssssssss.c b/5.1.0/tests/test-ssssssss.c new file mode 100644 index 0000000..3431be1 --- /dev/null +++ b/5.1.0/tests/test-ssssssss.c
@@ -0,0 +1,55 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <strings.h> +#include <stdio.h> +#include <assert.h> + +int main() +{ + struct sockaddr_un sockaddr; + socklen_t socklen; + char *sockname; + mode_t mask; + int status; + int fd; + + sockname = strdup("proot-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXXXXXX"); + if (sockname == NULL) + return 125; + + (void) mktemp(sockname); + if (sockname[0] == '\0') + return 125; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + bzero(&sockaddr, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + + assert(strlen(sockname) == sizeof(sockaddr.sun_path)); + memcpy(sockaddr.sun_path, sockname, sizeof(sockaddr.sun_path)); + + chdir("/tmp"); + + (void) unlink(sockaddr.sun_path); + status = bind(fd, (const struct sockaddr *) &sockaddr, sizeof(sockaddr)); + if (status < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + + (void) unlink(sockaddr.sun_path); + exit(EXIT_SUCCESS); +}
diff --git a/5.1.0/tests/test-wwwwwwww.sh b/5.1.0/tests/test-wwwwwwww.sh new file mode 100644 index 0000000..24d2cfc --- /dev/null +++ b/5.1.0/tests/test-wwwwwwww.sh
@@ -0,0 +1,10 @@ +if [ -z `which mcookie` ] || [ -z `which mkdir` ] || [ -z `which rm` ] || [ ! -x ${ROOTFS}/bin/pwd ]; then + exit 125; +fi + +TMP=/tmp/$(mcookie) +mkdir ${TMP} +cd ${TMP} +${PROOT} sh -c "cd ..; rm -r ${TMP}; mkdir ${TMP}; cd ${TMP}; ${ROOTFS}/bin/pwd" + +rm -fr ${TMP}
diff --git a/5.1.0/tests/test-xxxxxxxx.c b/5.1.0/tests/test-xxxxxxxx.c new file mode 100644 index 0000000..e8335e3 --- /dev/null +++ b/5.1.0/tests/test-xxxxxxxx.c
@@ -0,0 +1,52 @@ +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/stat.h> + +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); +}
diff --git a/5.1.0/tests/true.c b/5.1.0/tests/true.c new file mode 100644 index 0000000..78f2de1 --- /dev/null +++ b/5.1.0/tests/true.c
@@ -0,0 +1 @@ +int main(void) { return 0; }
diff --git a/5.1.0/tests/validation.mk b/5.1.0/tests/validation.mk new file mode 100644 index 0000000..c2d0786 --- /dev/null +++ b/5.1.0/tests/validation.mk
@@ -0,0 +1,133 @@ +libuv-version = 0.10.27 +coreutils-version = 8.21 +perl-version = 5.18.1 +ltp-version = 20140422 +opt-version = 20140422 +gdb-version = 7.6.1 +proot-version = 3.2.2 +glibc-version = 2.17 + +libuv = libuv-$(libuv-version) +coreutils = coreutils-$(coreutils-version) +perl = perl-$(perl-version) +ltp = ltp-$(ltp-version) +opt = opt-$(opt-version) +gdb = gdb-$(gdb-version) +proot = PRoot-$(proot-version) +glibc = glibc-$(glibc-version) + +testsuites = $(libuv) $(perl) $(ltp) $(opt) $(gdb) $(proot) $(coreutils) # $(glibc) too long. +logs = $(testsuites:=.log) + +logs: $(logs) + +.PHONY: clean +clean: + rm -f $(logs) + rm -fr $(testsuites) + +.PHONY: distclean + distclean: clean + rm -f $(testsuites:=.tar.*) + +###################################################################### + +$(libuv).tar.gz: + wget https://github.com/joyent/libuv/archive/v$(libuv-version).tar.gz -O $@ + +$(libuv).log: $(libuv).tar.gz + rm -fr $(libuv) + tar -xf $< + $(MAKE) -C $(libuv) + ($(MAKE) -C $(libuv) test 2>&1 || true) | tee $@ + +###################################################################### + +$(coreutils).tar.xz: + wget http://ftp.gnu.org/gnu/coreutils/$(coreutils).tar.xz + +$(coreutils).log: $(coreutils).tar.xz + rm -fr $(coreutils) + tar -xf $< + cd $(coreutils) && ./configure + $(MAKE) -C $(coreutils) + ($(MAKE) -C $(coreutils) check || true) | tee $@ + +###################################################################### + +$(perl).tar.gz: + wget http://www.cpan.org/src/5.0/$(perl).tar.gz + +$(perl).log: $(perl).tar.gz + rm -fr $(perl) + tar -xf $< + cd $(perl) && ./configure.gnu + $(MAKE) -C $(perl) + ($(MAKE) -C $(perl) check || true) | tee $@ + +###################################################################### + +$(ltp).tar.gz: + wget https://github.com/linux-test-project/ltp/archive/$(ltp-version).tar.gz -O $@ + +$(ltp).log: $(ltp).tar.gz + rm -fr $(ltp) + tar -xf $< + $(MAKE) -C $(ltp) autotools + cd $(ltp) && ./configure --prefix=$(PWD)/$(ltp)/install + $(MAKE) -C $(ltp) + $(MAKE) -C $(ltp) install + sed -i s/^msgctl10/#/ $(ltp)/install/runtest/syscalls # is too CPU intensive + sed -i s/^msgctl11/#/ $(ltp)/install/runtest/syscalls # is too CPU intensive + ($(ltp)/install/runltp -f syscalls || true) | tee $@ + +###################################################################### + +$(opt).log: $(ltp).tar.gz + rm -fr $(opt) + mkdir $(opt) + tar -C $(opt) -xf $< $(ltp)/testcases/open_posix_testsuite + $(MAKE) -C $(opt)/$(ltp)/testcases/open_posix_testsuite -j 1 # has broken // build + ($(MAKE) -C $(opt)/$(ltp)/testcases/open_posix_testsuite -j 1 test || true) | tee $@ + +###################################################################### + +$(gdb).tar.gz: + wget http://ftp.gnu.org/gnu/gdb/$(gdb).tar.gz + +$(gdb).log: $(gdb).tar.gz + rm -fr $(gdb) + tar -xf $< + cd $(gdb) && ./configure + $(MAKE) -C $(gdb) + rm -f $(gdb)/gdb/testsuite/gdb.base/attach-twice.exp # kills PRoot explicitly + ($(MAKE) -C $(gdb)/gdb/testsuite check-gdb.base1 check-gdb.base2 check-gdb.server || true) | tee $@ + +###################################################################### + +$(glibc).tar.xz: + wget http://ftp.gnu.org/gnu/glibc/$(glibc).tar.xz -O $@ + +$(glibc).log: $(glibc).tar.xz + rm -fr $(glibc) + tar -xf $< + mkdir -p $(glibc)/build/prefix + cd $(glibc)/build && ../configure --prefix=$(PWD)/prefix + $(MAKE) -C $(glibc)/build + cp /usr/lib*/libgcc_s.so.1 $(glibc)/build + cp /usr/lib*/libstdc++.so.6 $(glibc)/build + sed -i s/tst-atexit3//g $(glibc)/dlfcn/Makefile # fails natively on Slack64-14.1 + sed -i s/tst-cputimer1//g $(glibc)/rt/Makefile # fails natively on Slack64-14.1 + sed -i 's/tests: check-abi/tests: /g' $(glibc)/Makerules # fails natively on Slack64-14.1 + ($(MAKE) -j 1 -C $(glibc)/build check || true) | tee $@ # has broken // build + +###################################################################### + +$(proot).tar.gz: + wget https://github.com/cedric-vincent/proot/archive/v$(proot-version).tar.gz -O $@ + +$(proot).log: $(proot).tar.gz + rm -fr $(proot) + tar -xf $< + $(MAKE) -C $(proot)/src + ($(MAKE) -C $(proot)/tests || true) | tee $@