设为首页 加入收藏

TOP

KASAN实现原理【转】(一)
2019-09-01 23:09:25 】 浏览:136
Tags:KASAN 实现 原理

1. 前言

KASAN是一个动态检测内存错误的工具。KASAN可以检测全局变量、栈、堆分配的内存发生越界访问等问题。功能比SLUB DEBUG齐全并且支持实时检测。越界访问的严重性和危害性通过我之前的文章(SLUB DEBUG技术)应该有所了解。正是由于SLUB DEBUG缺陷,因此我们需要一种更加强大的检测工具。难道你不想吗?KASAN就是其中一种。KASAN的使用真的很简单。但是我是一个追求刨根问底的人。仅仅止步于使用的层面,我是不愿意的,只有更清楚的了解实现原理才能更加熟练的使用工具。不止是KASAN,其他方面我也是这么认为。但是,说实话,写这篇文章是有点底气不足的。因为从我查阅的资料来说,国内没有一篇文章说KASAN的工作原理,国外也是没有什么文章关注KASAN的原理。大家好像都在说How to use。由于本人水平有限,就根据现有的资料以及自己阅读代码揣摩其中的意思。本文章作为抛准引玉,如果有不合理的地方还请指正。

注:文章代码分析基于linux-4.15.0-rc3。

图片显示有点走形,请点开查看。

2. 简介

KernelAddressSANitizer(KASAN)是一个动态检测内存错误的工具。它为找到use-after-free和out-of-bounds问题提供了一个快速和全面的解决方案。KASAN使用编译时检测每个内存访问,因此您需要GCC 4.9.2或更高版本。检测堆栈或全局变量的越界访问需要GCC 5.0或更高版本。目前KASAN仅支持x86_64和arm64架构(linux 4.4版本合入)。你使用ARM64架构,那么就需要保证linux版本在4.4以上。当然了,如果你使用的linux也有可能打过KASAN的补丁。例如,使用高通平台做手机的厂商使用linux 3.18同样支持KASAN。

3. 如何使用

使用KASAN工具是比较简单的,只需要添加kernel以下配置项。

CONFIG_SLUB_DEBUG=y
CONFIG_KASAN=y

为什么这里必须打开SLUB_DEBUG呢?是因为有段时间KASAN是依赖SLUBU_DEBUG的,什么意思呢?就是在Kconfig中使用了depends on,明白了吧。不过最新的代码已经不需要依赖了,可以看下提交。但是我建议你打开该选项,因为log可以输出更多有用的信息。重新编译kernel即可,编译之后你会发现boot.img(Android环境)大小大了一倍左右。所以说,影响效率不是没有道理的。不过我们可以作为产品发布前的最后检查,也可以排查越界访问等问题。我们可以查看内核日志内容是否包含KASAN检查出的bugs信息。

4. KASAN是如何实现检测的?

KASAN的原理是利用额外的内存标记可用内存的状态。这部分额外的内存被称作shadow memory(影子区)。KASAN将1/8的内存用作shadow memory。使用特殊的magic num填充shadow memory,在每一次load/store(load/store检查指令由编译器插入)内存的时候检测对应的shadow memory确定操作是否valid。连续8 bytes内存(8 bytes align)使用1 byte shadow memory标记。如果8 bytes内存都可以访问,则shadow memory的值为0;如果连续N(1 =< N <= 7) bytes可以访问,则shadow memory的值为N;如果8 bytes内存访问都是invalid,则shadow memory的值为负数。

image

在代码运行时,每一次memory access都会检测对应的shawdow memory的值是否valid。这就需要编译器为我们做些工作。编译的时候,在每一次memory access前编译器会帮我们插入__asan_load##size()或者__asan_store##size()函数调用(size是访问内存字节的数量)。这也是要求更新版本gcc的原因,只有更新的版本才支持自动插入。

mov x0, #0x5678
movk x0, #0x1234, lsl #16
movk x0, #0x8000, lsl #32
movk x0, #0xffff, lsl #48
mov w1, #0x5
bl __asan_store1
strb w1, [x0]

上面一段汇编指令是往0xffff800012345678地址写5。在KASAN打开的情况下,编译器会帮我们自动插入bl __asan_store1指令,__asan_store1函数就是检测一个地址对应的shadow memory的值是否允许写1 byte。蓝色汇编指令就是真正的内存访问。因此KASAN可以在out-of-bounds的时候及时检测。__asan_load##size()和__asan_store##size()的代码在mm/kasan/kasan.c文件实现。

4.1. 如何根据shadow memory的值判断内存访问操作是否valid?

shadow memory检测原理的实现主要就是__asan_load##size()和__asan_store##size()函数的实现。那么KASAN是如何根据访问的address以及对应的shadow memory的状态值来判断访问是否合法呢?首先看一种最简单的情况。访问8 bytes内存。

long *addr = (long *)0xffff800012345678;
*addr = 0;

以上代码是访问8 bytes情况,检测原理如下:

long *addr = (long *)0xffff800012345678;
char *shadow = (char *)(((unsigned long)addr >> 3) + KASAN_SHADOW_OFFSE);
if (*shadow)
    report_bug();
*addr = 0;

红色区域类似是编译器插入的指令。既然是访问8 bytes,必须要保证对应的shadow mempry的值必须是0,否则肯定是有问题。那么如果访问的是1,2 or 4 bytes该如何检查呢?也很简单,我们只需要修改一下if判断条件即可。修改如下:

if (*shadow && *shadow < ((unsigned long)addr & 7) + N); //N = 1,2,4

如果*shadow的值为0代表8 bytes均可以访问,自然就不需要report bug。addr & 7是计算访问地址相对于8字节对齐地址的偏移。还是使用下图来说明关系吧。假设内存是从地址8~15一共8 bytes。对应的shadow memory值为5,现在访问11地址。那么这里的N只要大于2就是invalid。

image

4.2. shadow memory内存如何分配?

在ARM64中,假设VA_BITS配置成48。那么kernel space空间大小是256TB,因此shadow memory的内存需要32TB。我们需要在虚拟地址空间为KASAN shadow memory分配地址空间。所以我们有必要了解一下ARM64 memory layout。

基于linux-4.15.0-rc3的代码分析,我绘制了如下memory layout(VA_BITS = 48)。kernel space起始虚拟

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇高端内存映射之vmalloc分配内存中.. 下一篇痞子衡嵌入式:恩智浦MCU安全加密..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目