检查 mmap 的地址是否正确

发布于 2024-08-26 05:57:30 字数 1815 浏览 7 评论 0原文

我正在编写一个高负载守护进程,它应该在 FreeBSD 8.0 和 Linux 上运行。守护进程的主要目的是传递由其标识符请求的文件。通过向数据库请求将标识符转换为本地文件名/文件大小。然后我使用顺序 mmap() 调用通过 send() 传递文件块。

然而,有时数据库中的文件大小和文件系统上的文件大小不匹配(实际大小<数据库中的大小)。在这种情况下,我已经发送了所有实际数据块,并且当映射下一个数据块时 - mmap 没有返回错误,只是返回通常的地址(我也检查了 errno 变量,它在 mmap 之后等于零)。当守护进程尝试发送此块时,它会出现分段错误。 (此行为肯定在 FreeBSD 8.0 amd64 上发出)

我在打开之前使用安全检查来确保 stat() 调用的大小。然而现实生活向我表明,在极少数情况下仍然可能会出现段错误。

所以,我的问题是有没有办法在取消引用指针之前检查指针是否可访问?当我在 gdb 中打开 core 时,gdb 说给定的地址超出范围。 也许有人可以提出另一种解决方案。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define FILENAME        "./datafile"

int main()
{
    unsigned long i, j;

    srand(time(NULL));
    unsigned long pagesize = sysconf(_SC_PAGESIZE);

    unsigned long basesize = 4 * pagesize;
    unsigned long cropsize = 2 * pagesize;

    // create 4*pagesize sized file
    int f = creat(FILENAME, 0644);
    for (i = 0; i < basesize; i++) {
        unsigned char c = (unsigned char)rand();
        if (write(f, &c, 1) < 1) { perror("write"); break; }
    }
    close(f);

    f = open(FILENAME, O_RDONLY);

    // walk trough file
    unsigned char xor = 0;
    unsigned long offset = 0;
    for (j = 0; j < 4; j++) {
        // trunc file to 2*pagesize
        if (j == 2) truncate(FILENAME, cropsize);

        char *data = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE, f, offset);
        if (data == MAP_FAILED) { perror("mmap"); break; }
        printf("mmap: %lu@%lu for %i\n", pagesize, offset, f);

        for (i = 0; i < pagesize; i++) xor ^= data[i];

        offset += pagesize;
    }

    close(f);

    return 0;
}

I'm writing a high-loaded daemon that should be run on the FreeBSD 8.0 and on Linux as well. The main purpose of daemon is to pass files that are requested by their identifier. Identifier is converted into local filename/file size via request to db. And then I use sequential mmap() calls to pass file blocks with send().

However sometimes there are mismatch of filesize in db and filesize on filesystem (realsize < size in db). In this situation I've sent all real data blocks and when next data block is mapped -- mmap returns no errors, just usual address (I've checked errno variable also, it's equal to zero after mmap). And when daemon tries to send this block it gets Segmentation Fault. (This behaviour is guarantedly issued on FreeBSD 8.0 amd64)

I was using safe check before open to ensure size with stat() call. However real life shows to me that segfault still can be raised in rare situtaions.

So, my question is there a way to check whether pointer is accessible before dereferencing it? When I've opened core in gdb, gdb says that given address is out of bound.
Probably there is another solution somebody can propose.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define FILENAME        "./datafile"

int main()
{
    unsigned long i, j;

    srand(time(NULL));
    unsigned long pagesize = sysconf(_SC_PAGESIZE);

    unsigned long basesize = 4 * pagesize;
    unsigned long cropsize = 2 * pagesize;

    // create 4*pagesize sized file
    int f = creat(FILENAME, 0644);
    for (i = 0; i < basesize; i++) {
        unsigned char c = (unsigned char)rand();
        if (write(f, &c, 1) < 1) { perror("write"); break; }
    }
    close(f);

    f = open(FILENAME, O_RDONLY);

    // walk trough file
    unsigned char xor = 0;
    unsigned long offset = 0;
    for (j = 0; j < 4; j++) {
        // trunc file to 2*pagesize
        if (j == 2) truncate(FILENAME, cropsize);

        char *data = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE, f, offset);
        if (data == MAP_FAILED) { perror("mmap"); break; }
        printf("mmap: %lu@%lu for %i\n", pagesize, offset, f);

        for (i = 0; i < pagesize; i++) xor ^= data[i];

        offset += pagesize;
    }

    close(f);

    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜警司 2024-09-02 05:57:30

当然,我无法从这里证明这一点,但我强烈怀疑您的代码中存在记账错误。如果您调用 mmap 并传入一个大小,并且成功,您不应该收到 SIGSEGV。

我建议您在调查中应用 valgrind。

在许多 Linux 系统上,/proc/PID/maps 将显示哪些区域映射了哪些访问权限。

Of course I can't prove it from here, but I strongly suspect that you just have a book-keeping bug in your code. If you call mmap and pass in a size, and it succeeds, you shouldn't get SIGSEGV.

I recommend that you apply valgrind to your investigation.

On many linux systems /proc/PID/maps will show you what regions are mapped with what access permissions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文