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_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 34 static void 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 strncpy past the end of a member of a struct\n"); 48 49 /* 50 * strncpy(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 strncpy(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 */ 70 static void 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 138 static struct crashtype crashtypes[] = { 139 CRASHTYPE(FORTIFIED_OBJECT), 140 CRASHTYPE(FORTIFIED_SUBOBJECT), 141 CRASHTYPE(FORTIFIED_STRSCPY), 142 }; 143 144 struct crashtype_category fortify_crashtypes = { 145 .crashtypes = crashtypes, 146 .len = ARRAY_SIZE(crashtypes), 147 }; 148