CVE-2018-18281—linux内核中关于TLB漏洞的分析
字数 2266 2025-08-06 08:35:16

Linux内核TLB漏洞(CVE-2018-18281)分析与利用教学文档

一、漏洞概述

CVE-2018-18281是Linux内核中一个关于TLB(Translation Lookaside Buffer)处理不当导致的漏洞,由Project Zero的Jann Horn在2019年1月17日发现并报告。该漏洞存在于内存管理子系统中的页表移动操作中,可能导致TLB失效不及时,从而引发安全风险。

二、基础知识

1. 内核抢占模型

Linux内核支持三种抢占模型:

  • CONFIG_PREEMPT_NONE:无强迫抢占(服务器配置)
  • CONFIG_PREEMPT_VOLUNTARY:自愿内核抢占(桌面配置)
  • CONFIG_PREEMPT:内核可抢占(低延迟桌面配置)

Pixel 2使用CONFIG_PREEMPT配置,这意味着内核代码可以在执行期间的任何时候中断,包括进程持有互斥锁、信号量和位于RCU读端临界区时(取决于内核配置)。

2. 调度相关函数

  • sched_setaffinity:设置进程运行的CPU核心
  • sched_setscheduler:设置进程调度策略和参数

调度策略分为:

  • 普通调度策略:SCHED_NORMAL、SCHED_BATCH
  • 实时调度策略:SCHED_FIFO、SCHED_RR、SCHED_DEADLINE
  • 空闲调度策略:SCHED_IDLE

3. 页分配器

Linux页分配器基于buddy分配器实现,关键概念:

  • 区域(Zone)

    • ZONE_DMA:为DMA设备保留的内存
    • ZONE_NORMAL:内核直接映射的内存
  • 迁移类型(Migration Type)

    • MIGRATE_UNMOVABLE:不可移动分配(如kmalloc)
    • MIGRATE_MOVABLE:可移动分配(如用户空间内存)
    • MIGRATE_RECLAIMABLE:可回收分配
    • MIGRATE_CMA:为DMA预留的连续内存
  • 冷热页

    • 冷页:不在高速缓存中
    • 热页:仍在高速缓存中
    • 通过per-cpu-pageset(pcp)管理

4. TLB和分页结构缓存

  • TLB:缓存最后一级页表条目,加速地址转换
  • 分页结构缓存
    • Intel:Paging-Structure Caches
    • ARM:Intermediate table walk caches
    • 存储非最后一级页表条目的副本

TLB失效通用模式:

  1. 从页表中删除条目但保持物理页引用
  2. 对所有相关CPU核心执行TLB刷新
  3. 删除物理页上的引用

三、漏洞原理

1. 内存管理锁机制

  • mmap_sem:保护VMA(Virtual Memory Area)
  • 页表锁:保护页表访问

2. mremap操作流程

mremap函数通过mremap_to->move_vma->move_page_tables->move_ptes移动页表,其中move_ptes函数的逻辑:

  1. 获取旧页表和新页表锁
  2. 刷新旧的TLB条目
  3. 对于每个非空条目:
    • 原子性读取并清除页表条目
    • 如果条目为Dirty,设置force_flush标志
    • 将条目写入新页表
  4. 解锁新页表
  5. 根据force_flush决定是否执行TLB刷新
  6. 解锁旧页表

3. 漏洞点

问题在于:

  • 新页表解锁后,旧页表解锁前,另一个进程可能删除页
  • TLB刷新可能延迟到move_page_tables函数中执行
  • 这导致在页被释放后,旧TLB条目可能仍然有效

四、漏洞利用

1. 攻击准备

  1. 选择目标页:如/system/lib64/libandroid_runtime.so中的com_android_internal_os_Zygote_nativeForkAndSpecialize函数页
  2. 从页缓存中删除目标页:
    • 使用fallocate制造内存压力
    • 通过mincore检查页是否在缓存中
    • 使用MADV_RANDOM映射文件其他页防止预读

2. 利用步骤

  1. 分配具有文件背景的页并映射(映射1)
  2. 触发mremap/ftruncate竞争:
    • 设置mremap进程为SCHED_IDLE优先级
    • 与正常优先级进程运行在同一CPU核心
    • 通过管道控制抢占时机
    • 监控/proc/<pid>/status中的VmPTE确定写入时机
  3. 从目标页开始读取,导致内核重新分配已释放页
  4. 通过映射1轮询页内容,直到包含目标页
  5. 通过旧TLB反复覆盖目标页

3. Shellcode设计

利用zygote进程的CAP_SYS_ADMIN能力:

  • 读取SELinux上下文
  • 使用sethostname结果覆盖主机名

五、补丁分析

主要修复:

  • 将对flush_tlb_range的调用移到新页表解锁之前
  • 确保在释放页表引用前完成TLB刷新

六、防御建议

  1. 及时更新内核补丁
  2. 限制关键进程的调度优先级
  3. 监控异常的内存访问模式
  4. 考虑使用硬件支持的TLB失效机制

