mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Fix insecure user-space pointer dereferences in sys_execve.
This commit is contained in:
parent
23d9693261
commit
68d379c605
2 changed files with 99 additions and 56 deletions
|
@ -104,8 +104,6 @@ public:
|
|||
Ref<Descriptor> GetRoot();
|
||||
Ref<Descriptor> GetCWD();
|
||||
Ref<Descriptor> GetDescriptor(int fd);
|
||||
// TODO: This should be removed, don't call it.
|
||||
Ref<Descriptor> Open(ioctx_t* ctx, const char* path, int flags, mode_t mode = 0);
|
||||
void SetRoot(Ref<Descriptor> newroot);
|
||||
void SetCWD(Ref<Descriptor> newcwd);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -824,90 +825,133 @@ int Process::Execute(const char* programname, const uint8_t* program,
|
|||
return 0;
|
||||
}
|
||||
|
||||
// TODO. This is a hack. Please remove this when execve is moved to another
|
||||
// file/class, it doesn't belong here, it's a program loader ffs!
|
||||
Ref<Descriptor> Process::Open(ioctx_t* ctx, const char* path, int flags, mode_t mode)
|
||||
static
|
||||
int sys_execve_kernel(const char* filename,
|
||||
int argc,
|
||||
char* const argv[],
|
||||
int envc,
|
||||
char* const envp[],
|
||||
CPU::InterruptRegisters* regs)
|
||||
{
|
||||
// TODO: Locking the root/cwd pointers. How should that be arranged?
|
||||
Ref<Descriptor> dir = path[0] == '/' ? root : cwd;
|
||||
return dir->open(ctx, path, flags, mode);
|
||||
Process* process = CurrentProcess();
|
||||
|
||||
ioctx_t ctx;
|
||||
SetupKernelIOCtx(&ctx);
|
||||
Ref<Descriptor> from = filename[0] == '/' ? process->GetRoot() : process->GetCWD();
|
||||
Ref<Descriptor> desc = from->open(&ctx, filename, O_EXEC | O_READ, 0);
|
||||
if ( !desc )
|
||||
return -1;
|
||||
|
||||
struct stat st;
|
||||
if ( desc->stat(&ctx, &st) )
|
||||
return -1;
|
||||
if ( st.st_size < 0 )
|
||||
return errno = EINVAL, -1;
|
||||
if ( (uintmax_t) SIZE_MAX < (uintmax_t) st.st_size )
|
||||
return errno = EFBIG, -1;
|
||||
|
||||
size_t filesize = (size_t) st.st_size;
|
||||
uint8_t* buffer = new uint8_t[filesize];
|
||||
if ( !buffer )
|
||||
return -1;
|
||||
|
||||
for ( size_t sofar = 0; sofar < filesize; )
|
||||
{
|
||||
ssize_t amount = desc->read(&ctx, buffer + sofar, filesize - sofar);
|
||||
if ( amount < 0 )
|
||||
return delete[] buffer, -1;
|
||||
if ( amount == 0 )
|
||||
return delete[] buffer, errno = EEOF, -1;
|
||||
sofar += amount;
|
||||
}
|
||||
|
||||
int result = process->Execute(filename, buffer, filesize, argc, argv, envc, envp, regs);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
int sys_execve(const char* _filename, char* const _argv[], char* const _envp[])
|
||||
int sys_execve(const char* user_filename,
|
||||
char* const user_argv[],
|
||||
char* const user_envp[])
|
||||
{
|
||||
char* filename;
|
||||
int argc;
|
||||
int envc;
|
||||
char** argv;
|
||||
char** envp;
|
||||
ioctx_t ctx;
|
||||
Ref<Descriptor> desc;
|
||||
struct stat st;
|
||||
size_t sofar;
|
||||
size_t count;
|
||||
uint8_t* buffer;
|
||||
int result = -1;
|
||||
Process* process = CurrentProcess();
|
||||
CPU::InterruptRegisters regs;
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
filename = String::Clone(_filename);
|
||||
if ( !filename ) { goto cleanup_done; }
|
||||
if ( !user_filename || !user_argv || !user_envp )
|
||||
return errno = EFAULT, -1;
|
||||
|
||||
for ( argc = 0; _argv && _argv[argc]; argc++ );
|
||||
for ( envc = 0; _envp && _envp[envc]; envc++ );
|
||||
if ( !(filename = GetStringFromUser(user_filename)) )
|
||||
goto cleanup_done;
|
||||
|
||||
argc = 0;
|
||||
while ( true )
|
||||
{
|
||||
const char* user_arg;
|
||||
if ( !CopyFromUser(&user_arg, user_argv + argc, sizeof(user_arg)) )
|
||||
goto cleanup_filename;
|
||||
if ( !user_arg )
|
||||
break;
|
||||
if ( ++argc == INT_MAX )
|
||||
{
|
||||
errno = E2BIG;
|
||||
goto cleanup_filename;
|
||||
}
|
||||
}
|
||||
|
||||
argv = new char*[argc+1];
|
||||
if ( !argv ) { goto cleanup_filename; }
|
||||
if ( !argv )
|
||||
goto cleanup_filename;
|
||||
memset(argv, 0, sizeof(char*) * (argc+1));
|
||||
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
argv[i] = String::Clone(_argv[i]);
|
||||
if ( !argv[i] ) { goto cleanup_argv; }
|
||||
const char* user_arg;
|
||||
if ( !CopyFromUser(&user_arg, user_argv + i, sizeof(user_arg)) )
|
||||
goto cleanup_argv;
|
||||
if ( !(argv[i] = GetStringFromUser(user_arg)) )
|
||||
goto cleanup_argv;
|
||||
}
|
||||
|
||||
envc = 0;
|
||||
while ( true )
|
||||
{
|
||||
const char* user_env;
|
||||
if ( !CopyFromUser(&user_env, user_envp + envc, sizeof(user_env)) )
|
||||
goto cleanup_argv;
|
||||
if ( !user_env )
|
||||
break;
|
||||
if ( ++envc == INT_MAX )
|
||||
{
|
||||
errno = E2BIG;
|
||||
goto cleanup_argv;
|
||||
}
|
||||
}
|
||||
|
||||
envp = new char*[envc+1];
|
||||
if ( !envp ) { goto cleanup_argv; }
|
||||
envc = envc;
|
||||
if ( !envp )
|
||||
goto cleanup_argv;
|
||||
memset(envp, 0, sizeof(char*) * (envc+1));
|
||||
|
||||
for ( int i = 0; i < envc; i++ )
|
||||
{
|
||||
envp[i] = String::Clone(_envp[i]);
|
||||
if ( !envp[i] ) { goto cleanup_envp; }
|
||||
const char* user_env;
|
||||
if ( !CopyFromUser(&user_env, user_envp + i, sizeof(user_env)) )
|
||||
goto cleanup_envp;
|
||||
if ( !(envp[i] = GetStringFromUser(user_envp[i])) )
|
||||
goto cleanup_envp;
|
||||
}
|
||||
|
||||
SetupKernelIOCtx(&ctx);
|
||||
result = sys_execve_kernel(filename, argc, argv, envc, envp, ®s);
|
||||
|
||||
// TODO: Somehow mark the executable as busy and don't permit writes?
|
||||
desc = process->Open(&ctx, filename, O_READ | O_WRITE, 0);
|
||||
if ( !desc ) { goto cleanup_envp; }
|
||||
|
||||
if ( desc->stat(&ctx, &st) ) { goto cleanup_desc; }
|
||||
if ( st.st_size < 0 ) { errno = EINVAL; goto cleanup_desc; }
|
||||
if ( SIZE_MAX < (uintmax_t) st.st_size ) { errno = ERANGE; goto cleanup_desc; }
|
||||
|
||||
count = (size_t) st.st_size;
|
||||
buffer = new uint8_t[count];
|
||||
if ( !buffer ) { goto cleanup_desc; }
|
||||
sofar = 0;
|
||||
while ( sofar < count )
|
||||
{
|
||||
ssize_t bytesread = desc->read(&ctx, buffer + sofar, count - sofar);
|
||||
if ( bytesread < 0 ) { goto cleanup_buffer; }
|
||||
if ( bytesread == 0 ) { errno = EEOF; goto cleanup_buffer; }
|
||||
sofar += bytesread;
|
||||
}
|
||||
|
||||
result = process->Execute(filename, buffer, count, argc, argv, envc,
|
||||
envp, ®s);
|
||||
|
||||
cleanup_buffer:
|
||||
delete[] buffer;
|
||||
cleanup_desc:
|
||||
desc.Reset();
|
||||
cleanup_envp:
|
||||
for ( int i = 0; i < envc; i++)
|
||||
delete[] envp[i];
|
||||
|
@ -919,7 +963,8 @@ cleanup_argv:
|
|||
cleanup_filename:
|
||||
delete[] filename;
|
||||
cleanup_done:
|
||||
if ( !result ) { CPU::LoadRegisters(®s); }
|
||||
if ( result == 0 )
|
||||
CPU::LoadRegisters(®s);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue