Merge branch 'exec_rm_compat' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc
* 'exec_rm_compat' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc: exec: document acct_arg_size() exec: unify do_execve/compat_do_execve code exec: introduce struct user_arg_ptr exec: introduce get_user_arg_ptr() helper
This commit is contained in:
235
fs/compat.c
235
fs/compat.c
@@ -1306,241 +1306,6 @@ compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int
|
|||||||
return do_sys_open(dfd, filename, flags, mode);
|
return do_sys_open(dfd, filename, flags, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* compat_count() counts the number of arguments/envelopes. It is basically
|
|
||||||
* a copy of count() from fs/exec.c, except that it works with 32 bit argv
|
|
||||||
* and envp pointers.
|
|
||||||
*/
|
|
||||||
static int compat_count(compat_uptr_t __user *argv, int max)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (argv != NULL) {
|
|
||||||
for (;;) {
|
|
||||||
compat_uptr_t p;
|
|
||||||
|
|
||||||
if (get_user(p, argv))
|
|
||||||
return -EFAULT;
|
|
||||||
if (!p)
|
|
||||||
break;
|
|
||||||
argv++;
|
|
||||||
if (i++ >= max)
|
|
||||||
return -E2BIG;
|
|
||||||
|
|
||||||
if (fatal_signal_pending(current))
|
|
||||||
return -ERESTARTNOHAND;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* compat_copy_strings() is basically a copy of copy_strings() from fs/exec.c
|
|
||||||
* except that it works with 32 bit argv and envp pointers.
|
|
||||||
*/
|
|
||||||
static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
|
|
||||||
struct linux_binprm *bprm)
|
|
||||||
{
|
|
||||||
struct page *kmapped_page = NULL;
|
|
||||||
char *kaddr = NULL;
|
|
||||||
unsigned long kpos = 0;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
while (argc-- > 0) {
|
|
||||||
compat_uptr_t str;
|
|
||||||
int len;
|
|
||||||
unsigned long pos;
|
|
||||||
|
|
||||||
if (get_user(str, argv+argc) ||
|
|
||||||
!(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) {
|
|
||||||
ret = -EFAULT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len > MAX_ARG_STRLEN) {
|
|
||||||
ret = -E2BIG;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We're going to work our way backwords. */
|
|
||||||
pos = bprm->p;
|
|
||||||
str += len;
|
|
||||||
bprm->p -= len;
|
|
||||||
|
|
||||||
while (len > 0) {
|
|
||||||
int offset, bytes_to_copy;
|
|
||||||
|
|
||||||
if (fatal_signal_pending(current)) {
|
|
||||||
ret = -ERESTARTNOHAND;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
cond_resched();
|
|
||||||
|
|
||||||
offset = pos % PAGE_SIZE;
|
|
||||||
if (offset == 0)
|
|
||||||
offset = PAGE_SIZE;
|
|
||||||
|
|
||||||
bytes_to_copy = offset;
|
|
||||||
if (bytes_to_copy > len)
|
|
||||||
bytes_to_copy = len;
|
|
||||||
|
|
||||||
offset -= bytes_to_copy;
|
|
||||||
pos -= bytes_to_copy;
|
|
||||||
str -= bytes_to_copy;
|
|
||||||
len -= bytes_to_copy;
|
|
||||||
|
|
||||||
if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
|
|
||||||
struct page *page;
|
|
||||||
|
|
||||||
page = get_arg_page(bprm, pos, 1);
|
|
||||||
if (!page) {
|
|
||||||
ret = -E2BIG;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kmapped_page) {
|
|
||||||
flush_kernel_dcache_page(kmapped_page);
|
|
||||||
kunmap(kmapped_page);
|
|
||||||
put_page(kmapped_page);
|
|
||||||
}
|
|
||||||
kmapped_page = page;
|
|
||||||
kaddr = kmap(kmapped_page);
|
|
||||||
kpos = pos & PAGE_MASK;
|
|
||||||
flush_cache_page(bprm->vma, kpos,
|
|
||||||
page_to_pfn(kmapped_page));
|
|
||||||
}
|
|
||||||
if (copy_from_user(kaddr+offset, compat_ptr(str),
|
|
||||||
bytes_to_copy)) {
|
|
||||||
ret = -EFAULT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
out:
|
|
||||||
if (kmapped_page) {
|
|
||||||
flush_kernel_dcache_page(kmapped_page);
|
|
||||||
kunmap(kmapped_page);
|
|
||||||
put_page(kmapped_page);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* compat_do_execve() is mostly a copy of do_execve(), with the exception
|
|
||||||
* that it processes 32 bit argv and envp pointers.
|
|
||||||
*/
|
|
||||||
int compat_do_execve(char * filename,
|
|
||||||
compat_uptr_t __user *argv,
|
|
||||||
compat_uptr_t __user *envp,
|
|
||||||
struct pt_regs * regs)
|
|
||||||
{
|
|
||||||
struct linux_binprm *bprm;
|
|
||||||
struct file *file;
|
|
||||||
struct files_struct *displaced;
|
|
||||||
bool clear_in_exec;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = unshare_files(&displaced);
|
|
||||||
if (retval)
|
|
||||||
goto out_ret;
|
|
||||||
|
|
||||||
retval = -ENOMEM;
|
|
||||||
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
|
|
||||||
if (!bprm)
|
|
||||||
goto out_files;
|
|
||||||
|
|
||||||
retval = prepare_bprm_creds(bprm);
|
|
||||||
if (retval)
|
|
||||||
goto out_free;
|
|
||||||
|
|
||||||
retval = check_unsafe_exec(bprm);
|
|
||||||
if (retval < 0)
|
|
||||||
goto out_free;
|
|
||||||
clear_in_exec = retval;
|
|
||||||
current->in_execve = 1;
|
|
||||||
|
|
||||||
file = open_exec(filename);
|
|
||||||
retval = PTR_ERR(file);
|
|
||||||
if (IS_ERR(file))
|
|
||||||
goto out_unmark;
|
|
||||||
|
|
||||||
sched_exec();
|
|
||||||
|
|
||||||
bprm->file = file;
|
|
||||||
bprm->filename = filename;
|
|
||||||
bprm->interp = filename;
|
|
||||||
|
|
||||||
retval = bprm_mm_init(bprm);
|
|
||||||
if (retval)
|
|
||||||
goto out_file;
|
|
||||||
|
|
||||||
bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
|
|
||||||
if ((retval = bprm->argc) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
|
|
||||||
if ((retval = bprm->envc) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = prepare_binprm(bprm);
|
|
||||||
if (retval < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = copy_strings_kernel(1, &bprm->filename, bprm);
|
|
||||||
if (retval < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
bprm->exec = bprm->p;
|
|
||||||
retval = compat_copy_strings(bprm->envc, envp, bprm);
|
|
||||||
if (retval < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = compat_copy_strings(bprm->argc, argv, bprm);
|
|
||||||
if (retval < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = search_binary_handler(bprm, regs);
|
|
||||||
if (retval < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* execve succeeded */
|
|
||||||
current->fs->in_exec = 0;
|
|
||||||
current->in_execve = 0;
|
|
||||||
acct_update_integrals(current);
|
|
||||||
free_bprm(bprm);
|
|
||||||
if (displaced)
|
|
||||||
put_files_struct(displaced);
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (bprm->mm) {
|
|
||||||
acct_arg_size(bprm, 0);
|
|
||||||
mmput(bprm->mm);
|
|
||||||
}
|
|
||||||
|
|
||||||
out_file:
|
|
||||||
if (bprm->file) {
|
|
||||||
allow_write_access(bprm->file);
|
|
||||||
fput(bprm->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
out_unmark:
|
|
||||||
if (clear_in_exec)
|
|
||||||
current->fs->in_exec = 0;
|
|
||||||
current->in_execve = 0;
|
|
||||||
|
|
||||||
out_free:
|
|
||||||
free_bprm(bprm);
|
|
||||||
|
|
||||||
out_files:
|
|
||||||
if (displaced)
|
|
||||||
reset_files_struct(displaced);
|
|
||||||
out_ret:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
|
#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
|
||||||
|
|
||||||
static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
|
static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
|
||||||
|
125
fs/exec.c
125
fs/exec.c
@@ -55,6 +55,7 @@
|
|||||||
#include <linux/fs_struct.h>
|
#include <linux/fs_struct.h>
|
||||||
#include <linux/pipe_fs_i.h>
|
#include <linux/pipe_fs_i.h>
|
||||||
#include <linux/oom.h>
|
#include <linux/oom.h>
|
||||||
|
#include <linux/compat.h>
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/mmu_context.h>
|
#include <asm/mmu_context.h>
|
||||||
@@ -166,8 +167,13 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
|
/*
|
||||||
void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
|
* The nascent bprm->mm is not visible until exec_mmap() but it can
|
||||||
|
* use a lot of memory, account these pages in current->mm temporary
|
||||||
|
* for oom_badness()->get_mm_rss(). Once exec succeeds or fails, we
|
||||||
|
* change the counter back via acct_arg_size(0).
|
||||||
|
*/
|
||||||
|
static void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
|
||||||
{
|
{
|
||||||
struct mm_struct *mm = current->mm;
|
struct mm_struct *mm = current->mm;
|
||||||
long diff = (long)(pages - bprm->vma_pages);
|
long diff = (long)(pages - bprm->vma_pages);
|
||||||
@@ -186,7 +192,7 @@ void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
|
static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
|
||||||
int write)
|
int write)
|
||||||
{
|
{
|
||||||
struct page *page;
|
struct page *page;
|
||||||
@@ -305,11 +311,11 @@ static bool valid_arg_len(struct linux_binprm *bprm, long len)
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
|
static inline void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
|
static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
|
||||||
int write)
|
int write)
|
||||||
{
|
{
|
||||||
struct page *page;
|
struct page *page;
|
||||||
@@ -398,22 +404,56 @@ err:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct user_arg_ptr {
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
bool is_compat;
|
||||||
|
#endif
|
||||||
|
union {
|
||||||
|
const char __user *const __user *native;
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
compat_uptr_t __user *compat;
|
||||||
|
#endif
|
||||||
|
} ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||||
|
{
|
||||||
|
const char __user *native;
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
if (unlikely(argv.is_compat)) {
|
||||||
|
compat_uptr_t compat;
|
||||||
|
|
||||||
|
if (get_user(compat, argv.ptr.compat + nr))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
|
||||||
|
return compat_ptr(compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (get_user(native, argv.ptr.native + nr))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* count() counts the number of strings in array ARGV.
|
* count() counts the number of strings in array ARGV.
|
||||||
*/
|
*/
|
||||||
static int count(const char __user * const __user * argv, int max)
|
static int count(struct user_arg_ptr argv, int max)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (argv != NULL) {
|
if (argv.ptr.native != NULL) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const char __user * p;
|
const char __user *p = get_user_arg_ptr(argv, i);
|
||||||
|
|
||||||
if (get_user(p, argv))
|
|
||||||
return -EFAULT;
|
|
||||||
if (!p)
|
if (!p)
|
||||||
break;
|
break;
|
||||||
argv++;
|
|
||||||
|
if (IS_ERR(p))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
if (i++ >= max)
|
if (i++ >= max)
|
||||||
return -E2BIG;
|
return -E2BIG;
|
||||||
|
|
||||||
@@ -430,7 +470,7 @@ static int count(const char __user * const __user * argv, int max)
|
|||||||
* processes's memory to the new process's stack. The call to get_user_pages()
|
* processes's memory to the new process's stack. The call to get_user_pages()
|
||||||
* ensures the destination page is created and not swapped out.
|
* ensures the destination page is created and not swapped out.
|
||||||
*/
|
*/
|
||||||
static int copy_strings(int argc, const char __user *const __user *argv,
|
static int copy_strings(int argc, struct user_arg_ptr argv,
|
||||||
struct linux_binprm *bprm)
|
struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
struct page *kmapped_page = NULL;
|
struct page *kmapped_page = NULL;
|
||||||
@@ -443,16 +483,18 @@ static int copy_strings(int argc, const char __user *const __user *argv,
|
|||||||
int len;
|
int len;
|
||||||
unsigned long pos;
|
unsigned long pos;
|
||||||
|
|
||||||
if (get_user(str, argv+argc) ||
|
ret = -EFAULT;
|
||||||
!(len = strnlen_user(str, MAX_ARG_STRLEN))) {
|
str = get_user_arg_ptr(argv, argc);
|
||||||
ret = -EFAULT;
|
if (IS_ERR(str))
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid_arg_len(bprm, len)) {
|
len = strnlen_user(str, MAX_ARG_STRLEN);
|
||||||
ret = -E2BIG;
|
if (!len)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = -E2BIG;
|
||||||
|
if (!valid_arg_len(bprm, len))
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
/* We're going to work our way backwords. */
|
/* We're going to work our way backwords. */
|
||||||
pos = bprm->p;
|
pos = bprm->p;
|
||||||
@@ -519,14 +561,19 @@ out:
|
|||||||
/*
|
/*
|
||||||
* Like copy_strings, but get argv and its values from kernel memory.
|
* Like copy_strings, but get argv and its values from kernel memory.
|
||||||
*/
|
*/
|
||||||
int copy_strings_kernel(int argc, const char *const *argv,
|
int copy_strings_kernel(int argc, const char *const *__argv,
|
||||||
struct linux_binprm *bprm)
|
struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
mm_segment_t oldfs = get_fs();
|
mm_segment_t oldfs = get_fs();
|
||||||
|
struct user_arg_ptr argv = {
|
||||||
|
.ptr.native = (const char __user *const __user *)__argv,
|
||||||
|
};
|
||||||
|
|
||||||
set_fs(KERNEL_DS);
|
set_fs(KERNEL_DS);
|
||||||
r = copy_strings(argc, (const char __user *const __user *)argv, bprm);
|
r = copy_strings(argc, argv, bprm);
|
||||||
set_fs(oldfs);
|
set_fs(oldfs);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(copy_strings_kernel);
|
EXPORT_SYMBOL(copy_strings_kernel);
|
||||||
@@ -1379,10 +1426,10 @@ EXPORT_SYMBOL(search_binary_handler);
|
|||||||
/*
|
/*
|
||||||
* sys_execve() executes a new program.
|
* sys_execve() executes a new program.
|
||||||
*/
|
*/
|
||||||
int do_execve(const char * filename,
|
static int do_execve_common(const char *filename,
|
||||||
const char __user *const __user *argv,
|
struct user_arg_ptr argv,
|
||||||
const char __user *const __user *envp,
|
struct user_arg_ptr envp,
|
||||||
struct pt_regs * regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct linux_binprm *bprm;
|
struct linux_binprm *bprm;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
@@ -1489,6 +1536,34 @@ out_ret:
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_execve(const char *filename,
|
||||||
|
const char __user *const __user *__argv,
|
||||||
|
const char __user *const __user *__envp,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
struct user_arg_ptr envp = { .ptr.native = __envp };
|
||||||
|
return do_execve_common(filename, argv, envp, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
int compat_do_execve(char *filename,
|
||||||
|
compat_uptr_t __user *__argv,
|
||||||
|
compat_uptr_t __user *__envp,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct user_arg_ptr argv = {
|
||||||
|
.is_compat = true,
|
||||||
|
.ptr.compat = __argv,
|
||||||
|
};
|
||||||
|
struct user_arg_ptr envp = {
|
||||||
|
.is_compat = true,
|
||||||
|
.ptr.compat = __envp,
|
||||||
|
};
|
||||||
|
return do_execve_common(filename, argv, envp, regs);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void set_binfmt(struct linux_binfmt *new)
|
void set_binfmt(struct linux_binfmt *new)
|
||||||
{
|
{
|
||||||
struct mm_struct *mm = current->mm;
|
struct mm_struct *mm = current->mm;
|
||||||
|
@@ -60,10 +60,6 @@ struct linux_binprm {
|
|||||||
unsigned long loader, exec;
|
unsigned long loader, exec;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void acct_arg_size(struct linux_binprm *bprm, unsigned long pages);
|
|
||||||
extern struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
|
|
||||||
int write);
|
|
||||||
|
|
||||||
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
|
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
|
||||||
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
|
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user