linux/drivers/misc/lkdtm_usercopy.c
<<
>>
Prefs
   1/*
   2 * This is for all the tests related to copy_to_user() and copy_from_user()
   3 * hardening.
   4 */
   5#include "lkdtm.h"
   6#include <linux/slab.h>
   7#include <linux/vmalloc.h>
   8#include <linux/mman.h>
   9#include <linux/uaccess.h>
  10#include <asm/cacheflush.h>
  11
  12/*
  13 * Many of the tests here end up using const sizes, but those would
  14 * normally be ignored by hardened usercopy, so force the compiler
  15 * into choosing the non-const path to make sure we trigger the
  16 * hardened usercopy checks by added "unconst" to all the const copies,
  17 * and making sure "cache_size" isn't optimized into a const.
  18 */
  19static volatile size_t unconst = 0;
  20static volatile size_t cache_size = 1024;
  21static struct kmem_cache *bad_cache;
  22
  23static const unsigned char test_text[] = "This is a test.\n";
  24
  25/*
  26 * Instead of adding -Wno-return-local-addr, just pass the stack address
  27 * through a function to obfuscate it from the compiler.
  28 */
  29static noinline unsigned char *trick_compiler(unsigned char *stack)
  30{
  31        return stack + 0;
  32}
  33
  34static noinline unsigned char *do_usercopy_stack_callee(int value)
  35{
  36        unsigned char buf[32];
  37        int i;
  38
  39        /* Exercise stack to avoid everything living in registers. */
  40        for (i = 0; i < sizeof(buf); i++) {
  41                buf[i] = value & 0xff;
  42        }
  43
  44        return trick_compiler(buf);
  45}
  46
  47static noinline void do_usercopy_stack(bool to_user, bool bad_frame)
  48{
  49        unsigned long user_addr;
  50        unsigned char good_stack[32];
  51        unsigned char *bad_stack;
  52        int i;
  53
  54        /* Exercise stack to avoid everything living in registers. */
  55        for (i = 0; i < sizeof(good_stack); i++)
  56                good_stack[i] = test_text[i % sizeof(test_text)];
  57
  58        /* This is a pointer to outside our current stack frame. */
  59        if (bad_frame) {
  60                bad_stack = do_usercopy_stack_callee((uintptr_t)&bad_stack);
  61        } else {
  62                /* Put start address just inside stack. */
  63                bad_stack = task_stack_page(current) + THREAD_SIZE;
  64                bad_stack -= sizeof(unsigned long);
  65        }
  66
  67        user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
  68                            PROT_READ | PROT_WRITE | PROT_EXEC,
  69                            MAP_ANONYMOUS | MAP_PRIVATE, 0);
  70        if (user_addr >= TASK_SIZE) {
  71                pr_warn("Failed to allocate user memory\n");
  72                return;
  73        }
  74
  75        if (to_user) {
  76                pr_info("attempting good copy_to_user of local stack\n");
  77                if (copy_to_user((void __user *)user_addr, good_stack,
  78                                 unconst + sizeof(good_stack))) {
  79                        pr_warn("copy_to_user failed unexpectedly?!\n");
  80                        goto free_user;
  81                }
  82
  83                pr_info("attempting bad copy_to_user of distant stack\n");
  84                if (copy_to_user((void __user *)user_addr, bad_stack,
  85                                 unconst + sizeof(good_stack))) {
  86                        pr_warn("copy_to_user failed, but lacked Oops\n");
  87                        goto free_user;
  88                }
  89        } else {
  90                /*
  91                 * There isn't a safe way to not be protected by usercopy
  92                 * if we're going to write to another thread's stack.
  93                 */
  94                if (!bad_frame)
  95                        goto free_user;
  96
  97                pr_info("attempting good copy_from_user of local stack\n");
  98                if (copy_from_user(good_stack, (void __user *)user_addr,
  99                                   unconst + sizeof(good_stack))) {
 100                        pr_warn("copy_from_user failed unexpectedly?!\n");
 101                        goto free_user;
 102                }
 103
 104                pr_info("attempting bad copy_from_user of distant stack\n");
 105                if (copy_from_user(bad_stack, (void __user *)user_addr,
 106                                   unconst + sizeof(good_stack))) {
 107                        pr_warn("copy_from_user failed, but lacked Oops\n");
 108                        goto free_user;
 109                }
 110        }
 111
 112free_user:
 113        vm_munmap(user_addr, PAGE_SIZE);
 114}
 115
 116static void do_usercopy_heap_size(bool to_user)
 117{
 118        unsigned long user_addr;
 119        unsigned char *one, *two;
 120        size_t size = unconst + 1024;
 121
 122        one = kmalloc(size, GFP_KERNEL);
 123        two = kmalloc(size, GFP_KERNEL);
 124        if (!one || !two) {
 125                pr_warn("Failed to allocate kernel memory\n");
 126                goto free_kernel;
 127        }
 128
 129        user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
 130                            PROT_READ | PROT_WRITE | PROT_EXEC,
 131                            MAP_ANONYMOUS | MAP_PRIVATE, 0);
 132        if (user_addr >= TASK_SIZE) {
 133                pr_warn("Failed to allocate user memory\n");
 134                goto free_kernel;
 135        }
 136
 137        memset(one, 'A', size);
 138        memset(two, 'B', size);
 139
 140        if (to_user) {
 141                pr_info("attempting good copy_to_user of correct size\n");
 142                if (copy_to_user((void __user *)user_addr, one, size)) {
 143                        pr_warn("copy_to_user failed unexpectedly?!\n");
 144                        goto free_user;
 145                }
 146
 147                pr_info("attempting bad copy_to_user of too large size\n");
 148                if (copy_to_user((void __user *)user_addr, one, 2 * size)) {
 149                        pr_warn("copy_to_user failed, but lacked Oops\n");
 150                        goto free_user;
 151                }
 152        } else {
 153                pr_info("attempting good copy_from_user of correct size\n");
 154                if (copy_from_user(one, (void __user *)user_addr, size)) {
 155                        pr_warn("copy_from_user failed unexpectedly?!\n");
 156                        goto free_user;
 157                }
 158
 159                pr_info("attempting bad copy_from_user of too large size\n");
 160                if (copy_from_user(one, (void __user *)user_addr, 2 * size)) {
 161                        pr_warn("copy_from_user failed, but lacked Oops\n");
 162                        goto free_user;
 163                }
 164        }
 165
 166free_user:
 167        vm_munmap(user_addr, PAGE_SIZE);
 168free_kernel:
 169        kfree(one);
 170        kfree(two);
 171}
 172
 173static void do_usercopy_heap_flag(bool to_user)
 174{
 175        unsigned long user_addr;
 176        unsigned char *good_buf = NULL;
 177        unsigned char *bad_buf = NULL;
 178
 179        /* Make sure cache was prepared. */
 180        if (!bad_cache) {
 181                pr_warn("Failed to allocate kernel cache\n");
 182                return;
 183        }
 184
 185        /*
 186         * Allocate one buffer from each cache (kmalloc will have the
 187         * SLAB_USERCOPY flag already, but "bad_cache" won't).
 188         */
 189        good_buf = kmalloc(cache_size, GFP_KERNEL);
 190        bad_buf = kmem_cache_alloc(bad_cache, GFP_KERNEL);
 191        if (!good_buf || !bad_buf) {
 192                pr_warn("Failed to allocate buffers from caches\n");
 193                goto free_alloc;
 194        }
 195
 196        /* Allocate user memory we'll poke at. */
 197        user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
 198                            PROT_READ | PROT_WRITE | PROT_EXEC,
 199                            MAP_ANONYMOUS | MAP_PRIVATE, 0);
 200        if (user_addr >= TASK_SIZE) {
 201                pr_warn("Failed to allocate user memory\n");
 202                goto free_alloc;
 203        }
 204
 205        memset(good_buf, 'A', cache_size);
 206        memset(bad_buf, 'B', cache_size);
 207
 208        if (to_user) {
 209                pr_info("attempting good copy_to_user with SLAB_USERCOPY\n");
 210                if (copy_to_user((void __user *)user_addr, good_buf,
 211                                 cache_size)) {
 212                        pr_warn("copy_to_user failed unexpectedly?!\n");
 213                        goto free_user;
 214                }
 215
 216                pr_info("attempting bad copy_to_user w/o SLAB_USERCOPY\n");
 217                if (copy_to_user((void __user *)user_addr, bad_buf,
 218                                 cache_size)) {
 219                        pr_warn("copy_to_user failed, but lacked Oops\n");
 220                        goto free_user;
 221                }
 222        } else {
 223                pr_info("attempting good copy_from_user with SLAB_USERCOPY\n");
 224                if (copy_from_user(good_buf, (void __user *)user_addr,
 225                                   cache_size)) {
 226                        pr_warn("copy_from_user failed unexpectedly?!\n");
 227                        goto free_user;
 228                }
 229
 230                pr_info("attempting bad copy_from_user w/o SLAB_USERCOPY\n");
 231                if (copy_from_user(bad_buf, (void __user *)user_addr,
 232                                   cache_size)) {
 233                        pr_warn("copy_from_user failed, but lacked Oops\n");
 234                        goto free_user;
 235                }
 236        }
 237
 238free_user:
 239        vm_munmap(user_addr, PAGE_SIZE);
 240free_alloc:
 241        if (bad_buf)
 242                kmem_cache_free(bad_cache, bad_buf);
 243        kfree(good_buf);
 244}
 245
 246/* Callable tests. */
 247void lkdtm_USERCOPY_HEAP_SIZE_TO(void)
 248{
 249        do_usercopy_heap_size(true);
 250}
 251
 252void lkdtm_USERCOPY_HEAP_SIZE_FROM(void)
 253{
 254        do_usercopy_heap_size(false);
 255}
 256
 257void lkdtm_USERCOPY_HEAP_FLAG_TO(void)
 258{
 259        do_usercopy_heap_flag(true);
 260}
 261
 262void lkdtm_USERCOPY_HEAP_FLAG_FROM(void)
 263{
 264        do_usercopy_heap_flag(false);
 265}
 266
 267void lkdtm_USERCOPY_STACK_FRAME_TO(void)
 268{
 269        do_usercopy_stack(true, true);
 270}
 271
 272void lkdtm_USERCOPY_STACK_FRAME_FROM(void)
 273{
 274        do_usercopy_stack(false, true);
 275}
 276
 277void lkdtm_USERCOPY_STACK_BEYOND(void)
 278{
 279        do_usercopy_stack(true, false);
 280}
 281
 282void lkdtm_USERCOPY_KERNEL(void)
 283{
 284        unsigned long user_addr;
 285
 286        user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
 287                            PROT_READ | PROT_WRITE | PROT_EXEC,
 288                            MAP_ANONYMOUS | MAP_PRIVATE, 0);
 289        if (user_addr >= TASK_SIZE) {
 290                pr_warn("Failed to allocate user memory\n");
 291                return;
 292        }
 293
 294        pr_info("attempting good copy_to_user from kernel rodata\n");
 295        if (copy_to_user((void __user *)user_addr, test_text,
 296                         unconst + sizeof(test_text))) {
 297                pr_warn("copy_to_user failed unexpectedly?!\n");
 298                goto free_user;
 299        }
 300
 301        pr_info("attempting bad copy_to_user from kernel text\n");
 302        if (copy_to_user((void __user *)user_addr, vm_mmap,
 303                         unconst + PAGE_SIZE)) {
 304                pr_warn("copy_to_user failed, but lacked Oops\n");
 305                goto free_user;
 306        }
 307
 308free_user:
 309        vm_munmap(user_addr, PAGE_SIZE);
 310}
 311
 312void __init lkdtm_usercopy_init(void)
 313{
 314        /* Prepare cache that lacks SLAB_USERCOPY flag. */
 315        bad_cache = kmem_cache_create("lkdtm-no-usercopy", cache_size, 0,
 316                                      0, NULL);
 317}
 318
 319void __exit lkdtm_usercopy_exit(void)
 320{
 321        kmem_cache_destroy(bad_cache);
 322}
 323