Assembly 有没有办法从EL0刷新ARM缓存?
我一直在尝试在ARMV8上实现Spectre攻击的PoC代码(我知道大多数ARMV8处理器不易受到攻击,但无论如何都在尝试实现它)。我使用的是asm volatile(“DC-CIVAC,%[ad]”::[ad]“r”(addr))代码>指令刷新缓存,然后尝试测量再次读取刷新地址的时间。但是在清除缓存之前和之后读取地址时,我没有发现任何差异。这让我意识到我用于flush的指令不起作用Assembly 有没有办法从EL0刷新ARM缓存?,assembly,caching,arm64,cpu-cache,armv8,Assembly,Caching,Arm64,Cpu Cache,Armv8,我一直在尝试在ARMV8上实现Spectre攻击的PoC代码(我知道大多数ARMV8处理器不易受到攻击,但无论如何都在尝试实现它)。我使用的是asm volatile(“DC-CIVAC,%[ad]”::[ad]“r”(addr))指令刷新缓存,然后尝试测量再次读取刷新地址的时间。但是在清除缓存之前和之后读取地址时,我没有发现任何差异。这让我意识到我用于flush的指令不起作用 在阅读ARMV8参考手册()时,我意识到要使DC CIVAC指令工作,应将SCTLR_EL1.UCI设置为1。我不确定
在阅读ARMV8参考手册()时,我意识到要使
DC CIVAC
指令工作,应将SCTLR_EL1.UCI
设置为1。我不确定如何在不需要sudo
特权的情况下检查SCTLR\u EL1
寄存器的值并将其更改为1 下面的过程提供了从Linux用户模式读取/写入SCTLR\u EL1
的方法。
未回答在运行的Linux系统上读/写SCTLR_EL1是否能够/足以刷新缓存
这显然是一种不安全的做法,因为它显然破坏了Armv8-a
安全方案-不要在家尝试,而是为了学习
该过程是在运行的OrangePi PC2
上执行的(内核版本是LinuxOrangePic25.4.28-sunxi64#20.02.7 SMP-Sat Mar 28 17:25:10 CET 2020 aarch64 GNU/Linux
)
安装最新的内核/linux标头:
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install linux-headers-current-sunxi64
sudo shutdown -r now
使用procfs
支持编译最小可加载内核模块:
Makefile
:
obj-m+=/sctlr_el1.o
KBUILD_DIR=/usr/src/linux-headers-5.4.28-sunxi64
all:
make -C $(KBUILD_DIR) M=$(PWD) modules
# Kernel module clean dependency
clean:
make -C $(KBUILD_DIR) M=$(PWD) clean
install:
insmod sctlr_el1.ko
remove:
rmmod sctlr_el1
sctlr\u el1.c
:
// Credits:
//
// minimal kernel module: https://docs.legato.io/17_07/yoctoOutofTreeKernelModule.html
// procfs example: https://gist.github.com/BrotherJing/c9c5ffdc9954d998d1336711fa3a6480
#include<linux/module.h>
#include<linux/init.h>
#include<linux/proc_fs.h>
#include<linux/sched.h>
#include<linux/uaccess.h>
#include<linux/fs.h>
#include<linux/seq_file.h>
#include<linux/slab.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Frant");
MODULE_DESCRIPTION("Minimal kernel module suitable for reading/writing SCTLR_EL1");
MODULE_VERSION("0.0.1");
// write system register SCTLR_EL1 (s3_0_c1_c0_0) with specified value.
static inline void system_write_SCTLR_EL1(uint64_t val)
{
asm volatile("msr s3_0_c1_c0_0 , %0" : : "r" (val));
asm volatile("isb" : : : "memory");
}
// read system register value SCTLR_EL1 (s3_0_c1_c0_0).
static inline uint64_t system_read_SCTLR_EL1(void)
{
uint64_t val;
asm volatile("mrs %0, s3_0_c1_c0_0" : "=r" (val));
return val;
}
static int my_proc_show(struct seq_file *m,void *v) {
uint64_t value = system_read_SCTLR_EL1();
printk(KERN_INFO "Read value <0x%016llx> from SCTLR_EL1.", value);
seq_printf(m,"0x%016llx\n",value);
return 0;
}
static ssize_t my_proc_write(struct file* file,const char __user *buffer,size_t count,loff_t *f_pos){
int rc = -1;
uint64_t value = 0;
char *tmp = kzalloc((count+1),GFP_KERNEL);
if(!tmp)return -ENOMEM;
if(copy_from_user(tmp,buffer,count)){
kfree(tmp);
return EFAULT;
}
rc = kstrtoul (tmp, 0, (unsigned long*) &value);
if (rc != 0) {
printk(KERN_ERR "Value <%s> is invalid.", tmp);
kfree(tmp);
return EFAULT;
}
kfree(tmp);
system_write_SCTLR_EL1(value);
printk(KERN_INFO "Wrote value <0x%016llx> to SCTLR_EL1.", value);
return count;
}
static int my_proc_open(struct inode *inode,struct file *file){
return single_open(file,my_proc_show,NULL);
}
static struct file_operations my_fops={
.owner = THIS_MODULE,
.open = my_proc_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
.write = my_proc_write
};
static int __init sctlr_el1_init(void) {
struct proc_dir_entry *entry;
entry = proc_create("SCTLR_EL1",0777,NULL,&my_fops);
if(!entry){
return -1;
} else {
printk(KERN_INFO "Entering sctlr_el1\n");
}
return 0;
}
static void __exit sctlr_el1_exit(void) {
remove_proc_entry("SCTLR_EL1",NULL);
printk(KERN_INFO "Exiting sctlr_el1\n");
}
module_init(sctlr_el1_init);
module_exit(sctlr_el1_exit);
阅读SCTLR\u EL1
cat /proc/SCTLR_EL1
0x0000000034d4d91d
SCTLR\u EL1.UCI
在我的系统上
重新写入相同的值:
echo 0x0000000034d4d91d > /proc/SCTLR_EL1
dmesg
[ 1130.967798] Wrote value <0x0000000034d4d91d> to SCTLR_EL1.
下面的过程提供了从Linux用户模式读取/写入
SCTLR\u EL1
的方法。
未回答在运行的Linux系统上读/写SCTLR_EL1是否能够/足以刷新缓存
这显然是一种不安全的做法,因为它显然破坏了Armv8-a
安全方案-不要在家尝试,而是为了学习
该过程是在运行的OrangePi PC2
上执行的(内核版本是LinuxOrangePic25.4.28-sunxi64#20.02.7 SMP-Sat Mar 28 17:25:10 CET 2020 aarch64 GNU/Linux
)
安装最新的内核/linux标头:
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install linux-headers-current-sunxi64
sudo shutdown -r now
使用procfs
支持编译最小可加载内核模块:
Makefile
:
obj-m+=/sctlr_el1.o
KBUILD_DIR=/usr/src/linux-headers-5.4.28-sunxi64
all:
make -C $(KBUILD_DIR) M=$(PWD) modules
# Kernel module clean dependency
clean:
make -C $(KBUILD_DIR) M=$(PWD) clean
install:
insmod sctlr_el1.ko
remove:
rmmod sctlr_el1
sctlr\u el1.c
:
// Credits:
//
// minimal kernel module: https://docs.legato.io/17_07/yoctoOutofTreeKernelModule.html
// procfs example: https://gist.github.com/BrotherJing/c9c5ffdc9954d998d1336711fa3a6480
#include<linux/module.h>
#include<linux/init.h>
#include<linux/proc_fs.h>
#include<linux/sched.h>
#include<linux/uaccess.h>
#include<linux/fs.h>
#include<linux/seq_file.h>
#include<linux/slab.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Frant");
MODULE_DESCRIPTION("Minimal kernel module suitable for reading/writing SCTLR_EL1");
MODULE_VERSION("0.0.1");
// write system register SCTLR_EL1 (s3_0_c1_c0_0) with specified value.
static inline void system_write_SCTLR_EL1(uint64_t val)
{
asm volatile("msr s3_0_c1_c0_0 , %0" : : "r" (val));
asm volatile("isb" : : : "memory");
}
// read system register value SCTLR_EL1 (s3_0_c1_c0_0).
static inline uint64_t system_read_SCTLR_EL1(void)
{
uint64_t val;
asm volatile("mrs %0, s3_0_c1_c0_0" : "=r" (val));
return val;
}
static int my_proc_show(struct seq_file *m,void *v) {
uint64_t value = system_read_SCTLR_EL1();
printk(KERN_INFO "Read value <0x%016llx> from SCTLR_EL1.", value);
seq_printf(m,"0x%016llx\n",value);
return 0;
}
static ssize_t my_proc_write(struct file* file,const char __user *buffer,size_t count,loff_t *f_pos){
int rc = -1;
uint64_t value = 0;
char *tmp = kzalloc((count+1),GFP_KERNEL);
if(!tmp)return -ENOMEM;
if(copy_from_user(tmp,buffer,count)){
kfree(tmp);
return EFAULT;
}
rc = kstrtoul (tmp, 0, (unsigned long*) &value);
if (rc != 0) {
printk(KERN_ERR "Value <%s> is invalid.", tmp);
kfree(tmp);
return EFAULT;
}
kfree(tmp);
system_write_SCTLR_EL1(value);
printk(KERN_INFO "Wrote value <0x%016llx> to SCTLR_EL1.", value);
return count;
}
static int my_proc_open(struct inode *inode,struct file *file){
return single_open(file,my_proc_show,NULL);
}
static struct file_operations my_fops={
.owner = THIS_MODULE,
.open = my_proc_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
.write = my_proc_write
};
static int __init sctlr_el1_init(void) {
struct proc_dir_entry *entry;
entry = proc_create("SCTLR_EL1",0777,NULL,&my_fops);
if(!entry){
return -1;
} else {
printk(KERN_INFO "Entering sctlr_el1\n");
}
return 0;
}
static void __exit sctlr_el1_exit(void) {
remove_proc_entry("SCTLR_EL1",NULL);
printk(KERN_INFO "Exiting sctlr_el1\n");
}
module_init(sctlr_el1_init);
module_exit(sctlr_el1_exit);
阅读SCTLR\u EL1
cat /proc/SCTLR_EL1
0x0000000034d4d91d
SCTLR\u EL1.UCI
在我的系统上
重新写入相同的值:
echo 0x0000000034d4d91d > /proc/SCTLR_EL1
dmesg
[ 1130.967798] Wrote value <0x0000000034d4d91d> to SCTLR_EL1.
Linux默认为所有arm64处理器设置该位(),除了少数Cortex-A53版本需要解决勘误表(,)的问题。但是,即使在未设置UCI
的情况下,内核中的a也会以内核模式为您执行它们,因此它们仍然可以工作,但速度较慢
总之,您可以期望、、和缓存维护指令在用户空间中始终起作用,至少在最新版本的Linux上是如此。对于所有arm64处理器,该位由Linux默认设置(),除了少数Cortex-A53版本需要解决勘误表(,)的问题。但是,即使在未设置UCI
的情况下,内核中的a也会以内核模式为您执行它们,因此它们仍然可以工作,但只是速度较慢
总之,您可以期望、、和缓存维护指令在用户空间中始终起作用,至少在最新版本的Linux上是如此。您遗漏了一个
“内存”
clobber在您的内联asm中。编译器可以在编译时使用加载或存储对该asm进行重新排序,例如,如果愿意,在刷新后进行存储。您是否检查了编译器生成的asm以确保它正在执行您想要的操作?@Balvansh Heerekar:sudo
仍将在Linux用户模式下执行任何命令,即在EL0
您需要的是一个最小的可加载内核模块,能够获取/设置SCTLR\u EL1.UCI
@Frant有没有一种方法可以在不实现简单内核模块的情况下获取SCTLR\u EL1.UCI
从运行在EL1
的应用程序中获取驱动程序,而Linux应用程序总是在EL0
执行。请注意,如果从一个工作示例开始编写这样的驱动程序并不困难。你会在网上找到很多驱动程序。@Frant:我确实通过添加内存进行了检查
在我的内联asm中使用clobber,但这似乎也没有帮助。我想我必须检查我的SCTLR\u EL1.UCI
值。你漏掉了一个内存
clobber在您的内联asm中。编译器可以在编译时使用加载或存储对该asm进行重新排序,例如,如果愿意,在刷新后进行存储。您是否检查了编译器生成的asm以确保它正在执行您想要的操作?@Balvansh Heerekar:sudo
仍将在Linux用户模式下执行任何命令,即在EL0
您需要的是一个最小的可加载内核模块,能够获取/设置SCTLR\u EL1.UCI
@Frant有没有一种方法可以在不实现简单内核模块的情况下获取SCTLR\u EL1.UCI
从运行在EL1
的应用程序中获取驱动程序,而Linux应用程序总是在EL0
执行。请注意,如果从一个工作示例开始编写这样的驱动程序并不困难。你会在网上找到很多驱动程序。@Frant:我确实通过添加内存进行了检查
clobber在我的内联asm中,但这似乎也没有帮助。我想我