KASan部署、使用与原理分析

文章目录

  • 前言
  • 1、概述
  • 2、使用方法
  • 3、测试用例
    • 3.1、检测加载的内核模块
    • 3.2、检测调用的内核模块
    • 3.3、通过系统调用检测
    • 3.4、检测编译到Linux内核中的内核模块
  • 4、工作原理
    • 4.1、影子内存(Shadow Memory)
    • 4.2、内存状态(Memory States)
    • 4.3、红色区域(Redzones)
    • 4.4、KASan的实现
  • 5、参考文献
  • 总结


前言

  本博客的主要内容为KASan的部署、使用与原理分析。本博文内容较长,因为涵盖了KASan的几乎全部内容,从部署的详细过程到如何使用KASan对Linux内核中的内存错误进行检测,以及其对Linux内核中的内存错误进行检测的原理分析,相信认真读完本博文,各位读者一定会对KASan有更深的了解。以下就是本篇博客的全部内容了。


1、概述

  KASan(Kernel Address Sanitizer)是Linux内核中的一种内存错误检测工具,旨在帮助开发者发现和修复内核空间的内存访问错误。下面是关于KASan的一些基本介绍:

  1. 功能和目的
    • 内存错误检测:KASan主要用于检测内核空间代码中的内存错误,包括但不限于内存越界访问、使用未初始化的内存、以及一些特定的内存使用后释放问题。
    • 开发者工具:作为开发者工具,KASan帮助开发者在早期发现并修复潜在的内存错误,提高内核代码的稳定性和安全性。
  2. 工作原理
    • 内存影子(Shadow Memory):KASan使用内存影子技术来实现内存错误检测。它为每个分配的内存区域维护一个影子内存地图,记录了每个内存字节的分配状态和元数据信息。
    • 访问检查:当程序访问内存时,KASan会同时访问影子内存地图,检查对应的内存访问是否有效。例如,检测是否存在未分配或已释放的内存访问,以及是否发生了内存越界访问。
    • 报告和跟踪:当检测到内存错误时,KASan会生成报告并记录相关信息,帮助开发者追踪和修复问题。
  3. 使用方法
    • 配置和编译:KASan的启用需要在编译内核时进行配置,通常通过修改内核配置文件(如“.config”)来启用相关选项(如CONFIG_KASAN=y)。
    • 运行时检测:一旦内核加载并运行,KASan在检测到内存错误时会向系统日志(如dmesg)输出相应的错误信息,包括错误类型、位置和堆栈跟踪等。
  4. 适用性和限制
    • 适用范围:KASan主要用于内核空间的内存错误检测,适用于内核代码本身、驱动程序和内核模块等。
    • 性能开销:KASan在运行时会增加一定的内存和CPU开销,特别是在大规模的内核代码或高频率的内存访问场景下可能会有显著的性能影响。

  总之,KASan是Linux内核中的一种工具,专用于检测和报告内核空间代码中的内存错误。通过影子内存技术,KASan能够有效地检测内存访问越界、未初始化的内存使用等问题,并生成详细的报告帮助开发者迅速定位和修复这些问题。此外KASan工具基于C语言和汇编语言开发。

2、使用方法

  由于KASan是Linux内核自带的一个工具,所以需要在编译内核时开启该工具,故我们会首先编译一个新的内核已开启KASan工具的功能,最后验证其是否开启成功。

  1. 首先使用如下命令查看当前系统的Linux内核版本:
$ uname -r
  1. 可以发现,当前系统的Linux内核版本为4.15.0-45-generic:
    在这里插入图片描述

  2. 然后使用如下命令查看当前系统的版本:

$ lsb_release -a
  1. 可以发现,当前系统的版本为Ubuntu 16.04.6 LTS:
    在这里插入图片描述

  2. 然后下载安装编译内核所需要的软件:

