diff --git a/src/execve/enter.c b/src/execve/enter.c index fb7a3bf2..e51b65a3 100644 --- a/src/execve/enter.c +++ b/src/execve/enter.c @@ -166,8 +166,12 @@ static int add_interp(Tracee *tracee, int fd, LoadInfo *load_info, if (status < 0) return status; - load_info->interp->path = talloc_strdup(load_info->interp, host_path); - if (load_info->interp->path == NULL) + 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; @@ -176,7 +180,7 @@ static int add_interp(Tracee *tracee, int fd, LoadInfo *load_info, #undef P /** - * Extract the load info from @load->path. This function returns + * Extract the load info from @load->host_path. This function returns * -errno if an error occured, otherwise it returns 0. * * TODO: factorize with find_program_header() @@ -194,9 +198,9 @@ static int extract_load_info(Tracee *tracee, LoadInfo *load_info) int i; assert(load_info != NULL); - assert(load_info->path != NULL); + assert(load_info->host_path != NULL); - fd = open_elf(load_info->path, &load_info->elf_header); + fd = open_elf(load_info->host_path, &load_info->elf_header); if (fd < 0) return fd; @@ -439,16 +443,27 @@ int translate_execve_enter(Tracee *tracee) } /* WIP. */ +#ifdef LOADER2 + status = set_sysarg_path(tracee, "/usr/local/cedric/git/proot/src/execve/loader-x86_64", SYSARG_1); +#else status = set_sysarg_path(tracee, "/usr/local/cedric/git/proot/src/execve/stub-x86_64", SYSARG_1); +#endif if (status < 0) return status; + if (tracee->load_info != NULL) + TALLOC_FREE(tracee->load_info); + tracee->load_info = talloc_zero(tracee, LoadInfo); if (tracee->load_info == NULL) return -ENOMEM; - tracee->load_info->path = talloc_strdup(tracee->load_info, host_path); - if (tracee->load_info->path == NULL) + 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; status = extract_load_info(tracee, tracee->load_info); diff --git a/src/execve/execve.h b/src/execve/execve.h index 4716bb1f..dc68b429 100644 --- a/src/execve/execve.h +++ b/src/execve/execve.h @@ -30,6 +30,7 @@ extern int translate_execve(Tracee *tracee); extern int translate_execve_enter(Tracee *tracee); extern int translate_execve_exit(Tracee *tracee); +extern int translate_execve_exit2(Tracee *tracee); extern int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path); #endif /* EXECVE_H */ diff --git a/src/execve/exit.c b/src/execve/exit.c index 9569a030..1f36a215 100644 --- a/src/execve/exit.c +++ b/src/execve/exit.c @@ -36,6 +36,7 @@ #include "execve/load.h" #include "execve/auxv.h" #include "execve/elf.h" +#include "loader/script.h" #include "syscall/sysnum.h" #include "syscall/syscall.h" #include "syscall/chain.h" @@ -43,6 +44,283 @@ #include "tracee/mem.h" #include "cli/notice.h" +#ifdef LOADER2 + +/** + * Write the @value into the @script at the given @position, according + * to the mode (32/64-bit) of @tracee. + */ +static inline void write_word(const Tracee *tracee, void *script, size_t position, word_t value) +{ + const size_t size = sizeof_word(tracee); + + switch (size) { + case 4: { + uint32_t *script_ = (uint32_t *) script; + script_[position] = value; + break; + } + + case 8: { + uint64_t *script_ = (uint64_t *) script; + script_[position] = value; + break; + } + + default: + assert(0); + } +} + +/** + * Convert @mappings into load @script statements at the given + * @position. This function returns NULL if an error occurred, + * otherwise a pointer to the updated load script. + */ +static void *transcript_mappings(const Tracee *tracee, void *script, + size_t position, const Mapping *mappings) +{ + size_t nb_mappings; + size_t script_size; + size_t i; + + script_size = talloc_get_size(script); + + nb_mappings = talloc_array_length(mappings); + for (i = 0; i < nb_mappings; i++) { + if ((mappings[i].flags & MAP_ANONYMOUS) != 0) { + script_size += LOAD_PACKET_LENGTH_MMAP_ANON * sizeof_word(tracee); + script = talloc_realloc_size(tracee->ctx, script, script_size); + if (script == NULL) + return NULL; + + write_word(tracee, script, position++, LOAD_ACTION_MMAP_ANON); + write_word(tracee, script, position++, mappings[i].addr); + write_word(tracee, script, position++, mappings[i].length); + write_word(tracee, script, position++, mappings[i].prot); + } + else { + script_size += LOAD_PACKET_LENGTH_MMAP_FILE * sizeof_word(tracee); + script = talloc_realloc_size(tracee->ctx, script, script_size); + if (script == NULL) + return NULL; + + write_word(tracee, script, position++, LOAD_ACTION_MMAP_FILE); + write_word(tracee, script, position++, mappings[i].addr); + write_word(tracee, script, position++, mappings[i].length); + write_word(tracee, script, position++, mappings[i].prot); + write_word(tracee, script, position++, mappings[i].offset); + } + + if (mappings[i].clear_length != 0) { + word_t address; + + script_size += LOAD_PACKET_LENGTH_CLEAR * sizeof_word(tracee); + script = talloc_realloc_size(tracee->ctx, script, script_size); + if (script == NULL) + return NULL; + + address = mappings[i].addr + + mappings[i].length + - mappings[i].clear_length; + + write_word(tracee, script, position++, LOAD_ACTION_CLEAR); + write_word(tracee, script, position++, address); + write_word(tracee, script, position++, mappings[i].clear_length); + } + } + + return script; +} + +/** + * Convert @tracee->load_info into a load script, then transfer this + * latter into @tracee's memory. + */ +static int transfer_load_script(Tracee *tracee) +{ + word_t stack_pointer; + word_t entry_point; + + void *tail; + size_t tail_size; + size_t string1_size; + size_t string2_size; + size_t padding_size; + + word_t string1_address; + word_t string2_address; + + void *script; + size_t script_size; + + size_t position; + int status; + + stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER); + + /* 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 + * "tail" area. */ + string1_size = strlen(tracee->load_info->user_path) + 1; + string2_size = tracee->load_info->interp != NULL + ? strlen(tracee->load_info->interp->user_path) + 1 + : 0; + + /* A padding will be appended at the end of the load script + * (a.k.a the "tail") to ensure this latter is aligned on a + * word boundary, for sake of performance. */ + padding_size = (stack_pointer - string1_size - string2_size) % sizeof_word(tracee); + + tail_size = string1_size + string2_size + padding_size; + string1_address = stack_pointer - tail_size; + string2_address = stack_pointer - tail_size + string1_size; + + tail = talloc_size(tracee->ctx, tail_size); + if (tail == NULL) + return -ENOMEM; + + memcpy(tail, tracee->load_info->user_path, string1_size); + if (string2_size != 0) + memcpy(tail + string1_size, tracee->load_info->interp->user_path, string2_size); + + /* So far, the tail content is as follow: + * + * +---------+ <- initial stack pointer (higher address) + * | string1 | + * +---------+ + * | string2 | + * +---------+ + * | padding | + * +---------+ (lower address, word aligned) + */ + + /* Load script statement: open. */ + script_size = LOAD_PACKET_LENGTH_OPEN * sizeof_word(tracee); + script = talloc_size(tracee->ctx, script_size); + if (script == NULL) + return -ENOMEM; + + position = 0; + write_word(tracee, script, position++, LOAD_ACTION_OPEN); + write_word(tracee, script, position++, string1_address); + + /* Load script statements: mmap. */ + script = transcript_mappings(tracee, script, position, tracee->load_info->mappings); + if (script == NULL) + return -ENOMEM; + + /* These value were outdated by transcript_mappings(). */ + script_size = talloc_get_size(script); + position = script_size / sizeof_word(tracee); + + if (tracee->load_info->interp != NULL) { + /* Load script statement: close, open. */ + script_size += LOAD_PACKET_LENGTH_CLOSE_OPEN * sizeof_word(tracee); + script = talloc_realloc_size(tracee->ctx, script, script_size); + if (script == NULL) + return -ENOMEM; + + write_word(tracee, script, position++, LOAD_ACTION_CLOSE_OPEN); + write_word(tracee, script, position++, string2_address); + + script = transcript_mappings(tracee, script, position, + tracee->load_info->interp->mappings); + if (script == NULL) + return -ENOMEM; + + /* These value were outdated by transcript_mappings(). */ + script_size = talloc_get_size(script); + position = script_size / sizeof_word(tracee); + + 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: close, jump @entry_point. */ + script_size += LOAD_PACKET_LENGTH_CLOSE_BRANCH * sizeof_word(tracee); + script = talloc_realloc_size(tracee->ctx, script, script_size); + if (script == NULL) + return -ENOMEM; + + write_word(tracee, script, position++, LOAD_ACTION_CLOSE_BRANCH); + write_word(tracee, script, position++, stack_pointer); + write_word(tracee, script, position++, entry_point); + + /* Sanity checks. */ + assert(tail_size == talloc_get_size(tail)); + assert(script_size == talloc_get_size(script)); + + /* Concatenate the load script and the tail (strings). */ + script = talloc_realloc_size(tracee->ctx, script, script_size + tail_size); + if (script == NULL) + return -ENOMEM; + + memcpy(script + script_size, tail, tail_size); + script_size += tail_size; + + /* Copy everything in the tracee's memory at once. */ + status = write_data(tracee, stack_pointer - script_size, script, script_size); + if (status < 0) + return status; + + /* Update the stack pointer and the pointer to the load + * script. */ + poke_reg(tracee, STACK_POINTER, stack_pointer - script_size); + poke_reg(tracee, SYSARG_1, stack_pointer - script_size); + + /* Remember we are in the sysexit stage, so be sure the + * current register values will be used as at the end. */ + save_current_regs(tracee, ORIGINAL); + tracee->_regs_were_changed = true; + + /* So far, the stack content is as follow: + * + * +----------+ <- initial stack pointer + * | tail | + * +----------+ + * | load | + * | script | + * +----------+ <- stack pointer, sysarg1 (word aligned) + */ + + return 0; +} + +/** + * Start the loading of @tracee. This function returns -errno if an + * error occured, otherwise 0. + */ +int translate_execve_exit2(Tracee *tracee) +{ + word_t syscall_result; + int status; + + syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + if ((int) syscall_result < 0) + return 0; + + /* New processes have no heap. */ + bzero(tracee->heap, sizeof(Heap)); + + /* Adjust ELF auxiliary vectors before transfering the load + * script since the stack pointer might be changed. */ + adjust_elf_aux_vectors(tracee); + + /* Transfer the load script to the loader. */ + status = transfer_load_script(tracee); + if (status < 0) + return status; /* Note: it's too late to do anything + * useful. */ + + return 0; +} + +#else + /** * Start the loading of @tracee. This function returns -errno if an * error occured, otherwise 0. @@ -61,7 +339,7 @@ int translate_execve_exit(Tracee *tracee) bzero(tracee->heap, sizeof(Heap)); /* Adjust ELF auxiliary vectors before saving current - * registers since the stack pointer migth be changed. */ + * registers since the stack pointer might be changed. */ adjust_elf_aux_vectors(tracee); /* Once the loading process is done, registers must be @@ -111,10 +389,10 @@ void translate_load_enter(Tracee *tracee) case LOADING_STEP_OPEN: set_sysnum(tracee, PR_open); - status = set_sysarg_path(tracee, tracee->loading.info->path, SYSARG_1); + status = set_sysarg_path(tracee, tracee->loading.info->host_path, SYSARG_1); if (status < 0) { notice(tracee, ERROR, INTERNAL, "can't open '%s': %s", - tracee->loading.info->path, strerror(-status)); + tracee->loading.info->host_path, strerror(-status)); goto error; } @@ -174,7 +452,7 @@ void translate_load_exit(Tracee *tracee) case LOADING_STEP_OPEN: if (signed_result < 0) { notice(tracee, ERROR, INTERNAL, "can't open '%s': %s", - tracee->loading.info->path, strerror(-signed_result)); + tracee->loading.info->host_path, strerror(-signed_result)); goto error; } @@ -198,14 +476,14 @@ void translate_load_exit(Tracee *tracee) if ( signed_result < 0 && signed_result > -4096) { notice(tracee, ERROR, INTERNAL, "can't map '%s': %s", - tracee->loading.info->path, strerror(-signed_result)); + tracee->loading.info->host_path, strerror(-signed_result)); goto error; } if ( current_mapping->addr != result && (current_mapping->flags & MAP_FIXED) != 0) { notice(tracee, ERROR, INTERNAL, "can't map '%s' to the specified address", - tracee->loading.info->path); + tracee->loading.info->host_path); goto error; } @@ -227,7 +505,7 @@ void translate_load_exit(Tracee *tracee) case LOADING_STEP_CLOSE: if (signed_result < 0) { notice(tracee, ERROR, INTERNAL, "can't close '%s': %s", - tracee->loading.info->path, strerror(-signed_result)); + tracee->loading.info->host_path, strerror(-signed_result)); goto error; } @@ -282,4 +560,6 @@ void translate_load_exit(Tracee *tracee) kill(tracee->pid, SIGKILL); } +#endif /* LOADER2 */ + #endif /* EXECVE2 */ diff --git a/src/execve/ldso.c b/src/execve/ldso.c index a5c5e506..cba8b98b 100644 --- a/src/execve/ldso.c +++ b/src/execve/ldso.c @@ -29,7 +29,6 @@ #include /* PATH_MAX, ARG_MAX, */ #include "execve/ldso.h" -#include "execve/execve.h" #include "execve/elf.h" #include "execve/aoxp.h" #include "tracee/tracee.h" diff --git a/src/execve/load.h b/src/execve/load.h index 758fb3b9..9e51d3c5 100644 --- a/src/execve/load.h +++ b/src/execve/load.h @@ -40,7 +40,8 @@ typedef struct mapping { } Mapping; typedef struct load_info { - char *path; + char *host_path; + char *user_path; Mapping *mappings; ElfHeader elf_header; diff --git a/src/loader/loader.c b/src/loader/loader.c new file mode 100644 index 00000000..cd8345d1 --- /dev/null +++ b/src/loader/loader.c @@ -0,0 +1,134 @@ +/* gcc -Isrc -fPIC -nostartfiles -Wall -Wextra src/loader/loader.c -O3 -o src/loader/loader -g -static -Wl,-Ttext=0x00007f2000000000 */ + +#include /* SYS_*, */ +#include /* O_*, */ +#include /* MAP_*, */ +#include /* bzero(3), */ + +#include "loader/script.h" + +/* TODO: set all registers to 0. */ +#define BRANCH(stack_pointer, destination) \ + asm volatile ( \ + "movq %0, %%rsp \n\t" \ + "jmpq *%1 \n" \ + : : "irm" (stack_pointer), "irm" (destination) ) + +#define ERROR() \ + asm volatile ( \ + "movq $60, %rax \n\t" \ + "movq $182, %rdi \n\t" \ + "syscall \n") + +#define SC1(result, number, arg1) \ + asm volatile ( \ + "movq %1, %%rax \n\t" \ + "movq %2, %%rdi \n\t" \ + "syscall \n\t" \ + "movq %%rax, %0 \n" \ + : "=rm" (result) \ + : "irm" (number), "irm" (arg1) \ + : "cc", "rcx", "r11", "rax", "rdi") + +#define SC3(result, number, arg1, arg2, arg3) \ + asm volatile ( \ + "movq %1, %%rax \n\t" \ + "movq %2, %%rdi \n\t" \ + "movq %3, %%rsi \n\t" \ + "movq %4, %%rdx \n\t" \ + "syscall \n\t" \ + "movq %%rax, %0 \n" \ + : "=rm" (result) \ + : "irm" (number), "irm" (arg1), "irm" (arg2), "irm" (arg3) \ + : "cc", "rcx", "r11", "rax", "rdi", "rsi", "rdx") + +#define SC6(result, number, arg1, arg2, arg3, arg4, arg5, arg6) \ + asm volatile ( \ + "movq %1, %%rax \n\t" \ + "movq %2, %%rdi \n\t" \ + "movq %3, %%rsi \n\t" \ + "movq %4, %%rdx \n\t" \ + "movq %5, %%r10 \n\t" \ + "movq %6, %%r8 \n\t" \ + "movq %7, %%r9 \n\t" \ + "syscall \n\t" \ + "movq %%rax, %0 \n" \ + : "=rm" (result) \ + : "irm" (number), "irm" (arg1), "irm" (arg2), \ + "irm" (arg3), "irm" (arg4), "irm" (arg5), "irm" (arg6) \ + : "cc", "rcx", "r11", "rax", "rdi", "rsi", "rdx", "r10", "r8", "r9") + +typedef unsigned long word_t; + +#define unlikely(expr) __builtin_expect(!!(expr), 0) + +/***********************************************************************/ + +/* Note: this is optimized both for speed and for memory + * footprint. */ +void _start(word_t *cursor) +{ + word_t status; + word_t fd = -1; + + while(1) { +#define action LOAD_PACKET_ACTION(cursor) +#define addr LOAD_PACKET_ADDR(cursor) +#define addr2 LOAD_PACKET_LENGTH(cursor) +#define length LOAD_PACKET_LENGTH(cursor) +#define prot LOAD_PACKET_PROT(cursor) +#define offset LOAD_PACKET_OFFSET(cursor) + + switch (action) { + case LOAD_ACTION_CLOSE_OPEN: + SC1(status, SYS_close, fd); + if (unlikely((int) status < 0)) + ERROR(); + /* Fall through. */ + case LOAD_ACTION_OPEN: + SC3(fd, SYS_open, addr, O_RDONLY, 0); + if (unlikely((int) fd < 0)) + ERROR(); + + cursor += LOAD_PACKET_LENGTH_OPEN; + break; + + case LOAD_ACTION_CLOSE_BRANCH: + SC1(status, SYS_close, fd); + if (unlikely((int) status < 0)) + ERROR(); + + BRANCH(addr, addr2); + + ERROR(); /* Never reached. */ + + case LOAD_ACTION_MMAP_FILE: + SC6(status, SYS_mmap, addr, length, prot, + MAP_PRIVATE | MAP_FIXED, fd, offset); + if (unlikely(status != addr)) + ERROR(); + + cursor += LOAD_PACKET_LENGTH_MMAP_FILE; + break; + + case LOAD_ACTION_MMAP_ANON: + SC6(status, SYS_mmap, addr, length, prot, + MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); + if (unlikely(status != addr)) + ERROR(); + + cursor += LOAD_PACKET_LENGTH_MMAP_ANON; + break; + + case LOAD_ACTION_CLEAR: + bzero((void *) addr, length); + cursor += LOAD_PACKET_LENGTH_CLEAR; + break; + + default: + ERROR(); /* Never reached. */ + } + } + + ERROR(); /* Never reached. */ +} diff --git a/src/loader/script.h b/src/loader/script.h new file mode 100644 index 00000000..3fa1fcbb --- /dev/null +++ b/src/loader/script.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 SCRIPT +#define SCRIPT + +#define LOAD_PACKET_ACTION(pointer) (pointer)[0] +#define LOAD_PACKET_ADDR(pointer) (pointer)[1] +#define LOAD_PACKET_ADDR2(pointer) (pointer)[2] +#define LOAD_PACKET_LENGTH(pointer) (pointer)[2] +#define LOAD_PACKET_PROT(pointer) (pointer)[3] +#define LOAD_PACKET_OFFSET(pointer) (pointer)[4] + +#define LOAD_ACTION_OPEN 0 +#define LOAD_ACTION_CLOSE_OPEN 1 +#define LOAD_ACTION_CLOSE_BRANCH 2 +#define LOAD_ACTION_MMAP_FILE 3 +#define LOAD_ACTION_MMAP_ANON 4 +#define LOAD_ACTION_CLEAR 5 + +#define LOAD_PACKET_LENGTH_OPEN 2 /* action, addr */ +#define LOAD_PACKET_LENGTH_CLOSE_OPEN 2 /* action, addr */ +#define LOAD_PACKET_LENGTH_CLOSE_BRANCH 3 /* action, addr, addr2 */ +#define LOAD_PACKET_LENGTH_MMAP_FILE 5 /* action, addr, length, prot, offset */ +#define LOAD_PACKET_LENGTH_MMAP_ANON 4 /* action, addr, length, prot, */ +#define LOAD_PACKET_LENGTH_CLEAR 3 /* action, addr, length */ + +#endif /* SCRIPT */ diff --git a/src/syscall/enter.c b/src/syscall/enter.c index 47e4e78e..3eb91410 100644 --- a/src/syscall/enter.c +++ b/src/syscall/enter.c @@ -121,7 +121,7 @@ int translate_syscall_enter(Tracee *tracee) break; case PR_execve: -#if EXECVE2 +#ifdef EXECVE2 status = translate_execve_enter(tracee); #else status = translate_execve(tracee); diff --git a/src/syscall/exit.c b/src/syscall/exit.c index b9742480..02d3dcd0 100644 --- a/src/syscall/exit.c +++ b/src/syscall/exit.c @@ -425,9 +425,13 @@ void translate_syscall_exit(Tracee *tracee) } #endif -#if EXECVE2 +#ifdef EXECVE2 case PR_execve: +#ifdef LOADER2 + status = translate_execve_exit2(tracee); +#else status = translate_execve_exit(tracee); +#endif goto end; case PR_rt_sigreturn: diff --git a/src/syscall/syscall.c b/src/syscall/syscall.c index 7f762bea..7cbd892d 100644 --- a/src/syscall/syscall.c +++ b/src/syscall/syscall.c @@ -127,7 +127,7 @@ void translate_syscall(Tracee *tracee) save_current_regs(tracee, MODIFIED); } else { -#ifdef EXECVE2 +#if defined(EXECVE2) && !defined(LOADER2) if (tracee->loading.step != 0) { translate_load_enter(tracee); status = 0; @@ -170,7 +170,7 @@ void translate_syscall(Tracee *tracee) if (tracee->chain.syscalls == NULL) translate_syscall_exit(tracee); else { -#ifdef EXECVE2 +#if defined(EXECVE2) && !defined(LOADER2) if (tracee->loading.step != 0) translate_load_exit(tracee); else