xref: /linux/drivers/misc/lkdtm/fortify.c (revision cf2f06f7152d5e38a87aa2e9b8b452714789f6ba)
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