$ sudo apt-get update
$ sudo apt-get install g++ gcc make build-essential libncurses-dev bison flex libssl-dev libelf-dev -y
$ sudo apt-get install openssl zlibc minizip libidn11-dev libidn11 libncurses5-dev -y
  1. 然后下载一个新的Linux内核(在这里我采用的Linux内核版本为4.9.3)源码压缩包,并将其解压,最后进入解压好的Linux内核源码目录:
$ cd /usr/src/
$ sudo wget https://mirrors.aliyun.com/linux-kernel/v4.x/linux-4.9.3.tar.gz
$ sudo tar -zxvf linux-4.9.3.tar.gz
$ cd linux-4.9.3/
  1. 然后顺序执行如下命令来进行编译内核之前的清理工作:
$ sudo make mrproper
$ sudo make clean
  1. 然后执行如下命令来启动内核的编译配置界面:
$ sudo make menuconfig
  1. 然后按如下图所示进行选择,最后按一下“Enter”键:
    在这里插入图片描述

  2. 然后按如下图所示进行选择,最后按一下“Enter”键:
    在这里插入图片描述

  3. 然后按如下图所示进行选择,最后按一下“Enter”键:
    在这里插入图片描述

  4. 然后按如下图所示进行选择,最后按一下“Enter”键:
    在这里插入图片描述

  5. 然后按如下图所示进行选择,最后按一下“Space”键:
    在这里插入图片描述

  6. 然后连续按“Esc”键直到出现如下图所示的界面,然后按如下图所示进行选择,最后按一下“Enter”键,这样就开启了当前待编译内核的KASan功能:
    在这里插入图片描述

  7. 然后执行如下命令来编译内核:

$ sudo make -j$(nproc)
  1. 然后执行如下命令来安装编译好的内核模块:
$ sudo make modules_install
  1. 然后执行如下命令来安装编译好的内核:
$ sudo make install
  1. 然后重启系统:
$ reboot
  1. 在重启系统的过程中按“Esc”键,即可进入如下图所示界面,我们只需要如下图红框和红箭头处所示进行选择即可:
    在这里插入图片描述

  2. 然后按如下图所示进行选择,然后按一下“Enter”键,目的是使用我们刚刚编译好的Linux内核加载以进入系统:
    在这里插入图片描述

  3. 然后执行如下命令来查看系统内核是否更换成功:

$ uname -r
  1. 可以发现,当前系统的内核已经更换为4.9.3:
    在这里插入图片描述

  2. 然后执行如下命令来查看KASan是否成功开启:

$ grep CONFIG_KASAN /boot/config-$(uname -r)
  1. 执行上面的命令后,出现如下图所示的内容即代表KASan开启成功:
    在这里插入图片描述

3、测试用例

3.1、检测加载的内核模块

  在本章节,我们将通过编写和运行一个简单的内核模块,故意触发一个内存越界访问来对KASan进行测试(注意:该测试用例使用的Linux内核版本为4.4.252)。

  1. 首先来到当前用户的根目录,并新建一个代码文件,然后将其打开:
$ cd ~
$ touch kasan_test.c
$ gedit kasan_test.c
  1. 然后在打开的文件中输入如下内容,最后保存修改后退出:
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <asm/uaccess.h>
#include <linux/syscalls.h>
#include <linux/kernel.h>
#include <linux/slab.h>
 
static int __init kasan_test_init(void) 
{ 
    char *ptr; 
    size_t size = 124; 
 
    printk(KERN_INFO "KASan test module loaded\n");
    ptr = kmalloc(size, GFP_KERNEL); 
    if (!ptr) { 
        printk(KERN_ERR "Allocation failed\n"); 
        return -1; 
    } 
    printk(KERN_INFO "ptr address: %p\n", ptr);
 
    ptr[size] = 'x'; 
    printk(KERN_INFO "Accessing out-of-bounds memory: ptr[%zu] address: %p\n", size, ptr + size);
 
    kfree(ptr);
    return 0;
}
 
 
static void __exit kasan_test_exit(void)
{
    printk(KERN_INFO "KASan test module unloaded\n");
}
 
