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