Linux KASAN使用与实现原理

一、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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Apple II首席设计师为中国家庭设计,鹿客指脉锁S6 Max引领科技美学

智能门锁设计正在步入一个科技与艺术交织的美学时代。鹿客科技认为&#xff0c;智能门锁的设计理念是将锁视为人类与仿生形状之间的接口&#xff0c;将门视为几何建筑的一部分&#xff0c;产品设计应该通过提供诱人且用户友好的“触摸和感觉”来传达这种转变。 鹿客近日发布的最…

clickhouse学习笔记04

ClickHouse高可用之ReplicatedMergeTree引擎介绍 ClickHouse高可用架构准备-环境说明和ZK搭建 RPM安装ClickHouse 上传我们的clickhouse rpm文件。 安装&#xff1a; 中途需要输入用户名和密码 可以不设置 直接回车。 启动&#xff1a; 查看状态&#xff1a; 查看端口是否占用…

嵌入式s5p5818核心板介绍

底板寻址空间介绍 s5p6818 寻址空间采用统一编址方式进行管理 寻址空间映射图&#xff1a; 独立寻址&#xff1a;片内片外存储器只能选择其中一个 统一寻址&#xff1a;片内片外存储器都能使用&#xff0c;且使用的是同一片连续的寻址空间 reserved保留&#xff0c;Normaol …

晶振在PCB设计中,要注意哪些事项?

晶振(Crystal Oscillator)在PCB(Printed Circuit Board&#xff0c;印刷电路板)设计中扮演着至关重要的角色&#xff0c;因为它提供了稳定的时钟信号&#xff0c;这是许多电子设备正常运行的基础。在设计含有晶振的PCB时&#xff0c;应该注意以下几个关键事项&#xff1a; 1. …

实用电路图轻松掌握,一通百通 | 百能云芯

通过以下各种各样的实用电路&#xff0c;大家可以了解元器件的结构、特性、动作原理及电路的基本控制方式&#xff0c;掌握一些控制规律&#xff0c;这样的话&#xff0c;在日后的电路识图中就能融会贯通&#xff0c;一通百通。 文章中的电路图有难有易&#xff0c;有些图现在…

2024066期传足14场胜负前瞻

2024066期售止时间为4月24日&#xff08;周三&#xff09;17点30分&#xff0c;敬请留意&#xff1a; 本期1.5以下赔率5场&#xff0c;1.5-2.0赔率3场&#xff0c;其他场次是平半盘、平盘。本期14场难度中等。以下为基础盘前瞻&#xff0c;大家可根据自身判断&#xff0c;复选增…

想冲宇宙厂,直接挂了。。。

宇宙厂实际是字节&#xff0c;这个称呼是因为字节跳动主宰了宇宙内一切App&#xff0c;有点家大业大的意思。 今天分享一位字节春招凉经&#xff0c;问了一些数据库和Java八股&#xff0c;没出算法题&#xff0c;直接挂了&#xff0c;竟然最喜欢出算法题的字节&#xff0c;这次…

Web3革命:区块链如何重塑互联网

引言 互联网的发展已经深刻地改变了我们的生活方式&#xff0c;而现在&#xff0c;Web3和区块链技术正在为我们提供一个全新的数字世界的视角。本文将带你深入了解Web3的核心概念、技术特性以及它如何正在重塑我们的互联网体验。 从Web1.0到Web3&#xff1a;数字革命的演进 W…

Git TortoiseGit 详细安装使用教程

前言 Git 是一个免费的开源分布式版本控制系统&#xff0c;是用来保存工程源代码历史状态的命令行工具&#xff0c;旨在处理从小型到非常大型的项目&#xff0c;速度快、效率高。《请查阅Git详细说明》。TortoiseGit 是 Git 的 Windows Shell 界面工具&#xff0c;基于 Tortoi…

在Visual Studio配置C++的netCDF库的方法

本文介绍在Windows电脑的Visual Studio软件中&#xff0c;配置C 语言最新版netCDF库的方法。 netCDF&#xff08;Network Common Data Form&#xff09;是一种用于存储、访问和共享科学数据的文件格式和库&#xff0c;其提供了一种灵活的方式来组织、描述和存储多维数据&#…

Python-VBA函数之旅-id函数

目录 一、id函数的常见应用场景&#xff1a; 二、id函数使用注意事项&#xff1a; 1、id函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a;神奇夜光杯-CSDN博客 一、id函数的常见应用场景&#xff1a; id函…

【Linux开发实用篇】备份与恢复

备份 实体机无法做快照&#xff0c;我们可以使用备份和恢复技术 第一种方式 把需要的文件&#xff08;或者分区&#xff09;用TAR打包就好&#xff0c;下次恢复的时候进行解压 第二种方式 使用dump 和 restore 指令&#xff1a; 首先安装这两个指令 yum -y install dump, …

2024平替电容笔买哪个品牌好?iPad电容笔全能榜单热门款TOP5分享!

2024年&#xff0c;随着科技的不断发展和消费者对生活品质的追求&#xff0c;电容笔作为一种创新的无纸化工具&#xff0c;逐渐走进人们的生活和工作中。然而&#xff0c;在电容笔市场的繁荣背后&#xff0c;也隐藏着品质良莠不齐的现象。众多品牌为了追求利润&#xff0c;推出…

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…

2024/4/23 C++day1

有以下定义&#xff0c;说明哪些量可以改变哪些不可以改变&#xff1f; const char *p; 指针可以改变 值不可以改变 const (char *) p; 语法错误 char *const p; 指针不可以改变 值可以改变 const char* const p; 指针和值…

做抖音小店正确起店的方式,新店铺想快速爆单,步骤就这几个

大家好&#xff0c;我是电商笨笨熊 开通了抖音小店&#xff0c;但是店铺一直没有流量&#xff1b; 很多新手玩家进入抖店后都会遇到这样那样的问题&#xff0c;烦恼的事情一大堆&#xff1b; 没关系&#xff0c;今天我们就来聊聊新店铺该怎么快速起店&#xff0c;新手如何做…

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

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

莫名锁表? --- mysql的事务隔离级别

前言 系统响应超时 系统访问数据库特别慢 莫名提示锁等待超时 数据库锁表 事务长时间等锁&#xff0c;直到超时 以上问题都可能是事务锁表导致的 问题 今天测试反馈系统批量处理莫名提示锁等待超时&#xff0c;再次操作查看数据库事务确实存在等锁情况&#xff0c;甚至死锁。…

模版初阶【C++】

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

NLP自然语言处理_序章

开一个新篇章&#xff0c;立一个flag&#xff0c;用一段时间来学习一下NLP&#xff0c;涨涨见识。 准备以B站 机器学习算法到transformer神经网络模型应用视频作为入门&#xff0c;此分类专门用于记录学习过程中的知识点以备自用。 一、何为NLP自然语言处理&#xff1f; NLP…