免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 14265 | 回复: 6

do_execve时候用户栈中参数的位置问题 [复制链接]

论坛徽章:
0
发表于 2012-03-19 15:14 |显示全部楼层
do_execve为了把参数传递给新的程序,分三次调用copy_strings把filename,环境变量和命令行变量按地址从高到低的次序依次放入栈中。举个例子,如果运行"ls -l /usr"在shell中,那么filename就是"/bin/ls", 命令行参数总共三个,分别是"/bin/ls", "-l"和"/usr"。可是既然命令行参数中已经有filename了,为什么还要在单独先把filename放入栈中呢
下面的图从ULK3中拷贝下来,按照do_execve的实现似乎在Environment settings和栈底的NULL之间还有filename

aa.jpg

int do_execve(char * filename,
        char __user *__user *argv,
        char __user *__user *envp,
        struct pt_regs * regs)
{
        struct linux_binprm *bprm;
        struct file *file;
        int retval;
        int i;

        retval = -ENOMEM;
        bprm = kmalloc(sizeof(*bprm), GFP_KERNEL);
        if (!bprm)
                goto out_ret;
        memset(bprm, 0, sizeof(*bprm));

        file = open_exec(filename);
        retval = PTR_ERR(file);
        if (IS_ERR(file))
                goto out_kfree;

        sched_exec();

        bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);

        bprm->file = file;
        bprm->filename = filename;
        bprm->interp = filename;
        bprm->mm = mm_alloc();
        retval = -ENOMEM;
        if (!bprm->mm)
                goto out_file;

        retval = init_new_context(current, bprm->mm);
        if (retval < 0)
                goto out_mm;

        bprm->argc = count(argv, bprm->p / sizeof(void *));
        if ((retval = bprm->argc) < 0)
                goto out_mm;

        bprm->envc = count(envp, bprm->p / sizeof(void *));
        if ((retval = bprm->envc) < 0)
                goto out_mm;

        retval = security_bprm_alloc(bprm);
        if (retval)
                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 = copy_strings(bprm->envc, envp, bprm);
        if (retval < 0)
                goto out;

        retval = copy_strings(bprm->argc, argv, bprm);
        if (retval < 0)
                goto out;

        retval = search_binary_handler(bprm,regs);
        if (retval >= 0) {
                free_arg_pages(bprm);

                /* execve success */
                security_bprm_free(bprm);
                acct_update_integrals();
                update_mem_hiwater();
                kfree(bprm);
                return retval;
        }

out:
        /* Something went wrong, return the inode and free the argument pages*/
        for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
                struct page * page = bprm->page;
                if (page)
                        __free_page(page);
        }

        if (bprm->security)
                security_bprm_free(bprm);

out_mm:
        if (bprm->mm)
                mmdrop(bprm->mm);

out_file:
        if (bprm->file) {
                allow_write_access(bprm->file);
                fput(bprm->file);
        }

out_kfree:
        kfree(bprm);

out_ret:
        return retval;
}

论坛徽章:
2
CU十二周年纪念徽章
日期:2013-10-24 15:41:34处女座
日期:2013-12-27 22:22:41
发表于 2012-03-19 22:34 |显示全部楼层
这里看不出来吧,没有向用户栈放东西的代码。现在用户栈存在吗?

印象中内核是不会干涉这些的,所谓第一个参数是可执行文件名只是UNIX上约定俗成的,应该是由应用程序来实现,即调用exec系列函数时使第一个argv为应用程序名。

可以做个实验,写个程序调用exec载入另一个可执行文件,第一个argv设为任意内容,被执行的那个程序打印argv[0]就不是文件名了。

论坛徽章:
0
发表于 2012-03-20 09:12 |显示全部楼层
此时还没有建立栈,但是copy_strings函数(copy_strings_kernel也调用copy_strings)会把参数内容拷贝到新分配的页中,随后在mmap中会将页表项指向该页。

argv[0]指向文件路径名那是没有问题的。我的问题是文件路径名为什么在栈中拷贝两次,一次通过copy_strings_kernel,另一次通过第二个copy_strings

论坛徽章:
2
CU十二周年纪念徽章
日期:2013-10-24 15:41:34处女座
日期:2013-12-27 22:22:41
发表于 2012-03-20 23:31 |显示全部楼层
回复 3# littlenewer

靠,临睡前想起这个问题看了一下代码,结果搞到现在。

之前说了,第一个参数是文件名那是用户态的约定俗成,如果这个进程的前身传给exec的参数列表里的第一项不是文件名,那你在main里引用argv[0]也就不是文件名了。

不存在拷贝两次文件名。是将文件名压入栈内一次,拷贝参数列表一次。参数列表的第一项是什么对内核没有意义。

至于将文件名先入栈,这确实是今天才发现的特性。我验证了,可以在用户态读出来。这样,不管传给exec的参数列表的第一个元素是什么,总可以确切得到可执行文件的名字。

论坛徽章:
0
发表于 2012-03-21 09:51 |显示全部楼层
回复 4# tempname2

如果传给exec的参数列表里面没有文件名,那么只有在栈底才存有文件名
如果传给exec的参数列表里面有文件名,那么文件名会出现在栈中两个地方,一个是栈底,另外一个是命令行参数列表里

不管是那种情况,新程序都能正常执行。因为main函数并不需要关心当前文件名,但都和上面贴图中的栈数据分布不同。上图中文件名只保存在命令行参数列表中,这点可以从ULK3中的描述得到确认。

   

论坛徽章:
2
CU十二周年纪念徽章
日期:2013-10-24 15:41:34处女座
日期:2013-12-27 22:22:41
发表于 2012-03-21 10:35 |显示全部楼层
你的问题是什么?为什么要放两次文件名?为什么ULK上的图没有把栈底的文件名当一个独立的元素画出来?

论坛徽章:
0
发表于 2012-03-21 11:20 |显示全部楼层
回复 6# tempname2

是的
   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP