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(p: target[0].a, q: 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(p: target.a, q: 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(objp: 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(objp: 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(s: "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(objp: 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 | |