#include "elf.h"
#include "exec_reader.h"
#include "auxiliary_vector.h"
#include "sched.h"
#include "proc_struct.h"
#include <fd.h>
#include <resolve.h>
#include <node.h>
#include <stat_struct.h>
#include <vm.h>
#include <verify_area.h>
#include <string_table.h>
#include <gdt.h>
#include <segment.h>
#include <it_stack_structs.h>
#include <asm.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <i386.h>
#include <stddef.h>
#include <limits.h>
#include <auxv.h>
static inline int check_for_addition(unsigned long base, unsigned long offset)
{
return base <= ULONG_MAX - offset;
}
static inline int check_for_signed_addition(unsigned long base, long offset)
{
if (offset < 0)
return base >= 1UL - offset + 1UL;
else
return base <= ULONG_MAX - offset;
}
static int create_exec_reader(const char *fs_filename, struct exec_reader_struct **exec_reader)
{
int retval;
struct file_struct *file;
struct stat_struct statbuf;
if ((retval = resolve(fs_filename, &file)) < 0)
goto error;
if ((retval = stat_node(file, &statbuf)) < 0)
goto error_release;
if (statbuf.type != FT_REG) {
retval = -EACCES;
goto error_release;
}
if ((retval = node_deny_write_access(file)) < 0)
goto error_release;
if ((retval = is_elf(file)) < 0)
goto error_release_write_access;
if (retval) {
if ((retval = alloc_elf_exec_reader(file, exec_reader)) < 0)
goto error_release_write_access;
return 0;
}
retval = -ENOEXEC;
error_release_write_access:
node_release_write_access(file);
error_release:
node_release(file);
error:
return retval;
}
static unsigned int get_str_table_size(const struct string_table_struct *string_table)
{
return ALIGN(string_table->size);
}
static unsigned int get_auxv_entry_size()
{
return 2 * sizeof (unsigned long);
}
static unsigned int get_auxv_size(const struct auxiliary_vector_struct *auxiliary_vector)
{
unsigned int size;
size = 0;
size += get_auxv_entry_size();
if (auxiliary_vector->contents.program_header_table)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.program_header_entry_size)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.program_header_entry_number)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.page_size)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.interpreter_base_address)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.program_entry_point)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.not_elf)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.vdso_entry_point)
size += get_auxv_entry_size();
if (auxiliary_vector->contents.vdso_elf_header)
size += get_auxv_entry_size();
return size;
}
static unsigned int get_str_array_size(unsigned int count)
{
return (count + 1) * sizeof (long);
}
static unsigned int get_int_size()
{
return sizeof (long);
}
static unsigned int get_user_stack_size(const struct string_table_struct *environ, const struct auxiliary_vector_struct *auxiliary_vector)
{
unsigned int size;
size = 0;
size += get_str_table_size(environ);
size += get_str_table_size(¤t->cmdline);
size += get_auxv_size(auxiliary_vector);
size += get_str_array_size(environ->count);
size += get_str_array_size(current->cmdline.count);
size += get_int_size();
return size;
}
static void push_str_table(void **fs_user_stack_top, const struct string_table_struct *string_table)
{
if (string_table->buf) {
*fs_user_stack_top -= ALIGN(string_table->size);
memcpy_tofs(*fs_user_stack_top, string_table->buf, string_table->size);
memset_tofs(*fs_user_stack_top + string_table->size, 0, ALIGN(string_table->size) - string_table->size);
}
}
static void push_auxv_entry(void **fs_user_stack_top, int id, unsigned long val)
{
*fs_user_stack_top -= sizeof (long);
put_fs_long(val, *fs_user_stack_top);
*fs_user_stack_top -= sizeof (long);
put_fs_long(id, *fs_user_stack_top);
}
static void push_auxv(void **fs_user_stack_top, const struct auxiliary_vector_struct *auxiliary_vector)
{
push_auxv_entry(fs_user_stack_top, AT_NULL, 0);
if (auxiliary_vector->contents.program_header_table)
push_auxv_entry(fs_user_stack_top, AT_PHDR, auxiliary_vector->program_header_table);
if (auxiliary_vector->contents.program_header_entry_size)
push_auxv_entry(fs_user_stack_top, AT_PHENT, auxiliary_vector->program_header_entry_size);
if (auxiliary_vector->contents.program_header_entry_number)
push_auxv_entry(fs_user_stack_top, AT_PHNUM, auxiliary_vector->program_header_entry_number);
if (auxiliary_vector->contents.page_size)
push_auxv_entry(fs_user_stack_top, AT_PAGESZ, auxiliary_vector->page_size);
if (auxiliary_vector->contents.interpreter_base_address)
push_auxv_entry(fs_user_stack_top, AT_BASE, auxiliary_vector->interpreter_base_address);
if (auxiliary_vector->contents.program_entry_point)
push_auxv_entry(fs_user_stack_top, AT_ENTRY, auxiliary_vector->program_entry_point);
if (auxiliary_vector->contents.not_elf)
push_auxv_entry(fs_user_stack_top, AT_NOTELF, 0);
if (auxiliary_vector->contents.vdso_entry_point)
push_auxv_entry(fs_user_stack_top, AT_SYSINFO, auxiliary_vector->vdso_entry_point);
if (auxiliary_vector->contents.vdso_elf_header)
push_auxv_entry(fs_user_stack_top, AT_SYSINFO_EHDR, auxiliary_vector->vdso_elf_header);
}
static void push_str_array(void **fs_user_stack_top, const char *str_table, unsigned int count)
{
char **fs_str_array;
const char *s;
unsigned int i;
*fs_user_stack_top -= (count + 1) * sizeof (long);
fs_str_array = *fs_user_stack_top;
s = str_table;
for (i = 0; i < count; i++) {
put_fs_long((long)s, (long *)&fs_str_array[i]);
while (get_fs_byte(s++));
}
put_fs_long((long)NULL, (long *)&fs_str_array[count]);
}
static void push_int(void **fs_user_stack_top, int value)
{
*fs_user_stack_top -= sizeof (long);
put_fs_long(value, *fs_user_stack_top);
}
static void fill_in_user_stack(void **fs_user_stack_top, const struct string_table_struct *environ, const struct auxiliary_vector_struct *auxiliary_vector)
{
const char *environ_table, *argument_table;
push_str_table(fs_user_stack_top, environ);
environ_table = *fs_user_stack_top;
push_str_table(fs_user_stack_top, ¤t->cmdline);
argument_table = *fs_user_stack_top;
push_auxv(fs_user_stack_top, auxiliary_vector);
push_str_array(fs_user_stack_top, environ_table, environ->count);
push_str_array(fs_user_stack_top, argument_table, current->cmdline.count);
push_int(fs_user_stack_top, current->cmdline.count);
}
static void reset_exec(struct string_table_struct *cmdline)
{
int intr;
disable_intr(intr);
current->did_exec = 1;
string_table_destroy(¤t->cmdline);
current->cmdline = *cmdline;
vm_free(¤t->vm);
current->brk = 0;
restore_intr(intr);
}
static void close_file_descrs_on_exec(struct file_descr_struct *file_descr_table[])
{
struct file_descr_struct **file_descr;
for (file_descr = file_descr_table; file_descr < file_descr_table + OPEN_MAX; file_descr++)
if (*file_descr && fd_get_close_on_exec(*file_descr)) {
fd_close(*file_descr);
*file_descr = NULL;
}
}
static void init_auxiliary_vector(struct auxiliary_vector_struct *auxiliary_vector)
{
auxiliary_vector->contents.program_header_table = 0;
auxiliary_vector->contents.program_header_entry_size = 0;
auxiliary_vector->contents.program_header_entry_number = 0;
auxiliary_vector->contents.page_size = 0;
auxiliary_vector->contents.interpreter_base_address = 0;
auxiliary_vector->contents.program_entry_point = 0;
auxiliary_vector->contents.not_elf = 0;
auxiliary_vector->contents.vdso_entry_point = 0;
auxiliary_vector->contents.vdso_elf_header = 0;
}
static int setup_user_stack(unsigned long *user_stack_top, const struct string_table_struct *environ, const struct auxiliary_vector_struct *auxiliary_vector)
{
unsigned long length;
int rv;
length = PAGE_ALIGN(get_user_stack_size(environ, auxiliary_vector));
if ((rv = vm_map_anon(¤t->vm, USER_STACK_TOP - length, length, PROT_RDWR, 1, "stack")) < 0)
return rv;
if (!verify_area((void *)(USER_STACK_TOP - length), length, PF_WRITE))
return -EFAULT;
*user_stack_top = USER_STACK_TOP;
fill_in_user_stack((void **)user_stack_top, environ, auxiliary_vector);
return 0;
}
int exec(const char *fs_filename, char *const fs_argv[], char *const fs_envp[], struct registers_struct *registers, struct interrupted_instruction_struct *interrupted_instruction, int *status)
{
int retval;
struct exec_reader_struct *exec_reader;
struct string_table_struct cmdline, environ;
unsigned long user_stack_top, entry_point;
struct auxiliary_vector_struct auxiliary_vector;
if ((retval = create_exec_reader(fs_filename, &exec_reader)) < 0)
goto error;
if ((retval = string_table_init_from_array(&cmdline, fs_argv)) < 0)
goto error_free_exec_reader;
if ((retval = string_table_init_from_array(&environ, fs_envp)) < 0)
goto error_destroy_cmdline;
if (cmdline.size + environ.size > ARG_MAX) {
retval = -E2BIG;
goto error_destroy_environ;
}
reset_exec(&cmdline);
close_file_descrs_on_exec(current->file_descr_table);
init_auxiliary_vector(&auxiliary_vector);
if ((*status = exec_reader->vptr->load(exec_reader, &entry_point, &auxiliary_vector)) < 0)
goto fatal_error_destroy_environ;
if ((*status = setup_user_stack(&user_stack_top, &environ, &auxiliary_vector)) < 0)
goto fatal_error_destroy_environ;
if (!current->brk) {
*status = -EINVAL;
goto fatal_error_destroy_environ;
}
string_table_destroy(&environ);
exec_reader->vptr->free(exec_reader);
registers->eax = 0;
registers->ebx = 0;
registers->ecx = 0;
registers->edx = 0;
registers->esi = 0;
registers->edi = 0;
registers->ebp = 0;
registers->ds = USER_DATA_SEL;
registers->es = USER_DATA_SEL;
registers->fs = USER_DATA_SEL;
registers->gs = USER_DATA_SEL;
interrupted_instruction->eip = entry_point;
interrupted_instruction->cs = USER_CODE_SEL;
interrupted_instruction->eflags = 0x292;
interrupted_instruction->esp = user_stack_top;
interrupted_instruction->ss = USER_DATA_SEL;
*status = 0;
return 0;
error_destroy_environ:
string_table_destroy(&environ);
error_destroy_cmdline:
string_table_destroy(&cmdline);
error_free_exec_reader:
exec_reader->vptr->free(exec_reader);
error:
return retval;
fatal_error_destroy_environ:
string_table_destroy(&environ);
exec_reader->vptr->free(exec_reader);
return 0;
}
int sbrk(int increment, unsigned long *brk)
{
unsigned long new_brk;
unsigned long old_end_data, new_end_data;
int rv;
if (increment) {
if (!check_for_signed_addition(current->brk, increment))
return -EINVAL;
new_brk = current->brk + increment;
old_end_data = PAGE_ALIGN(current->brk);
if (!check_for_addition(new_brk, PAGE_SIZE - 1))
return -EINVAL;
new_end_data = PAGE_ALIGN(new_brk);
if (new_end_data < old_end_data) {
if ((rv = vm_unmap(¤t->vm, new_end_data, old_end_data - new_end_data)) < 0)
return rv;
}
else if (new_end_data > old_end_data)
if ((rv = vm_map_anon(¤t->vm, old_end_data, new_end_data - old_end_data, PROT_RDWR, 0, "heap")) < 0)
return rv;
current->brk = new_brk;
}
*brk = current->brk;
return 0;
}