1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-30 11:25:12 +00:00

Kernel: Properly set up the userland context for new processes on x86_64

This commit is contained in:
Gunnar Beutner 2021-06-28 17:42:29 +02:00 committed by Andreas Kling
parent 158355e0d7
commit d4c0d28035
2 changed files with 40 additions and 30 deletions

View file

@ -69,49 +69,50 @@ static bool validate_stack_size(const Vector<String>& arguments, const Vector<St
return true; return true;
} }
static KResultOr<FlatPtr> make_userspace_stack_for_main_thread(Region& region, Vector<String> arguments, Vector<String> environment, Vector<ELF::AuxiliaryValue> auxiliary_values) static KResultOr<FlatPtr> make_userspace_context_for_main_thread([[maybe_unused]] ThreadRegisters& regs, Region& region, Vector<String> arguments,
Vector<String> environment, Vector<ELF::AuxiliaryValue> auxiliary_values)
{ {
FlatPtr new_esp = region.range().end().get(); FlatPtr new_sp = region.range().end().get();
// Add some bits of randomness to the user stack pointer. // Add some bits of randomness to the user stack pointer.
new_esp -= round_up_to_power_of_two(get_fast_random<u32>() % 4096, 16); new_sp -= round_up_to_power_of_two(get_fast_random<u32>() % 4096, 16);
auto push_on_new_stack = [&new_esp](u32 value) { auto push_on_new_stack = [&new_sp](FlatPtr value) {
new_esp -= 4; new_sp -= sizeof(FlatPtr);
Userspace<u32*> stack_ptr = new_esp; Userspace<FlatPtr*> stack_ptr = new_sp;
return copy_to_user(stack_ptr, &value); return copy_to_user(stack_ptr, &value);
}; };
auto push_aux_value_on_new_stack = [&new_esp](auxv_t value) { auto push_aux_value_on_new_stack = [&new_sp](auxv_t value) {
new_esp -= sizeof(auxv_t); new_sp -= sizeof(auxv_t);
Userspace<auxv_t*> stack_ptr = new_esp; Userspace<auxv_t*> stack_ptr = new_sp;
return copy_to_user(stack_ptr, &value); return copy_to_user(stack_ptr, &value);
}; };
auto push_string_on_new_stack = [&new_esp](const String& string) { auto push_string_on_new_stack = [&new_sp](const String& string) {
new_esp -= round_up_to_power_of_two(string.length() + 1, 4); new_sp -= round_up_to_power_of_two(string.length() + 1, sizeof(FlatPtr));
Userspace<u32*> stack_ptr = new_esp; Userspace<FlatPtr*> stack_ptr = new_sp;
return copy_to_user(stack_ptr, string.characters(), string.length() + 1); return copy_to_user(stack_ptr, string.characters(), string.length() + 1);
}; };
Vector<FlatPtr> argv_entries; Vector<FlatPtr> argv_entries;
for (auto& argument : arguments) { for (auto& argument : arguments) {
push_string_on_new_stack(argument); push_string_on_new_stack(argument);
if (!argv_entries.try_append(new_esp)) if (!argv_entries.try_append(new_sp))
return ENOMEM; return ENOMEM;
} }
Vector<FlatPtr> env_entries; Vector<FlatPtr> env_entries;
for (auto& variable : environment) { for (auto& variable : environment) {
push_string_on_new_stack(variable); push_string_on_new_stack(variable);
if (!env_entries.try_append(new_esp)) if (!env_entries.try_append(new_sp))
return ENOMEM; return ENOMEM;
} }
for (auto& value : auxiliary_values) { for (auto& value : auxiliary_values) {
if (!value.optional_string.is_empty()) { if (!value.optional_string.is_empty()) {
push_string_on_new_stack(value.optional_string); push_string_on_new_stack(value.optional_string);
value.auxv.a_un.a_ptr = (void*)new_esp; value.auxv.a_un.a_ptr = (void*)new_sp;
} }
} }
@ -123,28 +124,35 @@ static KResultOr<FlatPtr> make_userspace_stack_for_main_thread(Region& region, V
push_on_new_stack(0); push_on_new_stack(0);
for (ssize_t i = env_entries.size() - 1; i >= 0; --i) for (ssize_t i = env_entries.size() - 1; i >= 0; --i)
push_on_new_stack(env_entries[i]); push_on_new_stack(env_entries[i]);
FlatPtr envp = new_esp; FlatPtr envp = new_sp;
push_on_new_stack(0); push_on_new_stack(0);
for (ssize_t i = argv_entries.size() - 1; i >= 0; --i) for (ssize_t i = argv_entries.size() - 1; i >= 0; --i)
push_on_new_stack(argv_entries[i]); push_on_new_stack(argv_entries[i]);
FlatPtr argv = new_esp; FlatPtr argv = new_sp;
// NOTE: The stack needs to be 16-byte aligned. // NOTE: The stack needs to be 16-byte aligned.
new_esp -= new_esp % 16; new_sp -= new_sp % 16;
#if ARCH(I386)
// GCC assumes that the return address has been pushed to the stack when it enters the function, // GCC assumes that the return address has been pushed to the stack when it enters the function,
// so we need to reserve an extra pointer's worth of bytes below this to make GCC's stack alignment // so we need to reserve an extra pointer's worth of bytes below this to make GCC's stack alignment
// calculations work // calculations work
new_esp -= sizeof(void*); new_sp -= sizeof(void*);
push_on_new_stack((FlatPtr)envp); push_on_new_stack(envp);
push_on_new_stack((FlatPtr)argv); push_on_new_stack(argv);
push_on_new_stack((FlatPtr)argv_entries.size()); push_on_new_stack(argv_entries.size());
push_on_new_stack(0); #else
regs.rdi = argv;
regs.rsi = argv_entries.size();
regs.rdx = envp;
#endif
push_on_new_stack(0); // return address
VERIFY((new_esp + sizeof(void*)) % 16 == 0); VERIFY((new_sp + sizeof(void*)) % 16 == 0);
return new_esp; return new_sp;
} }
struct RequiredLoadRange { struct RequiredLoadRange {
@ -596,10 +604,10 @@ KResult Process::do_exec(NonnullRefPtr<FileDescription> main_program_description
// NOTE: We create the new stack before disabling interrupts since it will zero-fault // NOTE: We create the new stack before disabling interrupts since it will zero-fault
// and we don't want to deal with faults after this point. // and we don't want to deal with faults after this point.
auto make_stack_result = make_userspace_stack_for_main_thread(*load_result.stack_region.unsafe_ptr(), move(arguments), move(environment), move(auxv)); auto make_stack_result = make_userspace_context_for_main_thread(new_main_thread->regs(), *load_result.stack_region.unsafe_ptr(), move(arguments), move(environment), move(auxv));
if (make_stack_result.is_error()) if (make_stack_result.is_error())
return make_stack_result.error(); return make_stack_result.error();
FlatPtr new_userspace_esp = make_stack_result.value(); FlatPtr new_userspace_sp = make_stack_result.value();
if (wait_for_tracer_at_next_execve()) { if (wait_for_tracer_at_next_execve()) {
// Make sure we release the ptrace lock here or the tracer will block forever. // Make sure we release the ptrace lock here or the tracer will block forever.
@ -647,10 +655,10 @@ KResult Process::do_exec(NonnullRefPtr<FileDescription> main_program_description
regs.fs = GDT_SELECTOR_DATA3 | 3; regs.fs = GDT_SELECTOR_DATA3 | 3;
regs.gs = GDT_SELECTOR_TLS | 3; regs.gs = GDT_SELECTOR_TLS | 3;
regs.eip = load_result.entry_eip; regs.eip = load_result.entry_eip;
regs.esp = new_userspace_esp; regs.esp = new_userspace_sp;
#else #else
regs.rip = load_result.entry_eip; regs.rip = load_result.entry_eip;
regs.rsp = new_userspace_esp; regs.rsp = new_userspace_sp;
#endif #endif
regs.cr3 = space().page_directory().cr3(); regs.cr3 = space().page_directory().cr3();

View file

@ -12,7 +12,7 @@
/* Auxiliary Vector types, from Intel386 ABI ver 1.0 section 2.3.3 */ /* Auxiliary Vector types, from Intel386 ABI ver 1.0 section 2.3.3 */
typedef struct typedef struct
{ {
long a_type; /* Note: Extended to long from int, for ease of comaptibility w/64 bit */ long a_type; /* Note: Extended to long from int, for ease of compatibility w/64 bit */
union { union {
long a_val; long a_val;
void* a_ptr; void* a_ptr;
@ -20,6 +20,8 @@ typedef struct
} a_un; } a_un;
} auxv_t; } auxv_t;
static_assert(sizeof(auxv_t) % sizeof(FlatPtr) == 0);
#define AT_NULL 0 /* No length, last entry's a_type has this value */ #define AT_NULL 0 /* No length, last entry's a_type has this value */
#define AT_IGNORE 1 /* Entry has no meaning, a_un undefined */ #define AT_IGNORE 1 /* Entry has no meaning, a_un undefined */
#define AT_EXECFD 2 /* a_val contains a file descriptor of the main program image */ #define AT_EXECFD 2 /* a_val contains a file descriptor of the main program image */