c - Linux POSIX C LibPCRE `double free or corruption (fasttop)` 崩溃

标签 c linux pcre glibc

我有以下代码(它读取进程虚拟内存并使用 libpcre 匹配一些字符串),它编译时没有错误,但是如果我用 -Wall 编译它,我会收到一些警告,我将在之后显示代码。

编译后的代码运行但崩溃,*** glibc detected *** ./readmempcreuniq: double free or corruption (fasttop): 0x097b9c80 ***,我怀疑问题出在线上pcre_get_substring(page, vector, pairs, 0, &buff); 因为函数的第一个参数需要 'const char *' 但得到的是 'unsigned char *' ,我怎样才能让它正确?

#ifdef TARGET_64
// for 64bit target (see /proc/cpuinfo addr size virtual)
#define MEM_MAX (1ULL << 48)
#else
#define MEM_MAX (1ULL << 32)
#endif

#define _LARGEFILE64_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ptrace.h>
#include <pcre.h>
#include <locale.h>

int main(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <pid>\n", argv[0]);
        exit(1);
    }

    char buf[128];
    int pid = atoi(argv[1]);
    snprintf(buf, sizeof(buf), "/proc/%d/mem", pid);
    int fd = open(buf, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "Error opening mem file: %m\n");
        exit(1);
    }

    pcre *f;
    pcre_extra *f_ext;
    char *pattern = "([0-9]{20,22})";
    const char *errstr;
    int errchar;
    int vector[50];
    int vecsize = 50;
    int pairs;
    const char *buff;
    const unsigned char *tables;
    int a;
    int count = 0;
    const char **matches = NULL;
    const char **more_matches;

    char *loc = setlocale(LC_ALL, 0);
    setlocale(LC_ALL, loc);
    tables = pcre_maketables();

    long ptret = ptrace(PTRACE_ATTACH, pid, 0, 0);
    if (ptret == -1) {
        fprintf(stderr, "Ptrace failed: %s\n", strerror(errno));
        close(fd);
        exit(1);
    }

    unsigned char page[4096];
    unsigned long long offset = 0;


    while (offset < MEM_MAX) {
        lseek64(fd, offset, SEEK_SET);

        ssize_t ret;
        ret = read(fd, page, sizeof(page));

        if (ret > 0) {
            page[ret] = '\0';
            if((f = pcre_compile(pattern, PCRE_CASELESS|PCRE_MULTILINE, &errstr, &errchar, tables)) == NULL)
            {
                printf("Error: %s\nCharacter N%i\nPattern:%s\n", errstr, errchar, pattern);
            }
            else
            {
                f_ext = pcre_study(f, 0, &errstr);
                a = 0;

                while((pairs = pcre_exec(f, f_ext, page, sizeof(page), a, PCRE_NOTEMPTY, vector, vecsize)) >=0)
                {
                    pcre_get_substring(page, vector, pairs, 0, &buff);
                    //printf("%s\n", buff);
                    more_matches = realloc(matches, (count+1)* sizeof(*more_matches));
                    if (more_matches!=NULL)
                    {
                        matches=more_matches;
                        matches[count++]=buff;
                    }
                    else
                    {
                        free(matches);
                        puts("Error (re)allocating memory");
                        exit(1);
                    }
                    a = vector[1] + 1;
                }
                int matches_len = count;
                const char *uniques[matches_len];
                int uniques_len = 0;
                int already_exists;
                int i, j;
                for (i = 0; i < matches_len; i++)
                {
                    already_exists = 0;
                    for ( j = 0; j < uniques_len; j++)
                    {
                        if (!strcmp(matches[i], uniques[j]))
                        {
                            already_exists = 1;
                            break;
                        }
                    }
                    if (!already_exists)
                    {
                        uniques[uniques_len] = matches[i];
                        uniques_len++;
                    }
                }
                for (i = 0; i < uniques_len; i++)
                {
                    printf("%s\n", uniques[i]);
                }
                free(matches);
                pcre_free(f);
            }

        }

        offset += sizeof(page);
    }

    ptrace(PTRACE_DETACH, pid, 0, 0);
    close(fd);
    return 0;
}

错误:

