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