Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 Francis Laniel <laniel_francis@privacyrequired.com> * * Add tests related to fortified functions in this file. */ #include "lkdtm.h" #include <linux/string.h> #include <linux/slab.h> static volatile int fortify_scratch_space; static void lkdtm_FORTIFY_STR_OBJECT(void) { struct target { char a[10]; int foo; } target[3] = {}; /* * Using volatile prevents the compiler from determining the value of * 'size' at compile time. Without that, we would get a compile error * rather than a runtime error. */ volatile int size = 20; pr_info("trying to strcmp() past the end of a struct\n"); strncpy(target[0].a, target[1].a, size); /* Store result to global to prevent the code from being eliminated */ fortify_scratch_space = target[0].a[3]; pr_err("FAIL: fortify did not block a strncpy() object write overflow!\n"); pr_expected_config(CONFIG_FORTIFY_SOURCE); } static void lkdtm_FORTIFY_STR_MEMBER(void) { struct target { char a[10]; char b[10]; } target; volatile int size = 20; char *src; src = kmalloc(size, GFP_KERNEL); strscpy(src, "over ten bytes", size); size = strlen(src) + 1; pr_info("trying to strncpy() past the end of a struct member...\n"); /* * strncpy(target.a, src, 20); will hit a compile error because the * compiler knows at build time that target.a < 20 bytes. Use a * volatile to force a runtime error. */ strncpy(target.a, src, size); /* Store result to global to prevent the code from being eliminated */ fortify_scratch_space = target.a[3]; pr_err("FAIL: fortify did not block a strncpy() struct member write overflow!\n"); pr_expected_config(CONFIG_FORTIFY_SOURCE); kfree(src); } static void lkdtm_FORTIFY_MEM_OBJECT(void) { int before[10]; struct target { char a[10]; int foo; } target = {}; int after[10]; /* * Using volatile prevents the compiler from determining the value of * 'size' at compile time. Without that, we would get a compile error * rather than a runtime error. */ volatile int size = 20; memset(before, 0, sizeof(before)); memset(after, 0, sizeof(after)); fortify_scratch_space = before[5]; fortify_scratch_space = after[5]; pr_info("trying to memcpy() past the end of a struct\n"); pr_info("0: %zu\n", __builtin_object_size(&target, 0)); pr_info("1: %zu\n", __builtin_object_size(&target, 1)); pr_info("s: %d\n", size); memcpy(&target, &before, size); /* Store result to global to prevent the code from being eliminated */ fortify_scratch_space = target.a[3]; pr_err("FAIL: fortify did not block a memcpy() object write overflow!\n"); pr_expected_config(CONFIG_FORTIFY_SOURCE); } static void lkdtm_FORTIFY_MEM_MEMBER(void) { struct target { char a[10]; char b[10]; } target; volatile int size = 20; char *src; src = kmalloc(size, GFP_KERNEL); strscpy(src, "over ten bytes", size); size = strlen(src) + 1; pr_info("trying to memcpy() past the end of a struct member...\n"); /* * strncpy(target.a, src, 20); will hit a compile error because the * compiler knows at build time that target.a < 20 bytes. Use a * volatile to force a runtime error. */ memcpy(target.a, src, size); /* Store result to global to prevent the code from being eliminated */ fortify_scratch_space = target.a[3]; pr_err("FAIL: fortify did not block a memcpy() struct member write overflow!\n"); pr_expected_config(CONFIG_FORTIFY_SOURCE); kfree(src); } /* * Calls fortified strscpy to test that it returns the same result as vanilla * strscpy and generate a panic because there is a write overflow (i.e. src * length is greater than dst length). */ static void lkdtm_FORTIFY_STRSCPY(void) { char *src; char dst[5]; struct { union { char big[10]; char src[5]; }; } weird = { .big = "hello!" }; char weird_dst[sizeof(weird.src) + 1]; src = kstrdup("foobar", GFP_KERNEL); if (src == NULL) return; /* Vanilla strscpy returns -E2BIG if size is 0. */ if (strscpy(dst, src, 0) != -E2BIG) pr_warn("FAIL: strscpy() of 0 length did not return -E2BIG\n"); /* Vanilla strscpy returns -E2BIG if src is truncated. */ if (strscpy(dst, src, sizeof(dst)) != -E2BIG) pr_warn("FAIL: strscpy() did not return -E2BIG while src is truncated\n"); /* After above call, dst must contain "foob" because src was truncated. */ if (strncmp(dst, "foob", sizeof(dst)) != 0) pr_warn("FAIL: after strscpy() dst does not contain \"foob\" but \"%s\"\n", dst); /* Shrink src so the strscpy() below succeeds. */ src[3] = '\0'; /* * Vanilla strscpy returns number of character copied if everything goes * well. */ if (strscpy(dst, src, sizeof(dst)) != 3) pr_warn("FAIL: strscpy() did not return 3 while src was copied entirely truncated\n"); /* After above call, dst must contain "foo" because src was copied. */ if (strncmp(dst, "foo", sizeof(dst)) != 0) pr_warn("FAIL: after strscpy() dst does not contain \"foo\" but \"%s\"\n", dst); /* Test when src is embedded inside a union. */ strscpy(weird_dst, weird.src, sizeof(weird_dst)); if (strcmp(weird_dst, "hello") != 0) pr_warn("FAIL: after strscpy() weird_dst does not contain \"hello\" but \"%s\"\n", weird_dst); /* Restore src to its initial value. */ src[3] = 'b'; /* * Use strlen here so size cannot be known at compile time and there is * a runtime write overflow. */ strscpy(dst, src, strlen(src)); pr_err("FAIL: strscpy() overflow not detected!\n"); pr_expected_config(CONFIG_FORTIFY_SOURCE); kfree(src); } static struct crashtype crashtypes[] = { CRASHTYPE(FORTIFY_STR_OBJECT), CRASHTYPE(FORTIFY_STR_MEMBER), CRASHTYPE(FORTIFY_MEM_OBJECT), CRASHTYPE(FORTIFY_MEM_MEMBER), CRASHTYPE(FORTIFY_STRSCPY), }; struct crashtype_category fortify_crashtypes = { .crashtypes = crashtypes, .len = ARRAY_SIZE(crashtypes), }; |