[FAQ18285]如何用MMU保护slub?

来源:互联网 发布:java写界面 编辑:程序博客网 时间:2024/05/14 09:13
[DESCRIPTION]
看过[FAQ14614]如何用MMU保护buddy system?就知道如果踩到空洞就会直接崩溃,是检测和解决问题的最好方法,不过踩坏不一定就落在空洞,这也导致了调试难度。
在quick start里有专题讲解踩内存问题:
  • MediaTek On-Line> Quick Start> 踩内存专题分析
kernel里大范围使用的是slub,如果slub被踩坏,不一定会落在空洞。有没有将slub隔离开来呢?如果可以,那么slub踩坏被抓到的机会就更大了。
对于32位kernel来说,不太可能了,因为4G虚拟空间,kernel占1G,能用的实在太小了。而对于64位kernel来说,512G的虚拟空间,足够我们保护slub。下面讲解方法。
 
[SOLUTION]
只应用于64bit kernel
1. 在slub找buddy system申请内存后,将page映射到隔离的地址上:
[C/C++]hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <linux/vmalloc.h> /* 添加这个头文件 */
 
 
 
staticstruct page *allocate_slab(structkmem_cache *s, gfp_t flags, intnode)
 
{
 
    ......
 
    if(flags&__GFP_WAIT) {
 
        /* 添加如下代码 */
 
        if(page) {
 
            constint num = 1 << oo_order(oo);
 
            structvm_struct tmp_area;
 
            structpage *pages[num];
 
            inti;
 
            //extern int set_memory_invalid(unsigned long addr, int numpages);
 
            //set_memory_invalid((unsigned long)lowmem_page_address(page), num); /* 如果有开启MMU保护buddy system([FAQ14614]如何用MMU保护buddy system?),则添加这行代码!!! */
 
            __SetPagePrivate(page);
 
            __SetPageSlab(page);
 
            tmp_area.addr = page_address(page);
 
            tmp_area.size = num * PAGE_SIZE + PAGE_SIZE;
 
            for(i = 0; i < num; i++) {
 
                pages[i] = &page[i];
 
            }
 
            map_vm_area(&tmp_area, PAGE_KERNEL, pages);
 
        }
 
        /* 添加结束 */
 
        local_irq_disable();
 
    }
 
    if(!page)
 
        returnNULL;
 
    ......
 
}
 
2. slub释放内存归还给buddy system时,需要撤销映射:
[C/C++]hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
staticvoid __free_slab(structkmem_cache *s, structpage *page)
 
{
 
    ......
 
    mod_zone_page_state(page_zone(page), (s->flags & SLAB_RECLAIM_ACCOUNT) ? NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE, -pages);
 
    /* 添加如下代码 */
 
    if(PagePrivate(page)) {
 
        unmap_kernel_range((unsignedlong)page_address(page), (1 << order) * PAGE_SIZE);
 
        ClearPagePrivate(page);
 
    }
 
    /* 添加结束 */
 
    __ClearPageSlabPfmemalloc(page);
 
    __ClearPageSlab(page);
 
    ......
 
}
 
3. slub.c里定义__phys_to_virt(),完成slub隔离:
[C/C++]hide
1
2
3
4
5
6
7
8
9
10
11
12
13
unsignedlong__phys_to_virt(phys_addr_t phys)
 
{
 
    if(slab_is_available() && (compound_head(phys_to_page(phys))->flags&((1UL << PG_slab)|(1UL << PG_private))) == ((1UL << PG_slab)|(1UL << PG_private))) {
 
        return(unsigned long)(phys - PHYS_OFFSET + PAGE_OFFSET)|SLUB_ADDR_FLAG;
 
    }
 
    return(unsigned long)(phys - PHYS_OFFSET + PAGE_OFFSET);
 
}
 
4. kernel-3.18/arch/arm64/include/asm/memory.h调整__virt_to_phys():
[C/C++]hide
1
2
3
#define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET))
 
#define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))
修改为:
[C/C++]hide
1
2
3
4
5
6
7
8
9
10
11
#define SLUB_ADDR_FLAG 0x400000000UL /* 8G shift */
 
 
 
#define __virt_to_phys(x) (((phys_addr_t)(x)&~SLUB_ADDR_FLAG) - PAGE_OFFSET + PHYS_OFFSET)
 
#ifndef __ASSEMBLY__
 
unsignedlong__phys_to_virt(phys_addr_t phys);
 
#endif
 
特征
  • 从slub申请的内存(kmalloc等)地址大于0xffffffc400000000。
  • 0xffffffc400000000起始的内存和0xffffffc000000000的内存映射的是同一物理地址。
  • 只有slub从buddy system申请的内存才会映射到0xffffffc400000000起始的内存。
0 0
原创粉丝点击