As per our phone conference from yesterday, this is my variant of
sys32_execve which compiles and works fine. It's very similar to the
Sparc64 code, just derived from more recent code than it.
Ralf
/* 4011 execve */
/*
* count() counts the number of arguments/envelopes
*
* We must use access_ok() and __get_user(), not get_user() or gcc 1.1.2
* will die with an internal error.
*/
static int count32(u32 *argv, int max)
{
int i = 0;
if (argv != NULL) {
for (;;) {
int error;
u32 p;
if (!access_ok(VERIFY_READ, argv, sizeof(*argv)))
return -EFAULT;
error = __get_user(p, argv);
if (error)
return error;
if (!p)
break;
argv++;
if (++i > max)
return -E2BIG;
}
}
return i;
}
/*
* 'copy_strings()' copies argument/envelope strings from user
* memory to free pages in kernel mem. These are in a format ready
* to be put directly into the top of new user memory.
*/
int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm)
{
while (argc-- > 0) {
s32 str;
int len;
unsigned long pos;
if (get_user(str, argv+argc) || !str ||
!(len = strnlen_user((char *)(long)str, bprm->p)))
return -EFAULT;
if (bprm->p < len)
return -E2BIG;
bprm->p -= len;
pos = bprm->p;
while (len>0) {
char *pag;
int offset, bytes_to_copy;
offset = pos % PAGE_SIZE;
if (!(pag = (char *) bprm->page[pos/PAGE_SIZE]) &&
!(pag = (char *) bprm->page[pos/PAGE_SIZE] =
(unsigned long *) get_free_page(GFP_USER)))
return -ENOMEM;
bytes_to_copy = PAGE_SIZE - offset;
if (bytes_to_copy > len)
bytes_to_copy = len;
if (copy_from_user(pag + offset, str, bytes_to_copy))
return -EFAULT;
pos += bytes_to_copy;
str += bytes_to_copy;
len -= bytes_to_copy;
}
}
return 0;
}
/*
* sys_execve() executes a new program.
*/
static int
do_execve32(char * filename, u32 *argv, u32 *envp, struct pt_regs *regs)
{
struct linux_binprm bprm;
struct dentry * dentry;
int retval;
int i;
bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
dentry = open_namei(filename, 0, 0);
retval = PTR_ERR(dentry);
if (IS_ERR(dentry))
return retval;
bprm.dentry = dentry;
bprm.filename = filename;
bprm.sh_bang = 0;
bprm.loader = 0;
bprm.exec = 0;
if ((bprm.argc = count32(argv, bprm.p / sizeof(void *))) < 0) {
dput(dentry);
return bprm.argc;
}
if ((bprm.envc = count32(envp, bprm.p / sizeof(void *))) < 0) {
dput(dentry);
return bprm.envc;
}
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 = copy_strings32(bprm.envc, envp, &bprm);
if (retval < 0)
goto out;
retval = copy_strings32(bprm.argc, argv, &bprm);
if (retval < 0)
goto out;
retval = search_binary_handler(&bprm,regs);
if (retval >= 0)
/* execve success */
return retval;
out:
/* Something went wrong, return the inode and free the argument pages*/
if (bprm.dentry)
dput(bprm.dentry);
/* Assumes that free_page() can take a NULL argument. */
/* I hope this is ok for all architectures */
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(bprm.page[i]);
return retval;
}
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys32_execve(abi64_no_regargs, struct pt_regs regs)
{
int error;
char * filename;
lock_kernel();
filename = getname((char *) regs.regs[4]);
printk("execve32: filename = 0x%lx, $a0 = 0x%lx\n", filename, regs.regs[4]);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve32(filename, (u32 *) regs.regs[5],
(u32 *) regs.regs[6], ®s);
putname(filename);
out:
unlock_kernel();
return error;
}
|