module_init(kasan_test_init);
module_exit(kasan_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("IronmanJay");
MODULE_DESCRIPTION("KASan test module");

该内核模块包含一个初始化函数和一个退出函数。在初始化函数kasan_test_init中,分配了一块大小为124字节的内存,然后故意访问越界位置(第124字节,即ptr[size])。函数会打印内存地址及越界访问地址,最后释放分配的内存。退出函数kasan_test_exit仅打印一个模块卸载信息。通过这些步骤,可以测试KASan(Kernel Address Sanitizer)是否能够检测到内存越界访问。

  1. 然后再在当前目录下创建一个Makefile文件,并将其打开:
$ touch Makefile
$ gedit Makefile
  1. 在打开的文件中输入如下内容,最后保存修改后退出:
obj-m += kasan_test.o

# 添加调试信息和警告
EXTRA_CFLAGS += -g -Wall

# 指定内核源码路径
KERNEL_SOURCE := /lib/modules/$(shell uname -r)/build

# 默认目标
all:
	make -C $(KERNEL_SOURCE) M=$(PWD) modules

# 清理目标
clean:
	make -C $(KERNEL_SOURCE) M=$(PWD) clean
  1. 然后顺序执行下面两条命令,以编译和加载该内核模块:
$ make
$ sudo insmod kasan_test.ko
  1. 然后执行如下命令来查看内核日志以确认KASan是否捕捉到我们设计好的内存越界访问:
$ dmesg -T
  1. 执行上面的命令后,会在命令行窗口打印如下图所示的信息,这就说明我们自定义的内核模块中的内存越界错误已经被KASan捕获到了:
    在这里插入图片描述

3.2、检测调用的内核模块

  在本章节,我们将使用用户态的二进制程序来调用存在内存越界错误的内核模块,从而故意触发一个内存越界访问来对KASan进行测试,测试KASan能否检测到调用的内核模块中的内存越界错误(注意:该测试用例使用的Linux内核版本为4.9.3)。

  1. 首先来到当前用户的根目录,并新建一个代码文件,然后将其打开:
$ cd ~
$ touch kasan_trigger.c
$ gedit kasan_trigger.c
  1. 然后在打开的文件中输入如下内容,最后保存修改后退出:
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#define PROC_NAME "kasan_trigger"

static ssize_t proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) {
    char *kbuf;
    int *p;

    // 分配内核缓冲区并从用户态复制数据
    kbuf = kmalloc(count, GFP_KERNEL);
    if (!kbuf)
        return -ENOMEM;

    if (copy_from_user(kbuf, buffer, count)) {
        kfree(kbuf);
        return -EFAULT;
    }

    // 故意引发内存越界错误
    p = kmalloc(sizeof(int) * 10, GFP_KERNEL);
    if (!p) {
        kfree(kbuf);
        return -ENOMEM;
    }

    p[10] = 0;  // 越界写入

    kfree(p);
    kfree(kbuf);
    return count;
}

static const struct file_operations proc_file_ops = {
    .write = proc_write,
};

static int __init kasan_trigger_init(void) {
    if (!proc_create(PROC_NAME, 0666, NULL, &proc_file_ops)) {
        printk(KERN_ERR "Error: Could not initialize /proc/%s\n", PROC_NAME);
        return -ENOMEM;
    }
    printk(KERN_INFO "/proc/%s created\n", PROC_NAME);
    return 0;
}