xtmtrx@server:~/regex/proc$ ./readmempcreuniq 5663
92991999918876543209
99299299292663552673
111992229922222288
119988922220000077
*** glibc detected *** ./readmempcreuniq: double free or corruption (fasttop): 0x097b9c80 ***
======= Backtrace: =========
/lib/libc.so.6(+0x6c0c1)[0xb7dfa0c1]
/lib/libc.so.6(+0x6d930)[0xb7dfb930]
/lib/libc.so.6(+0x71681)[0xb7dff681]
/lib/libc.so.6(realloc+0xe3)[0xb7dffb13]
./readmempcreuniq[0x8048c86]
/lib/libc.so.6(__libc_start_main+0xe7)[0xb7da4ce7]
./readmempcreuniq[0x80488b1]
======= Memory map: ========
08048000-0804a000 r-xp 00000000 fd:01 68388533                           /root/regex/proc/readmempcreuniq
0804a000-0804b000 r--p 00001000 fd:01 68388533                           /root/regex/proc/readmempcreuniq
0804b000-0804c000 rw-p 00002000 fd:01 68388533                           /root/regex/proc/readmempcreuniq
097a1000-097c2000 rw-p 097a1000 00:00 0                                  [heap]
b7c00000-b7c21000 rw-p b7c00000 00:00 0
b7c21000-b7d00000 ---p b7c21000 00:00 0
b7d66000-b7d80000 r-xp 00000000 fd:01 65901968                           /lib/libgcc_s.so.1
b7d80000-b7d81000 r--p 00019000 fd:01 65901968                           /lib/libgcc_s.so.1
b7d81000-b7d82000 rw-p 0001a000 fd:01 65901968                           /lib/libgcc_s.so.1
b7d8c000-b7d8e000 rw-p b7d8c000 00:00 0
b7d8e000-b7ee5000 r-xp 00000000 fd:01 65901949                           /lib/libc-2.12.1.so
b7ee5000-b7ee7000 r--p 00157000 fd:01 65901949                           /lib/libc-2.12.1.so
b7ee7000-b7ee8000 rw-p 00159000 fd:01 65901949                           /lib/libc-2.12.1.so
b7ee8000-b7eeb000 rw-p b7ee8000 00:00 0
b7eeb000-b7f1e000 r-xp 00000000 fd:01 65901993                           /lib/libpcre.so.3.12.1
b7f1e000-b7f1f000 r--p 00032000 fd:01 65901993                           /lib/libpcre.so.3.12.1
b7f1f000-b7f20000 rw-p 00033000 fd:01 65901993                           /lib/libpcre.so.3.12.1
b7f29000-b7f2c000 rw-p b7f29000 00:00 0
b7f2c000-b7f48000 r-xp 00000000 fd:01 65901940                           /lib/ld-2.12.1.so
b7f48000-b7f49000 r--p 0001b000 fd:01 65901940                           /lib/ld-2.12.1.so
b7f49000-b7f4a000 rw-p 0001c000 fd:01 65901940                           /lib/ld-2.12.1.so
bf8c1000-bf8d6000 rw-p 7ffffffe9000 00:00 0                              [stack]
Aborted

使用 -Wall 开关编译代码的警告:

xtmtrx@server:~/regex/proc$ gcc -o readmempcreuniq readmempcreuniq.c -lpcre -Wall readmempcreuniq.c: In function 'main': readmempcreuniq.c:83: warning: pointer targets in passing argument 3 of 'pcre_exec' differ in signedness /usr/include/pcre.h:286: note: expected 'const char *' but argument is of type 'unsigned char *' readmempcreuniq.c:85: warning: pointer targets in passing argument 1 of 'pcre_get_substring' differ in signedness /usr/include/pcre.h:297: note: expected 'const char *' but argument is of type 'unsigned char *'

编辑:

根据@stdcall 提示,我使用 efence 编译程序,然后在核心转储上使用 GDB:

xtmtrx@server:~/regex/proc$ ./readmempcreuniq 6036

  Electric Fence 2.1 Copyright (C) 1987-1998 Bruce Perens.
5,
Segmentation fault (core dumped)
xtmtrx@server:~/regex/proc$ gdb ./readmempcreuniq core
GNU gdb (GDB) 7.2-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/regex/proc/readmempcreuniq...done.
[New Thread 6093]
Reading symbols from /lib/libpcre.so.3...(no debugging symbols found)...done.
Loaded symbols for /lib/libpcre.so.3
Reading symbols from /usr/lib/libefence.so.0...(no debugging symbols found)...done.
Loaded symbols for /usr/lib/libefence.so.0
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/libpthread.so.0...(no debugging symbols found)...done.
Loaded symbols for /lib/libpthread.so.0
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./readmempcreuniq 6036'.
Program terminated with signal 11, Segmentation fault.
#0  0x08048ef8 in main (argc=2, argv=0xbfe1d2d4) at readmempcreuniq.c:125
125                                                     uniques[uniques_len] = matches[i];

