Linux KASAN使用与实现原理

news2024/11/25 3:30:45

一、KASAN工具使用

KASAN工具:Kernel Address SANitizer(KASAN)是一种动态内存安全错误检测工具,主要功能是检查内存越界访问和使用已释放内存的问题。

1.1 KASAN宏控开关

KASAN有三种模式:1.通用KASAN;2.基于软件标签的KASAN;3.基于硬件标签的KASAN

宏控开关

说明

通用KASAN

CONFIG_KASAN_GENERIC

启用通用KASAN,类似于用户空 间的ASan。这种模式在许多CPU架构上都被支持,但它有明显的性能和内存开销。

软件标签KASAN

CONFIG_KASAN_SW_TAGS

类似于用户空间HWASan,这种模式只支持arm64,但其适度的内存开销允许在内存受限的设备上用真实的工作负载进行测试。

硬件标签KASAN

CONFIG_KASAN_HW_TAGS

现场内存错误检测器或作为安全缓解的模式,这种模式只在支持MTE(内存标签扩展)的arm64 CPU上工作,但它的内存和性能开销很低,因此可以在生产中使用。

注:ARMv8.5-A 以及更高版本的 ARM 架构支持 MTE 功能,可以通过检查 CPUID 寄存器中的宏观架构标识符 (MIDR_EL1) 来确定 CPU 是否支持 MTE

打开宏控开关就可以使能KASAN.

1.2 编译器依赖

软件KASAN模式使用编译时工具在每个内存访问之前插入有效性检查,因此需要一个 提供支持的编译器版本。基于硬件标签的模式依靠硬件来执行这些检查,但仍然需要一个支持内存标签指令的编译器版本。

编译器依赖

通用KASAN

需要GCC 8.3.0版本或更高版本,或者内核支持的任何Clang版本。

软件标签KASAN

需要GCC 11+或者内核支持的任何Clang版本。

硬件标签KASAN

KASAN需要GCC 10+或Clang 12+。

1.3 检测的内存类型

检测的内存类型

通用KASAN

支持在所有的slab、page_alloc、vmap、vmalloc、堆栈和全局内存中查找错误。

软件标签KASAN

支持slab、page_alloc、vmalloc和堆栈内存。

硬件标签KASAN

支持slab、page_alloc和不可执行的vmalloc内存。

注:对于slab,通用KASAN和软KASAN模式都支持SLUB和SLAB分配器,而基于硬件标签的 KASAN只支持SLUB。

二、KASAN实现原理

2.1 linux kernel内存分配器

kernel中存在多种内存分配器,常用的包括Buddy、SLUB 、SLAB 、CMA、SLOB等,而KASAN基于内存分配器实现,因此需要对内存分配器做一些了解。

内存分配器

适用场景

接口函数

SLUB

是当前 Linux 内核中最常用的内存分配器。它适用于大多数通用的内存分配需求,具有较好的性能和灵活性。因此,对于大多数情况下的内存分配操作,SLUB 是首选的分配器。

1)kmalloc(size, flags): 分配指定大小的内存块。size 参数表示要分配的内存大小,flags 参数用于指定内存分配的标志。

2)kmem_cache_alloc(cache, flags): 在给定的缓存 cache 中分配一个对象。

3)kcalloc(n, size, flags): 分配 n 个元素,并将所分配的内存区域初始化为 0。

SLAB

相比SLUB分配器,SLAB 分配器对于某些特殊场景,如需要对齐要求、追踪统计信息等的内存分配操作,可能会更为合适。

1)kmem_cache_alloc(cache, flags): 在给定的缓存 cache 中分配一个对象。

2)kmalloc(size, flags): 分配指定大小的内存块。size 参数表示要分配的内存大小,flags 参数用于指定内存分配的标志。

Buddy

适用于需要页级别连续内存分配的情况,例如页面缓存、物理页面映射等。它将物理内存划分为固定大小的块,并提供对连续块的分配和释放。

alloc_pages(gfp_mask, order): 分配连续的页面。gfp_mask 参数表示内存分配的标志,order 参数表示要分配的页面数量的对数。

CMA

用于为需要大块连续内存的设备驱动程序提供内存分配支持,如 DMA 操作。

dma_alloc_coherent(dev, size, dma_handle, flag): 在 CMA 区域分配一块连续的内存。dev 参数是设备结构体指针,size 参数表示分配的内存大小,dma_handle 是 DMA 地址映射的输出参数,flag 是内存分配的标志。