static void __exit kasan_trigger_exit(void) {
    remove_proc_entry(PROC_NAME, NULL);
    printk(KERN_INFO "/proc/%s removed\n", PROC_NAME);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("IronmanJay");
MODULE_DESCRIPTION("A module to trigger KASAN error via proc file");

module_init(kasan_trigger_init);
module_exit(kasan_trigger_exit);

该内核模块通过在“/proc”文件系统中创建一个名为“kasan_trigger”的文件。写入该文件时,模块故意引发内存越界错误,以触发KASAN(Kernel Address Sanitizer)检测内存问题。模块的初始化和退出函数分别创建和删除该“/proc”文件。

  1. 然后再在当前目录下创建一个Makefile文件,并将其打开:
$ touch Makefile
$ gedit Makefile
  1. 在打开的文件中输入如下内容,最后保存修改后退出:
obj-m += kasan_trigger.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  1. 然后顺序执行下面两条命令,以编译和加载该内核模块:
$ make
$ sudo insmod kasan_trigger.ko
  1. 然后创建一个用户态程序来触发上面写好的内存越界错误:
$ touch trigger_kasan.c
$ gedit trigger_kasan.c 
  1. 在打开的文件中输入如下内容,然后保存修改后退出:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main() {
    int fd;
    char buffer[] = "trigger";

    fd = open("/proc/kasan_trigger", O_WRONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    if (write(fd, buffer, strlen(buffer)) < 0) {
        perror("write");
        close(fd);
        return 1;
    }

    close(fd);
    return 0;
}
  1. 然后编译并运行写好的用户态程序:
$ gcc -o trigger_kasan trigger_kasan.c
$ ./trigger_kasan
  1. 然后执行如下命令来查看内核日志以确认KASan是否捕捉到我们设计好的内存越界访问:
$ dmesg -T
  1. 执行上面的命令后,会在命令行窗口打印如下图所示的信息,这就说明我们自定义的内核模块中的内存越界错误已经被KASan捕获到了:
    在这里插入图片描述

3.3、通过系统调用检测

  在本章节,我们将使用用户态的二进制程序来调用某个系统调用,从而故意触发一个内存越界访问来对KASan进行测试,测试KASan能否检测到通过系统调用引发的内核中的内存越界错误(注意:该测试用例使用的Linux内核版本为4.9.3)。

  1. 首先来到当前用户的根目录,并新建一个代码文件,然后将其打开:
$ cd ~
$ touch kasan_syscall.c
$ gedit kasan_syscall.c 
  1. 然后在打开的文件中输入如下内容,最后保存修改后退出:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main() {
    // 分配内存映射区域
    size_t length = 4096;
    void *addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    // 写入数据到内存区域内
    strcpy((char *)addr, "hello, KASAN!");

    // 尝试访问映射区域外的内存(引发越界错误)
    // 这个操作应当引发KASan检测到内存越界错误
    *((char *)addr + length) = 'x'; // 越界写入

    // 清理内存映射区域
    munmap(addr, length);

    return 0;
}

这段代码是一个用户态程序,通过内存映射分配一块内存区域,并向其中写入数据。随后,它尝试访问并写入该内存区域的边界之外,故意引发内存越界错误,以触发KASAN(Kernel Address Sanitizer)的检测。最后,程序清理内存映射区域并退出。

  1. 然后编译并运行写好的用户态程序:
$ gcc -o kasan_syscall kasan_syscall.c
$ ./kasan_syscall
  1. 执行上面的命令后,会打印如下图所示的内容:
    在这里插入图片描述

  2. 然后执行如下命令来查看内核日志以确认KASan是否捕捉到我们设计好的内存越界访问:

$ dmesg | grep -i kasan
  1. 执行上面的命令后,会在命令行窗口打印如下图所示的信息,这就说明我们自定义的内核模块中的内存越界错误已经被KASan捕获到了:
    在这里插入图片描述

3.4、检测编译到Linux内核中的内核模块

  在前面章节的测试中,我们使用了十分明显的内存错误测试用例,然而在大多数情况下,内存错误是不易被察觉且难以发现的,所以本章节将模拟正常使用的情况,通过比较复杂的内核模块代码来模拟用户正常使用内核模块时的情况来测试KASan能否检测到我们设置的内存错误(注意:该测试用例使用的Linux内核版本为4.9.3)。

  1. 首先来到当前用户的根目录中,并创建测试目录,最后进入该目录:
$ cd ~
$ mkdir test
$ cd test/
  1. 然后创建下面的文件,并编辑:
$ touch mem_error_module.c
$ gedit mem_error_module.c
  1. 在打开的文件中输入下面的内容,最后保存修改后退出:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/device.h>  // 用于 class_create 和相关函数
#include <linux/string.h>

#define DEVICE_NAME "mem_error"
#define CLASS_NAME  "mem_error_class"
#define BUFFER_SIZE 1024
#define TRIGGER_CONDITION 1024

static int majorNumber;
static char kernel_buffer[BUFFER_SIZE];
static struct class* memErrorClass = NULL;
static struct device* memErrorDevice = NULL;

static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset) {
    // 复杂的条件判断
    if (len > TRIGGER_CONDITION) {
        // 只有当len大于TRIGGER_CONDITION时,才会导致缓冲区溢出
        printk(KERN_WARNING "mem_error: Buffer overflow condition met\n");
        // 使用不安全的内存操作来制造溢出
        memcpy(kernel_buffer, buffer, len);  // 不安全的内存操作
        return len;
    } else {
        // 安全地复制数据
        if (copy_from_user(kernel_buffer, buffer, len)) {
            return -EFAULT;
        }
        return len;
    }
}

static int dev_open(struct inode *inodep, struct file *filep) {
    printk(KERN_INFO "mem_error: Device has been opened\n");
    return 0;
}

static int dev_release(struct inode *inodep, struct file *filep) {
    printk(KERN_INFO "mem_error: Device successfully closed\n");
    return 0;
}

static struct file_operations fops = {
    .open = dev_open,
    .write = dev_write,
    .release = dev_release,
};

static int __init mem_error_init(void) {
    printk(KERN_INFO "mem_error: Initializing the mem_error module\n");

    // 动态分配主设备号
    majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
    if (majorNumber < 0) {
        printk(KERN_ALERT "mem_error: Failed to register a major number\n");
        return majorNumber;
    }
    printk(KERN_INFO "mem_error: Registered correctly with major number %d\n", majorNumber);

    // 注册设备类
    memErrorClass = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(memErrorClass)) {
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ALERT "mem_error: Failed to register device class\n");
        return PTR_ERR(memErrorClass);
    }
    printk(KERN_INFO "mem_error: Device class registered correctly\n");

    // 注册设备驱动
    memErrorDevice = device_create(memErrorClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
    if (IS_ERR(memErrorDevice)) {
        class_destroy(memErrorClass);
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ALERT "mem_error: Failed to create the device\n");
        return PTR_ERR(memErrorDevice);
    }
    printk(KERN_INFO "mem_error: Device class created correctly\n");

    return 0;
}

