linux-origin
[Top] [All Lists]

My sys32_execve().

To: Ulf Carlsson <ulfc@xxxxxxxxxxxx>, Kanoj Sarcar <kanoj@xxxxxxxxxxxx>
Subject: My sys32_execve().
From: Ralf Baechle <ralf@xxxxxxxxxxx>
Date: Wed, 1 Mar 2000 23:57:21 +0100
Cc: linux-origin@xxxxxxxxxxx
Sender: owner-linux-origin@xxxxxxxxxxx
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], &regs);
        putname(filename);

out:
        unlock_kernel();
        return error;
}

<Prev in Thread] Current Thread [Next in Thread>