Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Linux中测试/验证vmalloc保护页是否正常工作_Linux_Linux Kernel_Stack Overflow - Fatal编程技术网

如何在Linux中测试/验证vmalloc保护页是否正常工作

如何在Linux中测试/验证vmalloc保护页是否正常工作,linux,linux-kernel,stack-overflow,Linux,Linux Kernel,Stack Overflow,我正在学习Linux中的堆栈保护。我发现Linux内核VMAP_STACKconfig参数正在使用保护页机制和vmalloc()来提供堆栈保护。 我试图找到一种方法来检查这个保护页在Linux内核中是如何工作的。我在谷歌上搜索并检查了内核代码,但没有找到代码 另一个问题是如何验证受保护堆栈。 我有一个内核模块使进程的内核堆栈运行不足/溢出,如下所示 static void shoot_kernel_stack(void) { unsigned char *ptr = task_stac

我正在学习Linux中的堆栈保护。我发现Linux内核VMAP_STACKconfig参数正在使用保护页机制和vmalloc()来提供堆栈保护。
我试图找到一种方法来检查这个保护页在Linux内核中是如何工作的。我在谷歌上搜索并检查了内核代码,但没有找到代码

另一个问题是如何验证受保护堆栈。
我有一个内核模块使进程的内核堆栈运行不足/溢出,如下所示

static void shoot_kernel_stack(void)
{
    unsigned char *ptr =  task_stack_page(current);
    unsigned char *tmp = NULL;


    tmp = ptr + THREAD_SIZE + PAGE_SIZE + 0;
//  tmp -= 0x100;
    memset(tmp, 0xB4, 0x10); // Underrun
}
我真的感到内核恐慌,如下所示

[ 8006.358354] BUG: stack guard page was hit at 00000000e8dc2d98 (stack is 00000000cff0f921..00000000653b24a9)
[ 8006.361276] kernel stack overflow (page fault): 0000 [#1] SMP PTI

这是验证保护页的正确方法吗?

使用VMAP\u堆栈Linux功能将线程的内核堆栈映射到VMA。通过虚拟映射堆栈,底层物理页不需要是连续的。可以通过添加保护页来检测跨页溢出。由于VMA后面紧跟着一个保护(除非在分配时传递了VM_NO_guard标志),因此在这些区域中分配的堆栈在堆栈溢出检测方面受益匪浅

分配

线程堆栈在线程创建时使用kernel/fork.c中的alloc_thread_stack_node()进行分配。当激活VMAP_堆栈时,因为根据源代码中的注释:

vmalloc()有点慢,调用vfree()足够多的次数将强制TLB 脸红尝试通过缓存堆栈来最小化调用数量

内核堆栈大小为线程大小(在x86_64平台上等于4页)。在线程创建时调用的分配的源代码是:

static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node)
{
#ifdef CONFIG_VMAP_STACK
    void *stack;
    int i;

[...] // <----- Part which gets a previously cached stack. If no stack in cache
      // the following is run to allocate a brand new stack:

    /*
     * Allocated stacks are cached and later reused by new threads,
     * so memcg accounting is performed manually on assigning/releasing
     * stacks to tasks. Drop __GFP_ACCOUNT.
     */
    stack = __vmalloc_node_range(THREAD_SIZE, THREAD_ALIGN,
                     VMALLOC_START, VMALLOC_END,
                     THREADINFO_GFP & ~__GFP_ACCOUNT,
                     PAGE_KERNEL,
                     0, node, __builtin_return_address(0));
[...]
在上面的代码中,当检测到/overflow下的堆栈时,将调用arch/x86/kernel/traps.c)中定义的handle_stack_overflow()函数:

#ifdef CONFIG_VMAP_堆栈
__可见void\uuu noreturn句柄\u堆栈\u溢出(const char*消息,
结构pt_regs*regs,
无符号长错误(U地址)
{
printk(KERN_EMERG“错误:堆栈保护页在%p处被击中(堆栈为%p..%p)\n”,
(void*)故障地址,当前->堆栈,
(字符*)当前->堆栈+线程大小-1);
骰子(消息,regs,0);
/*绝对确定我们不会回来*/
恐慌(“%s”,消息);
}
#恩迪夫
问题中指出的示例错误消息“BUG:stack guard page hat at…”来自上面的handle_stack_overflow()函数

来自您的示例模块

定义VMAP\u STACK时,将显示任务描述符的STACK\u vm\u area字段,并使用与堆栈关联的VMA地址进行设置。从那里,可以获取有趣的信息:

struct task\u struct*task;
#ifdef CONFIG_VMAP_堆栈
结构vm_结构*vm;
#endif//CONFIG_VMAP_堆栈
任务=当前任务;
printk(“\tKernel stack:0x%lx\n”,(无符号长)(任务->堆栈));
printk(“\tStack-end-magic:0x%lx\n”,*(无符号长*)(任务->堆栈));
#ifdef CONFIG_VMAP_堆栈
vm=任务->堆栈\虚拟机\区域;
printk(“\tstack\u vm\u area->addr=0x%lx\n”,(无符号长)(vm->addr));
printk(“\tstack\u vm\u区域->nr\u页面=%u\n”,vm->nr\u页面);
printk(“\tstack\u vm\u area->size=%lu\n”,vm->size);
#endif//CONFIG_VMAP_堆栈
printk(“\t堆栈中的局部变量:0x%lx\n”,(无符号长)(&task));
nr_pages字段是没有附加保护页的页数。堆栈顶部的最后一个无符号长字符由include/uapi/linux/MAGIC.h中定义的stack\u END\u MAGIC设置为:

参考资料


您在谈论哪些堆栈?这个机制是针对内核端堆栈的,而不是针对用户空间端。是的,你是对的,我正在检查内核端堆栈。并且想知道防护页面是如何设置和工作的。谢谢Rachid提供的详细答案。进一步的问题是VMAP_堆栈如何使保护页成为可能?如何验证保护页是否在保护我的堆栈?我做了一个内核模块,在用户空间的fops.read()中,它将从task_struct获取内核堆栈指针,然后访问stack-PAGE_SIZE或stack+PAGE_SIZE,这是测试它的正确方法吗?(我提到了lkdtm模块)。我用一些额外的信息更新了我的答案,您可以从任务描述器中获取关于堆栈的信息。太棒了,更新后的答案帮助我更多,谢谢。我正在相应地更新我的内核模块。完成后,我可以转而研究另一个关于堆栈\保护\间隙的项目。:-)
static struct vm_struct *__get_vm_area_node(unsigned long size,
        unsigned long align, unsigned long flags, unsigned long start,
        unsigned long end, int node, gfp_t gfp_mask, const void *caller)
{
    struct vmap_area *va;
    struct vm_struct *area;

    BUG_ON(in_interrupt());
    size = PAGE_ALIGN(size);
    if (unlikely(!size))
        return NULL;

    if (flags & VM_IOREMAP)
        align = 1ul << clamp_t(int, get_count_order_long(size),
                       PAGE_SHIFT, IOREMAP_MAX_ORDER);

    area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
    if (unlikely(!area))
        return NULL;

    if (!(flags & VM_NO_GUARD)) // <----- A GUARD PAGE IS ADDED
        size += PAGE_SIZE;

    va = alloc_vmap_area(size, align, start, end, node, gfp_mask);
    if (IS_ERR(va)) {
        kfree(area);
        return NULL;
    }

    setup_vmalloc_vm(area, va, flags, caller);

    return area;
}
#define STACK_END_MAGIC     0x57AC6E9D