static void __exit mem_error_exit(void) {
    device_destroy(memErrorClass, MKDEV(majorNumber, 0));
    class_unregister(memErrorClass);
    class_destroy(memErrorClass);
    unregister_chrdev(majorNumber, DEVICE_NAME);
    printk(KERN_INFO "mem_error: Goodbye from the mem_error module!\n");
}

module_init(mem_error_init);
module_exit(mem_error_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("IronmanJay");
MODULE_DESCRIPTION("A simple Linux char driver with a conditional buffer overflow");
MODULE_VERSION("1.0");
  1. 然后创建下面的文件,并编辑:
$ touch Makefile
$ gedit Makefile
  1. 在打开的文件中输入下面的内容,最后保存修改后退出:
obj-m += mem_error_module.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  1. 然后执行下面的命令来编译该内核模块:
$ make
  1. 然后执行下面的命令来加载编译好的内核模块:
$ sudo insmod mem_error_module.ko
  1. 然后创建下面的文件,并编辑:
$ touch test_mem_error.c
$ gedit test_mem_error.c
  1. 在打开的文件中输入下面的内容,最后保存修改后退出:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#define DEVICE_PATH "/dev/mem_error"

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <input_string>\n", argv[0]);
        return 1;
    }

    const char *input = argv[1];

    int fd = open(DEVICE_PATH, O_WRONLY);
    if (fd < 0) {
        perror("Failed to open the device");
        return 1;
    }

    ssize_t bytes_written = write(fd, input, strlen(input));
    if (bytes_written < 0) {
        perror("Failed to write to the device");
        return 1;
    }

    printf("Wrote %zd bytes to the device\n", bytes_written);
    close(fd);

    return 0;
}
  1. 然后执行下面的命令来编译我们刚刚写好的代码:
