• Linux内核中KCSAN 数据竞争检测


    目录

    一、KCSAN:The Kernel Concurrency Sanitizer 

    二、内核配置以及说明

    1、配置选项

    2、内核提供的测试用例

    3、检测输出

    4、检测解析

    三、锁的注意事项

    1、通过debugfs查看

    2、加锁注意事项

    3、Local Locks

    四、锁的错误使用

    1、不正确的自旋锁用法 

    2、阻塞和无法获取引用

    3、长时间的延迟(数据处理)

    3.1 阿里的一个锁的问题

    锁时间的测量方法

    补充:大量的实际数据竞争的例子


    在多核处理器,多进程/线程编程。在访问数据时,容易出现竞争。在编程中可以使用锁或者lock-free来保护共享数据。KCSAN 实时动态检测数据竞争现象。

    一、KCSAN:The Kernel Concurrency Sanitizer 

    1、功能:

            KCSAN可以找到数据竞争所在
    2、工作原理:

            检查未标记读取是否与这些写入竞争持续扫描内核的主要分支,在访问的内存位置上设置观察点,挑出导致数据争用的模式,并将其报告给内核日志。是数据统计方法。

    3、触发条件:

            如果两个线程(或中断上下文)访问同一内存位置,那么两个读/写观察点都会被触发,就会有竞争!KCSAN进行检查时,如果满足了数据竞争的条件,内核会提示错误。


    二、内核配置以及说明

    版本说明

             x86_64: >=5.8

            ARM64: >=5.17

    编译器版本

             GCC or Clang version >= 11

    1、配置选项

    CONFIG_KCSAN = y

    CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n

    CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=n

    CONFIG_KCSAN_INTERRUPT_WATCHER=y

     

    CONFIG_DEBUG_KERNEL=y

    2、内核提供的测试用例

    源码位置:kernel/kcsan/kcsan-test.c

     编译文件:kcsan-test.ko

    3、检测输出

    选择其中两类输出

    1. [ 426.071584] ==================================================================
    2. [ 426.071584] BUG: KCSAN: data-race in test_kernel_read [kcsan_test] / test_kernel_write [kcsan_test]
    3. [ 426.071584]
    4. [ 426.071584] read to 0xffffffffc0203c60 of 8 bytes by task 307 on cpu 2:
    5. [ 426.071584] test_kernel_read+0x19/0x30 [kcsan_test]
    6. [ 426.101893] access_thread+0x43/0xb0 [kcsan_test]
    7. [ 426.101893] kthread+0x183/0x1b0
    8. [ 426.101893] ret_from_fork+0x22/0x30
    9. [ 426.101893]
    10. [ 426.101893] write to 0xffffffffc0203c60 of 8 bytes by task 306 on cpu 1:
    11. [ 426.101893] test_kernel_write+0x22/0x40 [kcsan_test]
    12. [ 426.101893] access_thread+0x43/0xb0 [kcsan_test]
    13. [ 426.101893] kthread+0x183/0x1b0
    14. [ 426.101893] ret_from_fork+0x22/0x30
    15. [ 426.101893]
    16. [ 426.101893] Reported by Kernel Concurrency Sanitizer on:
    17. [ 426.101893] CPU: 1 PID: 306 Comm: access_thread Kdump: loaded Tainted: G N 6.0.0-rc6 #5
    18. [ 426.101893] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
    19. [ 426.101893] ==================================================================
    20. [ 427.253735] ==================================================================
    21. [ 427.253735] BUG: KCSAN: data-race in test_kernel_read+0x19/0x30 [kcsan_test]
    22. [ 427.253735]
    23. [ 427.253735] race at unknown origin, with read to 0xffffffffc0203c60 of 8 bytes by task 307 on cpu 2:
    24. [ 427.274398] test_kernel_read+0x19/0x30 [kcsan_test]
    25. [ 427.274398] access_thread+0x43/0xb0 [kcsan_test]
    26. [ 427.274398] kthread+0x183/0x1b0
    27. [ 427.274398] ret_from_fork+0x22/0x30
    28. [ 427.274398]
    29. [ 427.274398] value changed: 0x000000000017b4a9 -> 0x000000000017b520
    30. [ 427.274398]
    31. [ 427.274398] Reported by Kernel Concurrency Sanitizer on:
    32. [ 427.274398] CPU: 2 PID: 307 Comm: access_thread Kdump: loaded Tainted: G N 6.0.0-rc6 #5
    33. [ 427.274398] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
    34. [ 427.274398] ==================================================================
    35. [ 427.732807] test_basic-torture: Stopping reader_thread task

    4、检测解析

    BUG: KCSAN: data-race

    •         read/write [(marked)] to of bytes by task on cpu  

            所涉及的是读 写操作

    •       “value changed”  如 value changed: 0x000000000017b4a9 -> 0x000000000017b520

    BUG: KCSAN: data-race in test_kernel_read+0x19/0x30


    三、锁的注意事项

    1、通过debugfs查看

    /sys/kernel/debug/kcsan 查看kcsan的参数
    1. # cat /sys/kernel/debug/kcsan
    2. enabled: 1
    3. used_watchpoints: 0
    4. setup_watchpoints: 177185
    5. data_races: 12
    6. assert_failures: 0
    7. no_capacity: 0
    8. report_races: 5
    9. races_unknown_origin: 0
    10. unencodable_accesses: 0
    11. encoding_false_positives: 0
    12. blacklisted functions: none



    2、加锁注意事项

    • 容易导致锁缺陷的情况

            尝试两次加锁
            尝试释放没有上锁的锁
            退出而不释放所

    • 在自旋锁中申请内存,应该使用GFP_ATOMIC标志,而不是GFP_KERNEL
    • 加锁的原则:仅在绝对必要时禁用中断,然后尽可能短的时间

    3、Local Locks
     

    新的同步原语,在5.8内核中添加

    本地锁是抢占和中断启用/禁用原语的包装器。特别是对于调试内核来说,使用lockdep和静态分析是发现bug的关键。

    在非实时系统上,获取本地锁只会映射到禁用抢占(以及可能的中断)。在实时系统上,本地锁实际上是休眠的自旋锁;它们不会禁用抢占或中断。这足以对受保护资源的访问,而不会增加整个系统的延迟。

    本地锁定操作按以下方式映射到抢占和中断禁用

         local_lock(&llock)              preempt_disable()
         local_unlock(&llock)           preempt_enable()
         local_lock_irq(&llock)         local_irq_disable()
         local_unlock_irq(&llock)       local_irq_enable()
         local_lock_save(&llock)        local_irq_save()
         local_unlock_restore(&llock)   local_irq_restore()

    参考  Concurrency bugs should fear the big bad data-race detector (part 1) [LWN.net]


    四、锁的错误使用

    1、不正确的自旋锁用法 

    内核源码驱动位置: drivers/tty/tty_jobctrl.c:tiocspgrp()

    使用了错误的锁
     

    kernel version 5.16 才修复

    2、阻塞和无法获取引用

    1)、在临界资源中不要睡眠

    1. rcu_read_lock(); // begin RCU read critical section
    2. [...]
    3. msleep(10); /* bug! *sleep() helpers all block; if
    4. you must, use the *delay() helpers instead (they're
    5. non-blocking) */
    6. rcu_read_unlock(); // end RCU read critical section

    2)、使用内核全局数据结构时,使用后要释放。因其内部有申请与释放的操作

    1. get_task_struct();
    2. /* ... use it ... */
    3. put_task_struct();

    1. get_file(file);
    2. /* ... use the file ... */
    3. fput(file);

    原文 My First Kernel Module: A Debugging Nightmare

    3、长时间的延迟(数据处理)

    1. spin_lock_irq[save](&mylock[, flags]); /* disables interrupts
    2. */
    3. // time t1
    4. /* ... do the work ... */
    5. // time t2
    6. spin_unlock_irq[restore](&mylock[, flags]); /* enables
    7. interrupts */

    在处理数据消耗的时间 自旋锁 t2-t1

    t2-t1 非常小(几微秒或者一位数的毫秒),一般情况下运行正常;

    t2-t1 比较大 (几十毫秒或者更长),这种情况不正常,会引起各种延迟问题,甚至是livelock
     

    3.1 阿里的一个锁的问题

    原文 Network Jitter: An In-Depth Case Study - Alibaba Cloud Community

    网络抖动是由于系统中的延迟很长而导致的。工程师发现,slab统计信息查找调用spin_lock_irq()的代码时引入的延迟。正如我们所知,它在内部禁用了硬件中断,因为这段代码在循环中运行了很长一段时间。O(n)时间复杂性


    这是一个非常典型的问题:迭代大于预期的列表,导致意外延迟

    因为spin_lock_irq[save]()API同时禁用硬件irq和内核抢占,所以我们必须尽快重新启用它们 spin_unlock_ir q[restore]()。保持自旋锁所花费的时间超过几十毫秒就被认为太长了。

    锁时间的测量方法

    1、eBPF

    criticalstat:在内核中查找原子临界区的时间

    1. # ./criticalstat -h
    2. usage: criticalstat [-h] [-p] [-i] [-d DURATION]
    3. Trace long critical sections
    4. optional arguments:
    5. -h, --help Show this help message and exit
    6. -p, --preemptoff Find long sections where preemption was off
    7. -i, --irqoff Find long sections where IRQ was off
    8. -d DURATION, --duration DURATION
    9. Duration in uS (microseconds) below which we filter
    10. examples:
    11. ./criticalstat # run with default options: irq off for more than 100 uS
    12. ./criticalstat -p # find sections with preemption disabled for more than 100 uS
    13. ./criticalstat -d 500 # find sections with IRQs disabled for more than 500 uS
    14. ./criticalstat -p -d 500 # find sections with preemption disabled for more than 500 uS

    补充:大量的实际数据竞争的例子

    kernel-sanitizers/FOUND_BUGS.md at master · google/kernel-sanitizers · GitHub


    参考


     
  • 相关阅读:
    [附源码]java毕业设计高校疫情日报管理信息系统
    MATLAB绘制伪彩图和切片轮廓线图
    R语言拟合ARIMA模型:剔除ARIMA模型中不显著的系数、通过分析系数的置信区间判断系数是否是冗余系数(参数)、以及是否需要被删除
    Vue基础:父子组件访问的方式
    trie树(零)零基础入门
    4、ARM异常处理
    GameStop熊市杀入NFT交易,老牌游戏零售商借Web3焕发第二春
    springboot毕设项目滁州学院体育馆管理h78n6(java+VUE+Mybatis+Maven+Mysql)
    企业管理中,商业智能BI主要做哪些事情?
    “鹅宝计划”,天鹅到家“以奋斗者为本”的时代缩影
  • 原文地址:https://blog.csdn.net/WANGYONGZIXUE/article/details/126981543