看来@alk 是对的,问题出在 uniques[uniques_len] = matches[i];

新编辑:

根据@alk 提示,我更改了行:

for ( j = 0; j < uniques_len; j++)

到:

for ( j = 0; j < matches_len; j++)

现在段错误在别处:

Program terminated with signal 11, Segmentation fault.
#0  0x08048ea7 in main (argc=2, argv=0xbfaaa3e4) at readmempcreuniq.c:118
118                                                     if (!strcmp(matches[i], uniques[j]))

最佳答案

在循环的下一轮之前 free() 之后,您永远不会将 matches 重置为 NULL。因此,它在第一轮重新分配后仍保留原始值。

要么在进入内部处理循环之前将其设置为 NULL(在第一遍时冗余),要么将其返回设置为 NULLfree(matches) 之后立即执行。或者,您可以简单地将其设置为具有初始 NULL 值的下一个外部循环的包含范围,但前面提到的更改是我能想到的最小的更改。

示例

matches = NULL; // HERE
while((pairs = pcre_exec(f, f_ext, page, sizeof(page), a, PCRE_NOTEMPTY, vector, vecsize)) >=0)
{
        pcre_get_substring(page, vector, pairs, 0, &buff);
        //printf("%s\n", buff);
        more_matches = realloc(matches, (count+1)* sizeof(*more_matches));
        if (more_matches!=NULL)
        {
                matches=more_matches;
                matches[count++]=buff;
        }
        else
        {
                free(matches);
                puts("Error (re)allocating memory");
                exit(1);
        }
        a = vector[1] + 1;
}

或者……

for (i = 0; i < uniques_len; i++)
{
        printf("%s\n", uniques[i]);
}
free(matches);
matches = NULL; // or HERE
pcre_free(f);

更多内容

继续我所注意到的事情:

这个:

ssize_t ret;
ret = read(fd, page, sizeof(page));

