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