【问题标题】:segfault on second calling of opendir第二次调用 opendir 时出现段错误
【发布时间】:2013-09-20 04:35:13
【问题描述】:

一些背景:

我正在为 nginx(Web 服务器)编写一个模块,该模块允许在共享环境中动态设置根目录。我有一个包含其他目录的目录,每个托管用户一个;给定与请求域相关的目录路径,我需要确定它属于哪个用户。

--public_dir
---user_1
---user_2
----com/example/www/_public/
---user_3

www.example.com 被转换为 com/example/www 并且函数应该返回“user_2”

问题:

我通过打开 public_dir、遍历它并检查每个 1 级目录(用户)是否存在 com/example/www/_public/ 并缓存结果来成功地做到了这一点。第一次调用该函数时,所有操作都按预期运行。第二次尝试再次打开public_dir时出现segfaults(在之前的函数调用中已经成功关闭):

#define NGX_SHARED_ENV_SHARED_DIR "/var/www/public/"
#define NGX_SHARED_ENV_PUBLIC_DIR "_public"

char* ownerFromDir(char *dir){
    char *owner;
    char fullPath[strlen(NGX_SHARED_ENV_SHARED_DIR)+strlen(dir)+128];
    struct dirent *e;
    struct stat sb;

// ------------ERROR IN HERE----------------------------

    fprintf(stderr, "Before opendir\n");
    DIR *sharedDir = opendir(NGX_SHARED_ENV_SHARED_DIR);
    fprintf(stderr, "After opendir\n");

// -----------------------------------------------------

    if(sharedDir){
        while((e=readdir(sharedDir))!=NULL){
            //is the entry a directory?
            if(e->d_type!=DT_DIR){ 
                continue;
            }
            if(strcmp(e->d_name, ".")==0 || strcmp(e->d_name, "..")==0){
                continue;
            }
            // build a path to check for /var/www/public/userX/com/example/www/_public
            snprintf(fullPath, sizeof(fullPath), "%s%s/%s/%s", NGX_SHARED_ENV_SHARED_DIR, e->d_name, dir, NGX_SHARED_ENV_PUBLIC_DIR);
            stat(fullPath, &sb);
            //if a matching directory is found then copy the entry name into owner
            if(S_ISDIR(sb.st_mode)){ 
                int len = 1 + (int) strlen(e->d_name);
                owner = (char*) malloc(sizeof(char)*len);
                free(owner);
                memcpy(owner, &(e->d_name), len);
                break;
            }
        }
        closedir(sharedDir);
    }
    return owner;
}

即使使用 gcc -g 编译调试标志,gdb 也不是很有帮助

Breakpoint 1, ownerFromDir (dir=0x603450 "com/example") at ngx_module.h:43
43  char* ownerFromDir(char *dir){
(gdb) step
45      char fullPath[strlen(NGX_SHARED_ENV_SHARED_DIR)+strlen(dir)+128];
(gdb) step
48      fprintf(stderr, "Before opendir\n");
(gdb) step
Before opendir
49      DIR *sharedDir = opendir(NGX_SHARED_ENV_SHARED_DIR);
(gdb) step
50      fprintf(stderr, "After opendir\n");
(gdb) step
After opendir
51      if(sharedDir){
(gdb) continue
Continuing.

Breakpoint 1, ownerFromDir (dir=0x60b4a0 "com/example2") at ngx_module.h:43
43  char* ownerFromDir(char *dir){
(gdb) step
45      char fullPath[strlen(NGX_SHARED_ENV_SHARED_DIR)+strlen(dir)+128];
(gdb) step
48      fprintf(stderr, "Before opendir\n");
(gdb) step
Before opendir
49      DIR *sharedDir = opendir(NGX_SHARED_ENV_SHARED_DIR);
(gdb) step

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a99df2 in ?? () from /lib/x86_64-linux-gnu/libc.so.6

编辑:回溯

(gdb) backtrace
#0  0x00007ffff7a99df2 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff7a9b446 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffff7a9dfc5 in malloc () from /lib/x86_64-linux-gnu/libc.so.6
#3  0x00007ffff7ad651b in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#4  0x0000000000400aea in ownerFromDir (dir=0x60b4a0 "com/example2") at ./ngx_module.h:48
#5  0x0000000000400cb2 in ownerFromDom (domain=0x40116c "example2.com") at ./ngx_module.h:82
#6  0x0000000000400f3f in testOwner (domain=0x40116c "example2.com", expect=0x401150 "user2") at ./ngx_module.c:45
#7  0x0000000000400e27 in runTests () at ./ngx_module.c:29
#8  0x0000000000400d0b in main (argc=1, argv=0x7fffffffe708) at ./ngx_module.c:10

谁能指点我正确的方向?

【问题讨论】:

  • 我的第一个问题是,在崩溃时你能提供一个回溯吗?我的第二个问题,这两个电话之间发生了什么?也许您在其他地方有一些堆栈覆盖问题?我的第三个问题是为什么你的头文件中有代码?
  • 感谢@JoachimPileborg 1. 添加回溯 2. 在这之间我运行一个测试函数,将域名传递给域/目录转换器,然后传递给这个函数,比较答案(我会测试更多直接排除堆栈覆盖) 3. 我是自学成才并且相对较新,因此假设头文件用于共享代码:)(我会继续阅读)
  • Re 2. 重复直接调用函数仍然会导致相同的段错误
  • _DIRENT_HAVE_D_TYPE 的值是多少?我们这里真的不需要看条件编译;它只会让你的代码更大,你的问题更难。
  • 出于兴趣: _DIRENT_HAVE_D_TYPE 表示从 readdir() 返回的结构是否包含条目的类型。只有名称 (d_name) 和 inode 编号 (d_ino) 是必要的 POSIX 返回值。

标签: c segmentation-fault opendir


【解决方案1】:

您的代码可以:

        if(S_ISDIR(sb.st_mode)){
            int len = 1 + (int) strlen(e->d_name);
            owner = (char*) malloc(sizeof(char)*len);
            free(owner);
            memcpy(owner, &(e->d_name), len);
            break;
        }

这导致:

return owner;

您已释放内存,因此 memcpy() 无效(访问您无权访问的内存)并且返回至少是危险的并且可以说是无效的(您正在传回指向已释放内存的指针)。

删除此代码中的free()。谁知道调用代码出了什么问题,但一个适度合理的猜测是'你free()返回的值',这是一个双重释放,这也是一个严重的禁忌。

正如所写,memcpy() 很容易损坏您的堆;双重释放可能会破坏您的堆。因此,由于堆损坏的两个来源,您很可能会因此在某个地方发生崩溃,并且opendir() 进行内存分配,因此它是一个合理的受害者。如果您在此之前进行了内存分配,它可能会崩溃。

【讨论】:

  • 谢谢,我之前在构建函数的一部分时已经把它放在那里,所以我不会有泄漏而忘记删除它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-17
相关资源
最近更新 更多