if (ret > 0) {
        page[ret] = '\0';

似乎试图设置一个空字符终止符。如果是这样,您将在填充满的缓冲区上调用未定义的行为。应该是这样的:

ssize_t ret = read(fd, page, sizeof(page)-1); // NOTE SPACE FOR TERM
if (ret > 0) {
        page[ret] = 0;

如果缓冲区的大小是特定的(你选择 4K 是有原因的)它应该是 4097 以确保最大精确的 4K 缓冲区。


还有一个……

您正在阅读的页面,我不能声称它是或不需要像我之前在代码中显示的那样被终止。但是假设它是并且你做了我建议的(或者.. 没有),这看起来也是错误的:

while((pairs = pcre_exec(f, f_ext, page, sizeof(page), a, PCRE_NOTEMPTY, vector, vecsize)) >=0)

这里你传递的是整个缓冲区的大小; 不是您读取的实际数据的大小。我是第一个告诉你我不熟悉 API 的人,但我很确定这应该是:

// notice the length of the buffer passed, ret
while((pairs = pcre_exec(f, f_ext, page, ret, a, PCRE_NOTEMPTY, vector, vecsize)) >=0)

换句话说,在读取数据量过小时,您是在告诉它数据比实际长度要长。同样,我对他们的 API 很天真,但这似乎是合理的。


独特匹配...

希望更容易阅读。

int matches_len = count, uniques_len = 0;
int i = 0, j = 0;

const char *uniques[matches_len];
for (i=0; i < matches_len; ++i)
{
    for (j = 0; j < uniques_len; ++j)
    {
        if (!strcmp(matches[i], uniques[j]))
            break;
    }

    if (j == uniques_len)
        uniques[uniques_len++] = matches[i];
}

for (i = 0; i < uniques_len; ++i)
    printf("%s\n", uniques[i]);

继续...

在每页之后将 count 重置为零。在 free(matches) 之后; matches = NULL; 会是个好地方。

值得注意。一旦文件读取开始失败,您的外循环中就没有退出案例,因此文件上会有很多砰砰声,无法超出其结尾。直到达到限制器计数。


最后的想法

认为这与您想要做的很接近:

#define _LARGEFILE64_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ptrace.h>
#include <pcre.h>
#include <locale.h>
#include <ctype.h>

int main(int argc, char **argv)
{
    // CHANGE TO ACCEPT PROC-ID FROM CMDLINE    
    int pid = 5916;

    setlocale(LC_ALL,"");

    const char *error = NULL;
    int erroffset = 0;
    const char **uniques = NULL;
    size_t uniques_len = 0;

    const char regex[] = "[0-9A-Fa-f]{8}";
    pcre* re = pcre_compile (regex,          /* the pattern */
                    PCRE_MULTILINE|PCRE_DOTALL|PCRE_NEWLINE_ANYCRLF,
                    &error,         /* for error message */
                    &erroffset,     /* for error offset */
                    0);             /* use default character tables */
    if (!re)
    {
        printf("pcre_compile failed (offset: %d), %s\n", erroffset, error);
        return -1;
    }

    // start proc trace
    long ptret = ptrace(PTRACE_ATTACH, pid, 0, 0);
    if (ptret == -1)
    {
        fprintf(stderr, "Ptrace failed: %s\n", strerror(errno));
        exit(1);
    }

    char path[256];
    snprintf(path, sizeof(path), "/proc/%d/maps", pid);
    FILE *maps = fopen(path, "r");
    snprintf(path, sizeof(path), "/proc/%d/mem", pid);
    int mem = open(path, O_RDONLY);

    if(maps && (mem != -1))
    {
        char buf[BUFSIZ + 1];
        while(fgets(buf, BUFSIZ, maps))
        {
            long long unsigned int start, end;
            if (sscanf(buf, "%llx-%llx", &start, &end) != 2)
                break;

            printf("reading %llx - %llx\n", start, end);

            lseek64(mem, start, SEEK_SET);
            while (start < end)
            {
                char page[4096] =  {0};
                int rd = read(mem, page, sizeof(page));
                if (rd < 0)
                    break;

                start += sizeof(page);

                int ov[128] = {0};
                unsigned int ov_len = 0;
                int rc = 0;

                while ((rc = pcre_exec(re, 0, page, (int)(rd), ov_len, 0, ov, 128)) >= 0)
                {
                    int i = 0;
                    for(; i < rc; ++i)
                    {
                        const char *sp = NULL;
                        pcre_get_substring(page, ov, rc, i, &sp);

                        // search unique list
                        size_t j=0;
                        for (;j<uniques_len;++j)
                        {
                            if (!strcmp(sp, uniques[j]))
                            break;
                        }

                        if (uniques_len == j)
                        {
                            const char **tmp = realloc(uniques, (uniques_len+1)*sizeof(*uniques));
                            if (tmp == NULL)
                            {
                                perror("Failed to resize uniques.");
                                pcre_free_substring(sp);
                            }
                            else
                            {
                                uniques = tmp;
                                uniques[uniques_len++] = sp;
                            }
                        }
                        else
                        {   // delete string. not needed
                            pcre_free_substring(sp);
                        }
                    }
                    ov_len = ov[2*(rc-1)]+1;
                }
            }
        }

        fclose(maps);
        close(mem);
    }

    size_t n = 0;
    for (; n<uniques_len; ++n)
    {
        printf("%s\n", uniques[n]);
        pcre_free_substring(uniques[n]);
    }
    printf("total uniques: %lu\n", uniques_len);
    free(uniques);

    ptrace(PTRACE_DETACH, pid, 0, 0);
    return 0;
}

警告。我对这个 API 了解,但我在这里看到并在线简要回顾了一些内容。 YMMV UAYOR。但似乎你一直都拥有它。只是累积独立于页面的唯一值(我认为这仍然是一个问题,页面边界,但那是另一天)。

关于c - Linux POSIX C LibPCRE `double free or corruption (fasttop)` 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21657732/

相关文章:

c - 通过我的 C 程序打开 GnuPlot

PHP无法读取具有读取权限的文件

linux - 检查脚本双重执行

regex - Geany 中的正则表达式搜索中是否可能出现负向后查找?

c - "int *a = (int[2]){0, 2};"究竟是做什么的?

c - 带空指针的动态数组

c - uv_fs_open : flags and mode on Windows

regex - Apache mod_rewrite 正则表达式限制匹配最大量词?

javascript - JS 中的命名组和 OR 运算符

c - stdout 和 STDOUT_FILENO 的区别