七、参考资料

  1. Memory Management with Huge Pages
  2. ARM64系统调用表(https://github.com/retme7/arm64_syscall_nr)
  3. Rowhammer防御相关研究

本教学文档详细分析了CVE-2018-18281漏洞的原理、利用方法及防御措施,涵盖了从基础知识到实际利用的全过程。理解此漏洞需要对Linux内存管理、TLB机制和调度系统有深入认识。

Linux内核TLB漏洞(CVE-2018-18281)分析与利用教学文档 一、漏洞概述 CVE-2018-18281是Linux内核中一个关于TLB(Translation Lookaside Buffer)处理不当导致的漏洞,由Project Zero的Jann Horn在2019年1月17日发现并报告。该漏洞存在于内存管理子系统中的页表移动操作中,可能导致TLB失效不及时,从而引发安全风险。 二、基础知识 1. 内核抢占模型 Linux内核支持三种抢占模型: CONFIG_ PREEMPT_ NONE :无强迫抢占(服务器配置) CONFIG_ PREEMPT_ VOLUNTARY :自愿内核抢占(桌面配置) CONFIG_ PREEMPT :内核可抢占(低延迟桌面配置) Pixel 2使用CONFIG_ PREEMPT配置,这意味着内核代码可以在执行期间的任何时候中断,包括进程持有互斥锁、信号量和位于RCU读端临界区时(取决于内核配置)。 2. 调度相关函数 sched_setaffinity :设置进程运行的CPU核心 sched_setscheduler :设置进程调度策略和参数 调度策略分为: 普通调度策略:SCHED_ NORMAL、SCHED_ BATCH 实时调度策略:SCHED_ FIFO、SCHED_ RR、SCHED_ DEADLINE 空闲调度策略:SCHED_ IDLE 3. 页分配器 Linux页分配器基于buddy分配器实现,关键概念: 区域(Zone) : ZONE_ DMA:为DMA设备保留的内存 ZONE_ NORMAL:内核直接映射的内存 迁移类型(Migration Type) : MIGRATE_ UNMOVABLE:不可移动分配(如kmalloc) MIGRATE_ MOVABLE:可移动分配(如用户空间内存) MIGRATE_ RECLAIMABLE:可回收分配 MIGRATE_ CMA:为DMA预留的连续内存 冷热页 : 冷页:不在高速缓存中 热页:仍在高速缓存中 通过per-cpu-pageset(pcp)管理 4. TLB和分页结构缓存 TLB :缓存最后一级页表条目,加速地址转换 分页结构缓存 : Intel:Paging-Structure Caches ARM:Intermediate table walk caches 存储非最后一级页表条目的副本 TLB失效通用模式: 从页表中删除条目但保持物理页引用 对所有相关CPU核心执行TLB刷新 删除物理页上的引用 三、漏洞原理 1. 内存管理锁机制 mmap_sem :保护VMA(Virtual Memory Area) 页表锁:保护页表访问 2. mremap操作流程 mremap 函数通过 mremap_to->move_vma->move_page_tables->move_ptes 移动页表,其中 move_ptes 函数的逻辑: 获取旧页表和新页表锁 刷新旧的TLB条目 对于每个非空条目: 原子性读取并清除页表条目 如果条目为Dirty,设置force_ flush标志 将条目写入新页表 解锁新页表 根据force_ flush决定是否执行TLB刷新 解锁旧页表 3. 漏洞点 问题在于: 新页表解锁后,旧页表解锁前,另一个进程可能删除页 TLB刷新可能延迟到 move_page_tables 函数中执行 这导致在页被释放后,旧TLB条目可能仍然有效 四、漏洞利用 1. 攻击准备 选择目标页:如 /system/lib64/libandroid_runtime.so 中的 com_android_internal_os_Zygote_nativeForkAndSpecialize 函数页 从页缓存中删除目标页: 使用 fallocate 制造内存压力 通过 mincore 检查页是否在缓存中 使用 MADV_RANDOM 映射文件其他页防止预读 2. 利用步骤 分配具有文件背景的页并映射(映射1) 触发mremap/ftruncate竞争: 设置mremap进程为SCHED_ IDLE优先级 与正常优先级进程运行在同一CPU核心 通过管道控制抢占时机 监控 /proc/<pid>/status 中的VmPTE确定写入时机 从目标页开始读取,导致内核重新分配已释放页 通过映射1轮询页内容,直到包含目标页 通过旧TLB反复覆盖目标页 3. Shellcode设计 利用zygote进程的CAP_ SYS_ ADMIN能力: 读取SELinux上下文 使用 sethostname 结果覆盖主机名 五、补丁分析 主要修复: 将对 flush_tlb_range 的调用移到新页表解锁之前 确保在释放页表引用前完成TLB刷新 六、防御建议 及时更新内核补丁 限制关键进程的调度优先级 监控异常的内存访问模式 考虑使用硬件支持的TLB失效机制 七、参考资料 Memory Management with Huge Pages ARM64系统调用表(https://github.com/retme7/arm64_ syscall_ nr) Rowhammer防御相关研究 本教学文档详细分析了CVE-2018-18281漏洞的原理、利用方法及防御措施,涵盖了从基础知识到实际利用的全过程。理解此漏洞需要对Linux内存管理、TLB机制和调度系统有深入认识。