SLOB

SLOB 分配器适用于嵌入式系统和资源受限环境中,它实现简单且占用较大的内存管理。因此,在资源非常有限的系统中,可以考虑使用 SLOB 分配器。

slob_alloc(size, align, boundary, usage): 分配指定大小的内存块。size 参数表示要分配的内存大小,align 参数表示内存对齐的要求,boundary 参数表示内存分配的边界,usage 是用于标识用途的标志。

2.2 kasan实现原理

kasan可以检测栈内存、堆内存的异常。

栈内存:全局变量、局部变量

堆内存:通过内存分配器(buddy、slub等)进行堆内存申请与释放的时候调用kasan的相关函数,对shadow memory做标记及检测。

2.2.1 kasan如何检测

检测原理:假设内存是从地址8~15一共8 bytes。对应的shadow memory值为5,假如现在访问11(11&7=3 3<5)地址,那么就是可以访问,假如想要访问地址13(13&7=5 5>=5),那么就不能访问,就检测出了问题。

 相关源码如下:

 static __always_inline bool memory_is_poisoned_1(unsigned long addr)
{
        /* 将地址转换成影子内存(每8byte有对应的1byte影子内存,后面分析具体映射关系) */
    s8 shadow_value = *(s8 *)kasan_mem_to_shadow((void *)addr);

/* 如果shadow_value不为0,比如说是负数或者1-7的值,
那么就需要进行判断,看看对应要访问的字节的影子内存对应是否能够访问 */
if (unlikely(shadow_value)) {
        /* KASAN_SHADOW_MASK 的值为7 */
#define KASAN_SHADOW_MASK       (KASAN_SHADOW_SCALE_SIZE - 1)
        /* 这里把虚拟地址 &7 目的就是为了看访问的地址(实际上已经是地址+size)
        是否大于剩余可访问的字节数,注意这里就是kasan的最根本的原理 */
        s8 last_accessible_byte = addr & KASAN_SHADOW_MASK;
        return unlikely(last_accessible_byte >= shadow_value);
    }

        /* shadow 值为 0,8个字节都能被访问,其中一个字节肯定能访问,
        返回false说明kasan没有检测出问题 */
    return false;
}

static __always_inline unsigned long bytes_is_nonzero(const u8 *start,
                    size_t size)
{
while (size) {
/* 这里如果对应的影子地址的值非0,就需要进行权限的判断了 */
        if (unlikely(*start))
            return (unsigned long)start; 
        start++;
        size--;
    }

    return 0;
}

static __always_inline unsigned long memory_is_nonzero(const void *start, const void *end)
{
    unsigned int words;
    unsigned long ret;
    unsigned int prefix = (unsigned long)start % 8;

    if (end - start <= 16)
        return bytes_is_nonzero(start, end - start);

        /* 如果影子地址差了16个以上(对应16*8=128 即size大于128) */
    if (prefix) {
        prefix = 8 - prefix;
/* 将start按8对齐,先把未对齐的前 prefix 长度权限先校验 */
        ret = bytes_is_nonzero(start, prefix);
        if (unlikely(ret))
            return ret;
        start += prefix; /* start补齐成8的倍数 */
    }

        /* 在计算end到start有多少个8字节影子地址(即对应words倍的128长度的实际内存) */
    words = (end - start) / 8;
    while (words) {
        if (unlikely(*(u64 *)start))
            return bytes_is_nonzero(start, 8);
             /* 再次进行权限判断,如果有一个不为0,则说明有问题 */
        start += 8;
        words--;
    }
        /* 最后,将剩余长度的影子地址进行权限判断,同样的有一个不为0,就可能有问题 */
    return bytes_is_nonzero(start, (end - start) % 8);
}

