如何将结构映射为共享匿名内存?
How to mmap a struct as shared anonymous memory?
我正在尝试将结构 mmap 为匿名共享内存,以便它可以在多个 child 进程之间共享。但是,每当我访问创建它的函数之外的结构时,我都会遇到段错误。代码源自 trinity fuzzer,可在此处找到 https://github.com/kernelslacker/trinity .
首先我们有一个 header 的摘录,它声明并定义了我们将要使用的结构。注意 shm 在 struct 声明下被声明为全局变量。
struct shm_s {
struct childdata **children;
struct stats_s stats;
unsigned int running_childs;
/* rng related state */
unsigned int seed;
#ifdef ARCH_IS_BIARCH
/* Check that 32bit emulation is available. */
unsigned int syscalls32_succeeded;
unsigned int syscalls32_attempted;
#endif
/* pids */
pid_t mainpid;
pid_t last_reaped;
/* various flags. */
enum exit_reasons exit_reason;
bool dont_make_it_fail;
bool spawn_no_more;
bool ready;
bool postmortem_in_progress;
/* Use dispatch queue instead of locks. */
/* main<>watchdog serial queue, for reap_child()
* provides exclusion so they don't both try at the same time. */
dispatch_queue_t reaper_queue;
/* to protect from multiple child processes from
* trying to disable the same syscall at the same time. */
dispatch_queue_t syscalltable_queue;
/* child<>child serial queue, used so only one child spews debug output */
dispatch_queue_t bugQueue;
/* global debug flag.
* This is in the shm so we can do things like gdb to the main pid,
* and have the children automatically take notice.
* This can be useful if for some reason we don't want to gdb to the child.
*/
bool debug;
};
extern struct shm_s *shm;
在 c 文件中,我们有结构的创建和初始化函数。
struct shm_s *shm;
#define SHM_PROT_PAGES 30
void create_shm(void)
{
void *p;
void *redbefore, *redafter;
unsigned int shm_pages;
unsigned int wholesize;
/* round up shm to nearest page size */
shm_pages = ((sizeof(struct shm_s) + page_size - 1) & PAGE_MASK) / page_size;
wholesize = (SHM_PROT_PAGES + shm_pages + SHM_PROT_PAGES) * page_size;
/* Waste some address space to set up some "protection" near the SHM location. */
p = alloc_shared(wholesize);
redbefore = p;
redafter = p + (SHM_PROT_PAGES + shm_pages) * page_size;
/* set the redzones. */
memset(redbefore, 0x77, SHM_PROT_PAGES * page_size);
memset(redafter, 0x88, SHM_PROT_PAGES * page_size);
/* set the redzones to PROT_NONE */
mprotect(redbefore, SHM_PROT_PAGES * page_size, PROT_NONE);
mprotect(redafter, SHM_PROT_PAGES * page_size, PROT_NONE);
/* clear the whole shm. */
shm = p + (SHM_PROT_PAGES * page_size);
memset(shm, 0, shm_pages * page_size);
printf("shm: redzone:%p. shmdata:%p. redzone:%p end:%p.\n",
redbefore, shm, redafter, p + wholesize);
}
void init_shm(void)
{
unsigned int i;
printf("shm is at %p\n", shm);
shm->stats.total_syscalls_done = 1;
shm->children = zmalloc(max_children * sizeof(struct childdata *));
shm->bugQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
shm->reaper_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
shm->syscalltable_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
for_each_child(i)
{
struct childdata *child;
child = alloc_shared(sizeof(struct childdata));
shm->children[i] = child;
child->pid = EMPTY_PIDSLOT;
child->logfile = NULL;
}
}
这里是 alloc_shared 函数,它实际上创建了共享内存。
void * alloc_shared(unsigned int size)
{
void *ret;
ret = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (ret == MAP_FAILED) {
printf("mmap %u failure\n", size);
exit(EXIT_FAILURE);
}
/* poison, to force users to set it to something sensible. */
memset(ret, rand(), size);
return ret;
}
最后 create_shm 和 init_shm 函数在单独的文件中像这样调用。
create_shm();
init_shm();
程序一到shm->stats.total_syscalls_done = 1就崩溃;里面 init_shm。我打印了 shm 的地址,它在 alloc_shared 和 init_shm 中创建后是相同的,所以我认为指针没有被破坏。
无论哪种方式,我都不明白为什么我无法访问 create_shm().
之外的结构
根据OP在评论中的回答,printf的输出是:
shm: redzone:0x10a0ad000. shmdata:0x10a0cb000. redzone:0x10a0cb000 end:0x10a0e9000
注意 shmdata
与 (redafter
) 之后的 redzone 在同一地址,两者都在 0x10a0cb000
。这意味着 shm_pages
为零。
这一行最有可能是问题所在:
shm_pages = ((sizeof(struct shm_s) + page_size - 1) & PAGE_MASK) / page_size;
我猜 PAGE_MASK
是 0xfff
。如果是这样,那么该表达式将始终计算为 0。正如 chmike 指出的那样,由于您无论如何都除以 page_size,因此根本不需要使用 PAGE_MASK
。在这种情况下,您的代码应为:
shm_pages = (sizeof(struct shm_s) + page_size - 1) / page_size;
我正在尝试将结构 mmap 为匿名共享内存,以便它可以在多个 child 进程之间共享。但是,每当我访问创建它的函数之外的结构时,我都会遇到段错误。代码源自 trinity fuzzer,可在此处找到 https://github.com/kernelslacker/trinity .
首先我们有一个 header 的摘录,它声明并定义了我们将要使用的结构。注意 shm 在 struct 声明下被声明为全局变量。
struct shm_s {
struct childdata **children;
struct stats_s stats;
unsigned int running_childs;
/* rng related state */
unsigned int seed;
#ifdef ARCH_IS_BIARCH
/* Check that 32bit emulation is available. */
unsigned int syscalls32_succeeded;
unsigned int syscalls32_attempted;
#endif
/* pids */
pid_t mainpid;
pid_t last_reaped;
/* various flags. */
enum exit_reasons exit_reason;
bool dont_make_it_fail;
bool spawn_no_more;
bool ready;
bool postmortem_in_progress;
/* Use dispatch queue instead of locks. */
/* main<>watchdog serial queue, for reap_child()
* provides exclusion so they don't both try at the same time. */
dispatch_queue_t reaper_queue;
/* to protect from multiple child processes from
* trying to disable the same syscall at the same time. */
dispatch_queue_t syscalltable_queue;
/* child<>child serial queue, used so only one child spews debug output */
dispatch_queue_t bugQueue;
/* global debug flag.
* This is in the shm so we can do things like gdb to the main pid,
* and have the children automatically take notice.
* This can be useful if for some reason we don't want to gdb to the child.
*/
bool debug;
};
extern struct shm_s *shm;
在 c 文件中,我们有结构的创建和初始化函数。
struct shm_s *shm;
#define SHM_PROT_PAGES 30
void create_shm(void)
{
void *p;
void *redbefore, *redafter;
unsigned int shm_pages;
unsigned int wholesize;
/* round up shm to nearest page size */
shm_pages = ((sizeof(struct shm_s) + page_size - 1) & PAGE_MASK) / page_size;
wholesize = (SHM_PROT_PAGES + shm_pages + SHM_PROT_PAGES) * page_size;
/* Waste some address space to set up some "protection" near the SHM location. */
p = alloc_shared(wholesize);
redbefore = p;
redafter = p + (SHM_PROT_PAGES + shm_pages) * page_size;
/* set the redzones. */
memset(redbefore, 0x77, SHM_PROT_PAGES * page_size);
memset(redafter, 0x88, SHM_PROT_PAGES * page_size);
/* set the redzones to PROT_NONE */
mprotect(redbefore, SHM_PROT_PAGES * page_size, PROT_NONE);
mprotect(redafter, SHM_PROT_PAGES * page_size, PROT_NONE);
/* clear the whole shm. */
shm = p + (SHM_PROT_PAGES * page_size);
memset(shm, 0, shm_pages * page_size);
printf("shm: redzone:%p. shmdata:%p. redzone:%p end:%p.\n",
redbefore, shm, redafter, p + wholesize);
}
void init_shm(void)
{
unsigned int i;
printf("shm is at %p\n", shm);
shm->stats.total_syscalls_done = 1;
shm->children = zmalloc(max_children * sizeof(struct childdata *));
shm->bugQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
shm->reaper_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
shm->syscalltable_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
for_each_child(i)
{
struct childdata *child;
child = alloc_shared(sizeof(struct childdata));
shm->children[i] = child;
child->pid = EMPTY_PIDSLOT;
child->logfile = NULL;
}
}
这里是 alloc_shared 函数,它实际上创建了共享内存。
void * alloc_shared(unsigned int size)
{
void *ret;
ret = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (ret == MAP_FAILED) {
printf("mmap %u failure\n", size);
exit(EXIT_FAILURE);
}
/* poison, to force users to set it to something sensible. */
memset(ret, rand(), size);
return ret;
}
最后 create_shm 和 init_shm 函数在单独的文件中像这样调用。
create_shm();
init_shm();
程序一到shm->stats.total_syscalls_done = 1就崩溃;里面 init_shm。我打印了 shm 的地址,它在 alloc_shared 和 init_shm 中创建后是相同的,所以我认为指针没有被破坏。 无论哪种方式,我都不明白为什么我无法访问 create_shm().
之外的结构根据OP在评论中的回答,printf的输出是:
shm: redzone:0x10a0ad000. shmdata:0x10a0cb000. redzone:0x10a0cb000 end:0x10a0e9000
注意 shmdata
与 (redafter
) 之后的 redzone 在同一地址,两者都在 0x10a0cb000
。这意味着 shm_pages
为零。
这一行最有可能是问题所在:
shm_pages = ((sizeof(struct shm_s) + page_size - 1) & PAGE_MASK) / page_size;
我猜 PAGE_MASK
是 0xfff
。如果是这样,那么该表达式将始终计算为 0。正如 chmike 指出的那样,由于您无论如何都除以 page_size,因此根本不需要使用 PAGE_MASK
。在这种情况下,您的代码应为:
shm_pages = (sizeof(struct shm_s) + page_size - 1) / page_size;