木芽

https://lwn.net/Articles/727322/

更强的用户拷贝白名单化

有很多种方法尝试去搞垮操作系统内核。有一种特别有效的方法,如果可以做到的话,就是攻击用户空间和内核空间之间拷贝数据的操作。如果内核可以被欺骗拷贝到用户空间大量数据,就会造成信息泄漏。反方向,如果攻击者覆盖内核内存会变得更糟。目前,内核已经有一系列抵抗攻击的patch,但仍然有些工作需要汇合进去。

内核里用到的堆内存主要来自于slab分配器。 hardened usercopy patch set [1](已经merge到4.8内核),试图去限制错误拷贝的影响,它主要是通过保证单个的拷贝操作不会跨越一个slab分配好的对象和它的下一个对象的边界。但是内核 会从slab分配器里获得大量的内存对象,并且它没有必要在用户和内核空间拷贝整个对象。假如只需要拷贝对象的部分的话,那么阻止一个无赖的拷贝操作(从那些没有必要暴露的结构里拷贝或者是拷贝进去)将会有用。

举个例子,mm_struct结构描述了一个进程的虚拟地址空间。它饮食了大量的安全敏感信息。其中一个字段为saved_auxy字段被拷贝到用户空间或者到从用户空间拷贝。用于操作这个字段的prctl()函数并不会直接把它拷贝到mm_struct结构里。有一些晦涩的代码(在ELF二进制代码)传递这个字段到copy_to_user()。这样限制拷贝操作就不会有暴露整个结构的风险。

授权保护是hardened usercopy whitelisting [2]的目标。经验说法是我们需要知道这些patch的由来。这些代码是来自于grsecurity/Pax 补丁集。

简单来讲,这些补丁集增强了hardened usercopy whitelisting机器,主要通过一个slab分配好的对象上的用户拷贝区域。只有在这个区域里的数据才可以被拷贝到用户空间(通过copy_to_user()或者copy_from_user())。值得强调的是,对于原来的一些操作比如put_user(),no checking机制已经被应用上。这样这些操作的大小就是固定的,不会再受到攻击的影响。

正常情况下,一个slab缓存使用kmem_cache_create()分配,这个补丁加入一个新的函数

struct kmem_cache kmem_cache_create_usercopy(const char *name, size_t obj_size, size_t align, unsigned long flags, size_t useroffset, size_t usersize, void (ctor)(void *));

参数useroffset和usersize是新的参数。它们描述了从slab缓存中分配对象的的区域。如果usersize为0,就不允许拷贝。从kmem_cache_create()创建的slab和这些函数比如kmalloc()的获取的对象,都是白名单。

不管什么时候,从slab获取的对象被传递到用户空间拷贝函数里。那些要被拷贝的区域将会被检查以保证它们全在白名单里。如果检查失败,内核oops就会发生。

上述设计的一个影响就是那些对象只有单个的区域可以被暴露到用户空间。如果有拷贝不止一个字段的需求,那么这些字段必须合在一起,这样单个的区域才可以覆盖到它们。为了达到这个目的,在白名单化阶段就需要把一些结构重新组织。目前,在补丁集里,许多结构已经被专门白名单化了。

补丁集里的最后一步就是为内存分配创建一个新的标志,GFP_USERCOPY。有一些专门的系统调用来强制内核从用户空间以可控制的大小分配结构。正常来讲,这是没有坏处的,只要大小控制在合理边界即可。但是一些攻击也会根据这个特点来实施。如果那些分配带上了GFP_USERCOPY标志,它们将会从一个分开的slab上获取,这样导致控制堆区域的布局很难。

不太清楚这些补丁什么时候进主线,但目前看,进入主线没有一些严重的障碍。