static __always_inline bool memory_is_poisoned_n(unsigned long addr,
                        size_t size)
{
    unsigned long ret;

        /* 判断 内存对应的影子内存中,起始和结束 shadow 值是否都为 0 
                注意:这里影子内存起始就是直接转换来的,而结束比较有意思,
                找的永远是对应地址对应长度的影子地址的下一个影子地址 */
    ret = memory_is_nonzero(kasan_mem_to_shadow((void *)addr),
            kasan_mem_to_shadow((void *)addr + size - 1) + 1);

        /* 根据前面的判断,如果ret不为0(可能的值为负数或1-7),
        就说明内存权限可能有问题,需要进一步判断 */
if (unlikely(ret)) {
        /* 只判断起始地址,连续size长度的最后一字节所在影子内存所在位置的权限值 */
        unsigned long last_byte = addr + size - 1;
        s8 *last_shadow = (s8 *)kasan_mem_to_shadow((void *)last_byte);

                /* 如果ret!=last_shadow 可能是因为在连续的内存检测过程中,
                就已经检测到了一个非法权限,那么肯定就是有问题的 */
                /* ||后面的检测方案和 memory_is_poisoned_1 实现是相同的 */
        if (unlikely(ret != (unsigned long)last_shadow ||
            ((long)(last_byte & KASAN_SHADOW_MASK) >= *last_shadow)))
            return true;
    }
    return false;
}

以上是kasan基于影子内存和对应权限值是如何检测出问题的原理。

kasan是如何维护和标记影子内存所对应的权限值的(详见2.2.2)?以及,kasan的影子内存是如何及映射的(详见2.2.3)?

2.2.2 影子内存标记

内存与释放内存时调用kasan的相关函数。

2.2.2.1 buddy内存分配器检测

Buddy 系统在 free 和 alloc 的时间点上插入了权限设置,所以 buddy 能检测出 use-after-free 类型的错误。调用的函数为:kasan_alloc_pages和kasan_free_pages

alloc 调用流程:

free调用流程:

/*
 * 该函数会为从“addr”开始的“size”字节的阴影内存添加tag。
 内存地址应该对齐到KASAN_SHADOW_SCALE_SIZE */
void kasan_poison_shadow(const void *address, size_t size, u8 value)
{
    void *shadow_start, *shadow_end;
    address = reset_tag(address); //实际就是调用了__tag_reset
    shadow_start = kasan_mem_to_shadow(address);
    shadow_end = kasan_mem_to_shadow(address + size);
        /* 将影子内存中对应的权限值设置成value,
        对于alloc来说,实际上就是设置成0 
        对于free来说,实际上就是设置了0xFF
        */
    __memset(shadow_start, value, shadow_end - shadow_start);
}

void kasan_unpoison_shadow(const void *address, size_t size)
{
    u8 tag = get_tag(address); //实际上就是调用了__tag_get,也就是tag = 0 [CONFIG_KASAN_SW_TAGS 没开】
    address = reset_tag(address);
    kasan_poison_shadow(address, size, tag);
        /* 如果size不是8的倍数,对最后一个影子内存的权限值设置为当前 siez & 7 的大小 */
        /* 对于buddy,申请的都是整页,不会走下面,这个函数slab也会调用,会走到底下 */
    if (size & KASAN_SHADOW_MASK) {
        u8 *shadow = (u8 *)kasan_mem_to_shadow(address + size);
        
        if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
            *shadow = tag; /* 这里走不进来 */
        else
            *shadow = size & KASAN_SHADOW_MASK;
    }
}

void kasan_alloc_pages(struct page *page, unsigned int order)
{
    u8 tag;
    unsigned long i;

    if (unlikely(PageHighMem(page)))
        return;

    tag = random_tag();
    for (i = 0; i < (1 << order); i++)
        page_kasan_tag_set(page + i, tag);
        /* 在这里设置对应影子内存的值 */
    kasan_unpoison_shadow(page_address(page), PAGE_SIZE << order);
}

void kasan_free_pages(struct page *page, unsigned int order)
{
#define KASAN_FREE_PAGE         0xFF

    if (likely(!PageHighMem(page)))
        kasan_poison_shadow(page_address(page),
                PAGE_SIZE << order,
                KASAN_FREE_PAGE);
}

 

2.2.2.2 SLUB内存分配器检测

TBD

2.2.2.3 全局变量检测

开启kasan后,编译器会自动识别全局变量,进行初始化,最终调用__asan_register_globals()

/* The layout of struct dictated by compiler */
struct kasan_global {
    const void *beg;        /* Address of the beginning of the global variable. */
    size_t size;            /* Size of the global variable. */
    size_t size_with_redzone;   /* Size of the variable + size of the red zone. 32 bytes aligned */
    const void *name;
    const void *module_name;    /* Name of the module where the global variable is declared. */
    unsigned long has_dynamic_init; /* This needed for C++ */
#if KASAN_ABI_VERSION >= 4
    struct kasan_source_location *location;
#endif
#if KASAN_ABI_VERSION >= 5
    char *odr_indicator;
#endif
};

