cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

perms.c (7249B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * This is for all the tests related to validating kernel memory
      4 * permissions: non-executable regions, non-writable regions, and
      5 * even non-readable regions.
      6 */
      7#include "lkdtm.h"
      8#include <linux/slab.h>
      9#include <linux/vmalloc.h>
     10#include <linux/mman.h>
     11#include <linux/uaccess.h>
     12#include <asm/cacheflush.h>
     13#include <asm/sections.h>
     14
     15/* Whether or not to fill the target memory area with do_nothing(). */
     16#define CODE_WRITE	true
     17#define CODE_AS_IS	false
     18
     19/* How many bytes to copy to be sure we've copied enough of do_nothing(). */
     20#define EXEC_SIZE 64
     21
     22/* This is non-const, so it will end up in the .data section. */
     23static u8 data_area[EXEC_SIZE];
     24
     25/* This is const, so it will end up in the .rodata section. */
     26static const unsigned long rodata = 0xAA55AA55;
     27
     28/* This is marked __ro_after_init, so it should ultimately be .rodata. */
     29static unsigned long ro_after_init __ro_after_init = 0x55AA5500;
     30
     31/*
     32 * This just returns to the caller. It is designed to be copied into
     33 * non-executable memory regions.
     34 */
     35static noinline void do_nothing(void)
     36{
     37	return;
     38}
     39
     40/* Must immediately follow do_nothing for size calculuations to work out. */
     41static noinline void do_overwritten(void)
     42{
     43	pr_info("do_overwritten wasn't overwritten!\n");
     44	return;
     45}
     46
     47static noinline void do_almost_nothing(void)
     48{
     49	pr_info("do_nothing was hijacked!\n");
     50}
     51
     52static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
     53{
     54	if (!have_function_descriptors())
     55		return dst;
     56
     57	memcpy(fdesc, do_nothing, sizeof(*fdesc));
     58	fdesc->addr = (unsigned long)dst;
     59	barrier();
     60
     61	return fdesc;
     62}
     63
     64static noinline void execute_location(void *dst, bool write)
     65{
     66	void (*func)(void);
     67	func_desc_t fdesc;
     68	void *do_nothing_text = dereference_function_descriptor(do_nothing);
     69
     70	pr_info("attempting ok execution at %px\n", do_nothing_text);
     71	do_nothing();
     72
     73	if (write == CODE_WRITE) {
     74		memcpy(dst, do_nothing_text, EXEC_SIZE);
     75		flush_icache_range((unsigned long)dst,
     76				   (unsigned long)dst + EXEC_SIZE);
     77	}
     78	pr_info("attempting bad execution at %px\n", dst);
     79	func = setup_function_descriptor(&fdesc, dst);
     80	func();
     81	pr_err("FAIL: func returned\n");
     82}
     83
     84static void execute_user_location(void *dst)
     85{
     86	int copied;
     87
     88	/* Intentionally crossing kernel/user memory boundary. */
     89	void (*func)(void);
     90	func_desc_t fdesc;
     91	void *do_nothing_text = dereference_function_descriptor(do_nothing);
     92
     93	pr_info("attempting ok execution at %px\n", do_nothing_text);
     94	do_nothing();
     95
     96	copied = access_process_vm(current, (unsigned long)dst, do_nothing_text,
     97				   EXEC_SIZE, FOLL_WRITE);
     98	if (copied < EXEC_SIZE)
     99		return;
    100	pr_info("attempting bad execution at %px\n", dst);
    101	func = setup_function_descriptor(&fdesc, dst);
    102	func();
    103	pr_err("FAIL: func returned\n");
    104}
    105
    106static void lkdtm_WRITE_RO(void)
    107{
    108	/* Explicitly cast away "const" for the test and make volatile. */
    109	volatile unsigned long *ptr = (unsigned long *)&rodata;
    110
    111	pr_info("attempting bad rodata write at %px\n", ptr);
    112	*ptr ^= 0xabcd1234;
    113	pr_err("FAIL: survived bad write\n");
    114}
    115
    116static void lkdtm_WRITE_RO_AFTER_INIT(void)
    117{
    118	volatile unsigned long *ptr = &ro_after_init;
    119
    120	/*
    121	 * Verify we were written to during init. Since an Oops
    122	 * is considered a "success", a failure is to just skip the
    123	 * real test.
    124	 */
    125	if ((*ptr & 0xAA) != 0xAA) {
    126		pr_info("%p was NOT written during init!?\n", ptr);
    127		return;
    128	}
    129
    130	pr_info("attempting bad ro_after_init write at %px\n", ptr);
    131	*ptr ^= 0xabcd1234;
    132	pr_err("FAIL: survived bad write\n");
    133}
    134
    135static void lkdtm_WRITE_KERN(void)
    136{
    137	size_t size;
    138	volatile unsigned char *ptr;
    139
    140	size = (unsigned long)dereference_function_descriptor(do_overwritten) -
    141	       (unsigned long)dereference_function_descriptor(do_nothing);
    142	ptr = dereference_function_descriptor(do_overwritten);
    143
    144	pr_info("attempting bad %zu byte write at %px\n", size, ptr);
    145	memcpy((void *)ptr, (unsigned char *)do_nothing, size);
    146	flush_icache_range((unsigned long)ptr, (unsigned long)(ptr + size));
    147	pr_err("FAIL: survived bad write\n");
    148
    149	do_overwritten();
    150}
    151
    152static void lkdtm_WRITE_OPD(void)
    153{
    154	size_t size = sizeof(func_desc_t);
    155	void (*func)(void) = do_nothing;
    156
    157	if (!have_function_descriptors()) {
    158		pr_info("XFAIL: Platform doesn't use function descriptors.\n");
    159		return;
    160	}
    161	pr_info("attempting bad %zu bytes write at %px\n", size, do_nothing);
    162	memcpy(do_nothing, do_almost_nothing, size);
    163	pr_err("FAIL: survived bad write\n");
    164
    165	asm("" : "=m"(func));
    166	func();
    167}
    168
    169static void lkdtm_EXEC_DATA(void)
    170{
    171	execute_location(data_area, CODE_WRITE);
    172}
    173
    174static void lkdtm_EXEC_STACK(void)
    175{
    176	u8 stack_area[EXEC_SIZE];
    177	execute_location(stack_area, CODE_WRITE);
    178}
    179
    180static void lkdtm_EXEC_KMALLOC(void)
    181{
    182	u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL);
    183	execute_location(kmalloc_area, CODE_WRITE);
    184	kfree(kmalloc_area);
    185}
    186
    187static void lkdtm_EXEC_VMALLOC(void)
    188{
    189	u32 *vmalloc_area = vmalloc(EXEC_SIZE);
    190	execute_location(vmalloc_area, CODE_WRITE);
    191	vfree(vmalloc_area);
    192}
    193
    194static void lkdtm_EXEC_RODATA(void)
    195{
    196	execute_location(dereference_function_descriptor(lkdtm_rodata_do_nothing),
    197			 CODE_AS_IS);
    198}
    199
    200static void lkdtm_EXEC_USERSPACE(void)
    201{
    202	unsigned long user_addr;
    203
    204	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
    205			    PROT_READ | PROT_WRITE | PROT_EXEC,
    206			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
    207	if (user_addr >= TASK_SIZE) {
    208		pr_warn("Failed to allocate user memory\n");
    209		return;
    210	}
    211	execute_user_location((void *)user_addr);
    212	vm_munmap(user_addr, PAGE_SIZE);
    213}
    214
    215static void lkdtm_EXEC_NULL(void)
    216{
    217	execute_location(NULL, CODE_AS_IS);
    218}
    219
    220static void lkdtm_ACCESS_USERSPACE(void)
    221{
    222	unsigned long user_addr, tmp = 0;
    223	unsigned long *ptr;
    224
    225	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
    226			    PROT_READ | PROT_WRITE | PROT_EXEC,
    227			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
    228	if (user_addr >= TASK_SIZE) {
    229		pr_warn("Failed to allocate user memory\n");
    230		return;
    231	}
    232
    233	if (copy_to_user((void __user *)user_addr, &tmp, sizeof(tmp))) {
    234		pr_warn("copy_to_user failed\n");
    235		vm_munmap(user_addr, PAGE_SIZE);
    236		return;
    237	}
    238
    239	ptr = (unsigned long *)user_addr;
    240
    241	pr_info("attempting bad read at %px\n", ptr);
    242	tmp = *ptr;
    243	tmp += 0xc0dec0de;
    244	pr_err("FAIL: survived bad read\n");
    245
    246	pr_info("attempting bad write at %px\n", ptr);
    247	*ptr = tmp;
    248	pr_err("FAIL: survived bad write\n");
    249
    250	vm_munmap(user_addr, PAGE_SIZE);
    251}
    252
    253static void lkdtm_ACCESS_NULL(void)
    254{
    255	unsigned long tmp;
    256	volatile unsigned long *ptr = (unsigned long *)NULL;
    257
    258	pr_info("attempting bad read at %px\n", ptr);
    259	tmp = *ptr;
    260	tmp += 0xc0dec0de;
    261	pr_err("FAIL: survived bad read\n");
    262
    263	pr_info("attempting bad write at %px\n", ptr);
    264	*ptr = tmp;
    265	pr_err("FAIL: survived bad write\n");
    266}
    267
    268void __init lkdtm_perms_init(void)
    269{
    270	/* Make sure we can write to __ro_after_init values during __init */
    271	ro_after_init |= 0xAA;
    272}
    273
    274static struct crashtype crashtypes[] = {
    275	CRASHTYPE(WRITE_RO),
    276	CRASHTYPE(WRITE_RO_AFTER_INIT),
    277	CRASHTYPE(WRITE_KERN),
    278	CRASHTYPE(WRITE_OPD),
    279	CRASHTYPE(EXEC_DATA),
    280	CRASHTYPE(EXEC_STACK),
    281	CRASHTYPE(EXEC_KMALLOC),
    282	CRASHTYPE(EXEC_VMALLOC),
    283	CRASHTYPE(EXEC_RODATA),
    284	CRASHTYPE(EXEC_USERSPACE),
    285	CRASHTYPE(EXEC_NULL),
    286	CRASHTYPE(ACCESS_USERSPACE),
    287	CRASHTYPE(ACCESS_NULL),
    288};
    289
    290struct crashtype_category perms_crashtypes = {
    291	.crashtypes = crashtypes,
    292	.len	    = ARRAY_SIZE(crashtypes),
    293};