linux/drivers/misc/lkdtm/fortify.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2020 Francis Laniel <laniel_francis@privacyrequired.com>
   4 *
   5 * Add tests related to fortified functions in this file.
   6 */
   7#include "lkdtm.h"
   8#include <linux/string.h>
   9#include <linux/slab.h>
  10
  11static volatile int fortify_scratch_space;
  12
  13void lkdtm_FORTIFIED_OBJECT(void)
  14{
  15        struct target {
  16                char a[10];
  17        } target[2] = {};
  18        /*
  19         * Using volatile prevents the compiler from determining the value of
  20         * 'size' at compile time. Without that, we would get a compile error
  21         * rather than a runtime error.
  22         */
  23        volatile int size = 11;
  24
  25        pr_info("trying to read past the end of a struct\n");
  26
  27        /* Store result to global to prevent the code from being eliminated */
  28        fortify_scratch_space = memcmp(&target[0], &target[1], size);
  29
  30        pr_err("FAIL: fortify did not block an object overread!\n");
  31        pr_expected_config(CONFIG_FORTIFY_SOURCE);
  32}
  33
  34void lkdtm_FORTIFIED_SUBOBJECT(void)
  35{
  36        struct target {
  37                char a[10];
  38                char b[10];
  39        } target;
  40        volatile int size = 20;
  41        char *src;
  42
  43        src = kmalloc(size, GFP_KERNEL);
  44        strscpy(src, "over ten bytes", size);
  45        size = strlen(src) + 1;
  46
  47        pr_info("trying to strcpy past the end of a member of a struct\n");
  48
  49        /*
  50         * memcpy(target.a, src, 20); will hit a compile error because the
  51         * compiler knows at build time that target.a < 20 bytes. Use a
  52         * volatile to force a runtime error.
  53         */
  54        memcpy(target.a, src, size);
  55
  56        /* Store result to global to prevent the code from being eliminated */
  57        fortify_scratch_space = target.a[3];
  58
  59        pr_err("FAIL: fortify did not block an sub-object overrun!\n");
  60        pr_expected_config(CONFIG_FORTIFY_SOURCE);
  61
  62        kfree(src);
  63}
  64
  65/*
  66 * Calls fortified strscpy to test that it returns the same result as vanilla
  67 * strscpy and generate a panic because there is a write overflow (i.e. src
  68 * length is greater than dst length).
  69 */
  70void lkdtm_FORTIFIED_STRSCPY(void)
  71{
  72        char *src;
  73        char dst[5];
  74
  75        struct {
  76                union {
  77                        char big[10];
  78                        char src[5];
  79                };
  80        } weird = { .big = "hello!" };
  81        char weird_dst[sizeof(weird.src) + 1];
  82
  83        src = kstrdup("foobar", GFP_KERNEL);
  84
  85        if (src == NULL)
  86                return;
  87
  88        /* Vanilla strscpy returns -E2BIG if size is 0. */
  89        if (strscpy(dst, src, 0) != -E2BIG)
  90                pr_warn("FAIL: strscpy() of 0 length did not return -E2BIG\n");
  91
  92        /* Vanilla strscpy returns -E2BIG if src is truncated. */
  93        if (strscpy(dst, src, sizeof(dst)) != -E2BIG)
  94                pr_warn("FAIL: strscpy() did not return -E2BIG while src is truncated\n");
  95
  96        /* After above call, dst must contain "foob" because src was truncated. */
  97        if (strncmp(dst, "foob", sizeof(dst)) != 0)
  98                pr_warn("FAIL: after strscpy() dst does not contain \"foob\" but \"%s\"\n",
  99                        dst);
 100
 101        /* Shrink src so the strscpy() below succeeds. */
 102        src[3] = '\0';
 103
 104        /*
 105         * Vanilla strscpy returns number of character copied if everything goes
 106         * well.
 107         */
 108        if (strscpy(dst, src, sizeof(dst)) != 3)
 109                pr_warn("FAIL: strscpy() did not return 3 while src was copied entirely truncated\n");
 110
 111        /* After above call, dst must contain "foo" because src was copied. */
 112        if (strncmp(dst, "foo", sizeof(dst)) != 0)
 113                pr_warn("FAIL: after strscpy() dst does not contain \"foo\" but \"%s\"\n",
 114                        dst);
 115
 116        /* Test when src is embedded inside a union. */
 117        strscpy(weird_dst, weird.src, sizeof(weird_dst));
 118
 119        if (strcmp(weird_dst, "hello") != 0)
 120                pr_warn("FAIL: after strscpy() weird_dst does not contain \"hello\" but \"%s\"\n",
 121                        weird_dst);
 122
 123        /* Restore src to its initial value. */
 124        src[3] = 'b';
 125
 126        /*
 127         * Use strlen here so size cannot be known at compile time and there is
 128         * a runtime write overflow.
 129         */
 130        strscpy(dst, src, strlen(src));
 131
 132        pr_err("FAIL: strscpy() overflow not detected!\n");
 133        pr_expected_config(CONFIG_FORTIFY_SOURCE);
 134
 135        kfree(src);
 136}
 137