$ gcc -o test_mem_error test_mem_error.c
  1. 然后打开一个新的命令行终端,并执行下面的命令来实时检测KASan捕获到的信息:
$ sudo dmesg -Tw
  1. 然后执行下面的命令来进行第一次测试,本次的测试用例并不会触发内存错误:
$ sudo ./test_mem_error "This is a normal situation"
  1. 由于该测试用例并不会触发内存错误,所以可以发现KASan并没有捕获到相关信息:
    在这里插入图片描述

  2. 然后执行下面的命令来进行第二次测试,本次的测试用例会触发我们手动设置的内存错误:

$ sudo ./test_mem_error $(perl -e 'print "A" x 2000')
  1. 由于该测试用例会触发我们手动设置的内存错误,所以可以发现KASan成功捕获到了相关信息:
    在这里插入图片描述

4、工作原理

  KASan(Kernel Address Sanitizer)是用于检测内核内存错误的工具,主要用于检测内存越界访问和使用已释放的内存。它通过在内存周围插入红色区域(称为“毒区”或“红色区域”)并在内存分配和释放时检查这些区域的状态来工作。它可以检测以下类型的内存错误:

  • 内存越界访问(Out-of-bounds access)
  • 使用未初始化的内存(Use-after-free)
  • 使用未初始化的堆内存(Use-after-poison)
  • 重复释放内存(Double-free)

  KASan的实现依赖于影子内存(Shadow Memory)和红色区域(Redzones)来跟踪内存分配和访问情况。

4.1、影子内存(Shadow Memory)

  影子内存是KASan用于记录每个内存字节状态的内存区域。每个实际内存字节对应一个影子字节,影子字节的值表示相应内存字节的状态。影子内存的布局如下:

  • 1字节影子内存对应8字节实际内存
  • 每个影子字节可以表示8个实际内存字节的状态

  影子内存的计算公式如下:
S h a d o w A d d r = ( A d d r > > 3 ) + S H A D O W _ O F F S E T ShadowAddr = (Addr >> 3) + SHADOW\_OFFSET ShadowAddr=(Addr>>3)+SHADOW_OFFSET

  其中 A d d r Addr Addr是实际内存地址, S H A D O W _ O F F S E T SHADOW\_OFFSET SHADOW_OFFSET是影子内存的起始偏移。

4.2、内存状态(Memory States)

  影子内存字节的值表示相应内存字节的状态:

  • 0:对应内存字节是安全的
  • 负数(-1到-8):对应内存字节在红色区域内,不可访问
  • 正数(1到7):对应内存字节部分有效

4.3、红色区域(Redzones)

  红色区域是在每个分配的内存块前后插入的未分配区域。这些区域用于检测越界访问。当访问红色区域时,影子内存会标记这些访问为非法。

4.4、KASan的实现

  KASan通过以下步骤实现内存错误检测:

  1. 内存分配
    • 在内存块前后插入红色区域
    • 更新影子内存以标记红色区域和实际分配的内存块
  2. 内存释放
    • 将整个内存块及其红色区域标记为中毒状态
    • 更新影子内存
  3. 内存访问
    • 检查影子内存对应的值,以确定内存访问是否合法
    • 如果发现非法访问,记录错误信息并触发内核警告

5、参考文献

  1. linux之kasan原理及解析
  2. linux内核(5.4.81)—KASAN - povcfe’s blog
  3. 编译内核报错 No rule to make target ‘debian/canonical-certs.pem‘ 或 ‘canonical-revoked-certs.pem‘ 的解决方法
  4. warning: the frame size of 1040 bytes is larger than 1024 bytes
  5. ubuntu22.04:使用时遇到的问题_missing symbol table
  6. 编译内核报错——Failed to generate BTF for vmlinux
  7. 编译内核启用KASan动态检测内核内存错误功能(ubuntu16.04 4.4.0内核编译升级到linux-4.4.252版本)_module for testing kasan for bug detection

