xref: /linux/drivers/misc/lkdtm/fortify.c (revision a5ba183bdeeeedd5f5b683c02561072848258496)
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 
lkdtm_FORTIFY_STR_OBJECT(void)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 
lkdtm_FORTIFY_STR_MEMBER(void)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 
lkdtm_FORTIFY_MEM_OBJECT(void)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 
lkdtm_FORTIFY_MEM_MEMBER(void)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  */
lkdtm_FORTIFY_STRSCPY(void)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