static void register_global(struct kasan_global *global)
{
        /* 按照全局变量的大小向8取整,假设size=4,那么对齐大小为8 */
    size_t aligned_size = round_up(global->size, KASAN_SHADOW_SCALE_SIZE);

        /* 全局量的起始地址+大小 设置初值,那么这里就是设置0x4 */
kasan_unpoison_shadow(global->beg, global->size);

#define KASAN_GLOBAL_REDZONE 0xF9
        /* 对地址+对齐为起始地址  假设地址=0,那么这里起始地址就是8,到红区结束填充0Xf9 */
    kasan_poison_shadow(global->beg + aligned_size,
        global->size_with_redzone - aligned_size,
        KASAN_GLOBAL_REDZONE);
}

void __asan_register_globals(struct kasan_global *globals, size_t size)
{
    int i;
    for (i = 0; i < size; i++)
        register_global(&globals[i]);
}
EXPORT_SYMBOL(__asan_register_globals);

 

2.2.2.4 栈内存检测

TBD

2.2.2.5 影子内存标记总结

类型

影子内存标记

检测类型

buddy

初始化:0

释放:0xff

use_after_free

slub

TBD

TBD

global

初始化:0xf9

global-out-of-bounds

stack

TBD

TBD

2.2.3 影子内存映射

TBD

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1617746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

模版初阶【C++】

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

嵌入式Python基础1-2