总结

  以上就是本篇博文的全部内容,可以发现,KASan的部署与使用过程并不复杂,我们本篇博客对其进行了详细的分析。相信读完本篇博客,各位读者一定对KASan有了更深的了解。

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

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

相关文章

海南聚广众达电子商务咨询有限公司靠谱吗怎么样?

在当今这个数字化浪潮席卷全球的时代&#xff0c;抖音电商以其独特的魅力成为了众多商家争相入驻的新蓝海。而在这片浩瀚的电商海洋中&#xff0c;如何找到一家既专业又可靠的合作伙伴&#xff0c;成为了众多商家心中的一大难题。今天&#xff0c;我们就来深入剖析一下海南聚广…

爬虫日常实战

爬取美团新闻信息&#xff0c;此处采用两种方法实现&#xff1a; 注意点&#xff1a;因为此处的数据都是动态数据&#xff0c;所以一定要考虑好向下滑动数据包会更新的情况&#xff0c;不然就只能读取当前页即第一页数据&#xff0c;方法一通过更新ajax数据包网址页数&#xf…

转变软件交付方式:通过统一 API 和测试策略提高质量和速度

API 在当今的数字化转型中至关重要&#xff0c;但无缝交付也同样重要。然而&#xff0c;许多组织仍然分散其 API 开发和 UI 测试流程&#xff0c;导致问题检测延迟、发布时间延长&#xff0c;甚至遗漏错误。在快节奏的环境中&#xff0c;这种方法是不可持续的&#xff0c;因为上…

Java调用上传文件接口

以 QAnthing 上传文件&#xff08;POST&#xff09;接口为例&#xff0c;展示Java如何调用上传文件接口。 接口文档如下&#xff1a; QAnthign接口文档地址 上代码&#xff1a; RestTemplate 版 /** * * param url 接口地址 * param filePath 文件本地路径 */ public vo…

【C++】踏上C++学习之旅(三):“我“ 与 “引用“ 的浪漫邂逅

文章目录 前言1. "引用"的概念1.1 "引用"的语法 2. "引用"的特性3. "引用"的使用场景3.1 "引用"做参数3. 2 "引用"做返回值3.2.1 "引用"做返回值时需要注意的点 4. 常引用5. "引用"在底层的实…

【设计模式系列】命令模式

目录 一、什么是命令模式 二、命令模式的角色 三、命令模式的典型应用场景 四、命令模式在Runnable中的应用 一、什么是命令模式 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将一个请求或简单操作封装为一个对象。这个模式提供了一种…

在使用new Date()生成时间戳时,发现数据库中 的时间总是多出一秒钟。

项目汇报的时候&#xff0c;进一步研究问题 insert into t_tax_file(task_id, task_no, business_type, file_name, file_url, creator_id, created_time, modifier_id,modified_time)value (10, taskNo测试, 1, 文件名称, 文件地址, 1, 2024-10-21 10:25:21.889, 1, 2024-10-…

CCF-BDCI大数据与计算智能大赛TOP4-京东生鲜

2023 CCF 大数据与计算智能大赛《线上线下全场景生鲜超市库存履约一体化决策》top4南山论剑 摘要1 数据预处理1.1 数据整合1.2 数据划分 2 特征工程2.1 静态特征2.2 动态特征 3 方案设计3.1 数据构造3.2 模型训练3.3 模型融合3.4库存分配3.5 方案对比 链接: CCFBDCI-线上线下全…

对BSV区块链下一代节点Teranode的答疑解惑(上篇)

​​发表时间&#xff1a;2024年8月7日 2024年初BSV区块链研发团队揭晓了即将到来的Teranode更新的突破性特性&#xff0c;这些特性将显著提升网络的效率和处理速度&#xff0c;使BSV区块链能够达到百万级TPS。 Teranode的项目主管Siggi Oskarsson强调&#xff1a;“当你阅读这…

uniapp项目结构基本了解

