blob: c44409a38713ec7db5f66ce46d2d641f2f5e96bf [file] [log] [blame]
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
MODULE_LICENSE("Proprietary");
MODULE_AUTHOR("Google");
MODULE_DESCRIPTION("");
MODULE_VERSION("0.1");
#ifndef __aarch64__
# error Unsupported CPU. ARM64 required.
#endif
// Provides an endpoint on
// /proc/safeside_eret_hvc_smc/address
// to which userspace programs can send their addresses that will be
// fetched due to speculation over ERET, HVC and SMC instructions.
// Currently should be accessible only by root, because there is no checking of
// those addresses.
// Directory record. Must be available on unloading the module.
struct proc_dir_entry *safeside_eret_hvc_smc;
static ssize_t address_store(struct file *f, const char __user *buf,
size_t length, loff_t *off) {
ptrdiff_t userspace_address;
int res;
int *kernel_memory;
// Enable kernel access to userspace memory.
__uaccess_enable(ARM64_ALT_PAN_NOT_UAO);
res = kstrtoul(buf, 0, &userspace_address);
if (res != 0) {
// Incorrectly formatted address was provided.
pr_err(
"kstrtol failed with input %s and return value %d,"
"correct format is 0xHHHHHHHHHHHHHHHH\n", buf, res);
return length;
}
kernel_memory = kmalloc(sizeof(int), GFP_KERNEL);
kernel_memory[0] = 0;
// Core functionality.
asm volatile(
// 1000 repetitions to confuse the Pattern History Table sufficiently and
// achieve a Spectre v1 misspeculation.
".rept 1000\n"
// Flush kernel_memory from cache and synchronize.
"dc civac, %0\n"
"dsb sy\n"
// Slowly load kernel_memory from main memory and speculate forward in the
// meantime.
"ldr w1, [%0]\n"
// Impossible condition - always false because the kernel_memory[0] is 0
// and not 1.
"cmp w1, #0x1\n"
// De-facto unconditional forward jump to the 1: label.
"bne 1f\n"
// Dead code begins - the four following instructions are executed only
// speculatively.
"eret\n"
"hvc #0\n"
"smc #0\n"
// Load the userspace_address speculatively after speculating over ERET,
// HVC and SMC.
"ldrb w1, [%1]\n"
// Dead code ends.
"1:\n"
".endr\n"::"r"(kernel_memory), "r"(userspace_address));
kfree(kernel_memory);
// Disable kernel access to userspace memory.
__uaccess_disable(ARM64_ALT_PAN_NOT_UAO);
return length;
}
static struct file_operations address_file_ops = {
write: address_store
};
static int __init eret_hvc_smc_init(void) {
struct proc_dir_entry *address;
pr_info("safeside_eret_hvc_smc init\n");
safeside_eret_hvc_smc = proc_mkdir("safeside_eret_hvc_smc", NULL);
if (safeside_eret_hvc_smc == NULL) {
return -ENOMEM;
}
// Write-only file, accessible only by root.
address = proc_create("address", 0200, safeside_eret_hvc_smc,
&address_file_ops);
if (address == NULL) {
remove_proc_entry("safeside_eret_hvc_smc", NULL);
return -ENOMEM;
}
return 0;
}
static void __exit eret_hvc_smc_exit(void) {
pr_info("safeside_eret_hvc_smc exit\n");
remove_proc_entry("address", safeside_eret_hvc_smc);
remove_proc_entry("safeside_eret_hvc_smc", NULL);
}
module_init(eret_hvc_smc_init);
module_exit(eret_hvc_smc_exit);