嵌入式Python基础1-2 条件语句 if elif else 随机数random eval while循环 for循环 水仙花数 循环else list 列表常用方法 增删改查 加排序 append remove pop index() 升序sort(&#xff09;降序 sort(reverseTrue) 反转 reverse&#xff08;&#xff09;…

[Collection与数据结构] PriorityQueue与堆

1. 优先级队列 1.1 概念 前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队列时&#xff0c;可能需要优先级高的元素先出队列&#xff0c;该中场景下&#xff0c;使用队列显然…

持续更新。。。

1、JAVA面试指南 https://javaguide.cn/ 简历优化&#xff1a;突出与应聘职位相关的项目经验、技术栈、技能证书和专业成就。确保简历中的技术关键词与JD&#xff08;Job Description&#xff09;相符。 自我评估&#xff1a;对照目标职位要求&#xff0c;梳理自身Java技能树&…

mac安装nvm管理node(手残流,git下载)

1. 准备 首先电脑里得有brew、git、vscode&#xff0c;看这里安装brew、git&#xff0c;看这里安装vscode。 我本人比较low&#xff0c;mac命令也记不熟&#xff0c;本篇就是git下载nvm&#xff0c;vscode看配置&#xff0c;省心不动脑子就可以了。 2. 清理node 如果mac里没…

Xshell正版免费使用(学生党)

话不多说&#xff0c;上链接 https://www.xshell.com/zh/free-for-home-school/

Introducing Meta Llama 3: The most capable openly available LLM to date

要点 今天&#xff0c;我们推出 Meta Llama 3&#xff0c;这是我们最先进的开源大型语言模型的下一代。Llama 3型号将很快在AWS&#xff0c;Databricks&#xff0c;Google Cloud&#xff0c;Hugging Face&#xff0c;Kaggle&#xff0c;IBM WatsonX&#xff0c;Microsoft Azur…

Linux学习:文件描述符fd

目录 1. 引子2. C语言文件接口2.1 文件的打开与关闭的操作2.2 文件写入读取操作2.3 当前路径 3. 文件I/O操作与系统调用3.1 程序默认打开的文件流3.2 操作系统访问文件的系统调用接口3.2.1 文件打开与关闭操作3.2.2 写入与读取操作 4. 什么是文件描述符fd4.1 进程与文件的关系4…

【笔记】Telephony SIM SPN及运营商名称显示数据来源介绍

来源介绍 网络名称显示 来源及优先级&#xff08;高到低&#xff09; SourceCommentEnhanced Operator Name String(Eons) 名称信息存放&#xff1a; EF_PNN(PLMN Network Name, fid: 6FC5) &#xff1a;LAC和EF_PNN中的Record Identifier EF_OPL(Operator PLMN List, fid: 6FC…

RBA认证是什么?RBA认证的流程是怎么样的

RBA认证&#xff0c;即“责任商业联盟”认证&#xff0c;英文全称是Responsible Business Alliance。这是一个为电子行业或以电子为主要组成部分的行业及其供应链制定的社会责任审核标准。该标准旨在确保工作环境的安全、工人受到尊重并富有尊严、商业营运合乎环保性质并遵守道…

【AI】如何让局域网PC能够访问langchain框架的AI服务

【背景】 在单位内部成功运行了langchain服务&#xff0c;但是发现本地可以用默认8000端口访问&#xff0c;但是局域网内其它机器却无法访问服务页面。 【分析】 首先查看项目文件夹中的server.py。由于这个server.py的存在&#xff0c;我一开始以为langchain整套框架的服务…

哪些因素影响了PCB电路板切割精度?

PCB电路板切割是电子制造过程中一个至关重要的环节&#xff0c;其精度对后续工序的质量和效率具有决定性影响。因此&#xff0c;了解影响PCB电路板切割精度的原因&#xff0c;对于提高电子产品的质量和生产效率具有重要意义。 1. PCB分板机稳定性 PCB分板机的性能直接影响到切…

中国AIGC最值得关注企业产品榜单揭晓!首份应用全景图谱发布

组委会 发自 凹非寺 量子位 | 公众号 QbitAI “你好&#xff0c;新应用&#xff01;” 站在大模型落地元年&#xff0c;是时候喊出这句话了。 从软件APP、智能终端乃至具身智能等等&#xff0c;AIGC开始席卷一切。 大模型玩家、互联网巨头、终端厂商、垂直场景玩家纷纷入场…

web测试基础知识

目录 web系统的基础 web概念(worldwideweb) 网络结构 发展 架构 B/S C/S P2P 工作原理 静态页面 动态页面 web客户端技术 浏览器的核心--渲染引擎 web服务器端技术 web服务器 应用服务器 集群环境 数据库 案例-URL 协议类型 主机名 端口 IP地址 分类 …

Ubuntu 安装 Harbor

一、安装 docker 原文参考传送门 1st 卸载系统自带的 docker 应用 for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done 2nd 设置Docker 的apt源 # Add Dockers official GPG key: sudo…

翻译《The Old New Thing》 - Some reasons not to do anything scary in your DllMain

Some reasons not to do anything scary in your DllMain - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20040127-00/?p40873 Raymond Chen 2004年01月27日 简介 这篇文章讨论了为什么不应该在DLL的DllMain函数中执行复杂的操作 正文 众所…

SAP DMS创建文档根目录操作简介

前面我们已经对DMS的后台进行了系统的配置,本文开始我们对DMS的前台操作进行说明 1、CV01N创建文档 注意:EDIPUBLICROOTFOLDER为根目录的凭证号,不允许更改。 输入好后回车。进入下图所示: 点击文档浏览器,进入下一屏如下图: 注意:点击创建新的私人文件夹按创建是创…

使用CSS3 + Vue3 + js-tool-big-box工具,实现炫酷五一倒计时动效

时间过得真是飞速&#xff0c;很快又要到一年一度的五一劳动节啦&#xff0c;今年五天假&#xff0c;做好准备了吗&#xff1f;今天我们用CSS3 Vue3 一个前端工具库 js-tool-big-box来实现一个炫酷的五一倒计时动效吧。 目录 1 先制作一个CSS3样式 2 Vue3功能提前准备 3…

基于DEAP数据集的四种机器学习方法的情绪分类

在机器学习领域&#xff0c;KNN&#xff08;K-Nearest Neighbors&#xff09;、SVM&#xff08;Support Vector Machine&#xff09;、决策树&#xff08;Decision Tree&#xff09;和随机森林&#xff08;Random Forest&#xff09;是常见且广泛应用的算法。 介绍 1. KNN&am…

windows本地提权--令牌窃取烂土豆UAC

免责声明:本文仅做技术交流与学习,请知法守法,不要乱搞破坏等等... 目录 一.令牌窃取 操作: 1-生成-->上传后门后,让msf上线 2-执行命令 二.烂土豆(MS16-075) 操作: 1-先让MSF上线 2-上传烂土豆 3-执行命令 三.UAC(用户账户控制) 1-MSF模块提权 2-UACME 项目(yy…