基本结构的解释 App.vue&#xff1a;应用的根组件&#xff0c;定义全局布局和逻辑。pages/&#xff1a;存放各个页面的 .vue 文件&#xff0c;定义应用的具体页面和功能模块。main.js&#xff1a;应用入口文件&#xff0c;初始化应用&#xff0c;挂载 App.vue。manifest.json&…

[Linux进程概念]命令行参数|环境变量

目录 一、命令行参数 1.什么是命令行参数 2.为什么要有命令行参数 &#xff08;1&#xff09;书写的代码段 &#xff08;2&#xff09;实际的代码段 3.Linux中的命令行参数 二、环境变量 1.什么是环境变量&#xff1f; 2.获取环境变量 &#xff08;1&#xff09;指令…

基于Multisim电子配料秤电路设计(含仿真和报告)

【全套资料.zip】电子配料秤电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 电子配料秤仿真功能: 准确测量物体重量&#xff0c;精确度0.1Kg使用两位数码管显示重量信息 使用拨码…

深度学习 基本函数01

np.dot 是 NumPy 库中的一个函数&#xff0c;用于计算两个数组的点积&#xff08;也称为内积或数量积&#xff09;。点积是两个向量的对应元素乘积之和。 np.random.normal 是 NumPy 库中的一个函数&#xff0c;用于生成符合正态分布&#xff08;也称为高斯分布&#xff09;的…

jmeter用csv data set config做参数化1

在jmeter中&#xff0c;csv data set config的作用非常强大&#xff0c;用它来做批量测试和参数化非常好用。 csv data set config的常用配置项如下&#xff1a; Variable Names处&#xff0c;写上源文件中的参数名&#xff0c;用于后续接口发送请求时引用 Ignore first line…

Mybatis多对一查询的配置及两种方法的使用示例对比以及Mybatis一对多查询两种方法使用示例及对比

一、Mybatis多对一查询的配置及两种方法的使用示例对比 为了试验Mybatis多对一的查询&#xff0c;我们先在数据库中建两个表&#xff0c;一个城市表&#xff0c;一个市区表&#xff0c;一个城市有多个区是一个一对多的关系&#xff1b;多个区对应一个城市是一个多对一的关系。建…

spring源码拓展点3之addBeanPostProcesser

概述 在refresh方法中的prepareBeanFactory方法中&#xff0c;有一个拓展点&#xff1a;addBeanPostProcessor。即通过注入Aware对象从而将容器中的某些值设置到某个bean中。 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));aware接口调用 …

ThinkPad T480拆机屏幕改装:便携式显示器DIY指南

ThinkPad T480拆机屏幕改装&#xff1a;便携式显示器DIY指南 本文记录了将旧笔记本电脑 T480 拆机屏幕改装为便携式显示器的全过程。作者在决定升级设备后&#xff0c;选择通过 DIY 方式利用原有的屏幕资源。文章详细介绍了屏幕驱动板的安装、螺丝孔的剪裁、排线连接及固定的步…

[DB] NSM

Database Workloads&#xff08;数据库工作负载&#xff09; 数据库工作负载指的是数据库在执行不同类型任务时所需的资源和计算方式&#xff0c;主要包括以下几种类型&#xff1a; 1. On-Line Transaction Processing (OLTP) 中文&#xff1a;联机事务处理解释&#xff1a;…

hive初体验

1.首先&#xff0c;确保启动了Metastore服务。 runjar就是metastore进程 2.进入hive客户端: 命令:hive 3.操作:没有指定数据库时默认在default 一:创建表:CREATE TABLE test(id INT, name STRING, gender STRING); 完成,show tables看一下 也可以通过hdfs文件系统查看,默认路径…

go多线程

1.仅加go 在一个golang编写的程序&#xff0c;主函数运行完毕后&#xff0c;程序就结束了 package mainimport ("fmt""time" )func main() {// 如果这样写go 要加在上面的函数&#xff0c;因为如果只单独加在下面的函数或者都加上&#xff0c;程序就会直接…