Commit | Line | Data |
---|---|---|
febebaf3 FL |
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 | ||
fe8e353b KC |
11 | static volatile int fortify_scratch_space; |
12 | ||
73f62e60 | 13 | static void lkdtm_FORTIFIED_OBJECT(void) |
fe8e353b KC |
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 | ||
73f62e60 | 34 | static void lkdtm_FORTIFIED_SUBOBJECT(void) |
fe8e353b KC |
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 | ||
f4e335f3 | 47 | pr_info("trying to strncpy past the end of a member of a struct\n"); |
fe8e353b KC |
48 | |
49 | /* | |
f4e335f3 | 50 | * strncpy(target.a, src, 20); will hit a compile error because the |
fe8e353b KC |
51 | * compiler knows at build time that target.a < 20 bytes. Use a |
52 | * volatile to force a runtime error. | |
53 | */ | |
f4e335f3 | 54 | strncpy(target.a, src, size); |
fe8e353b KC |
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 | } | |
febebaf3 FL |
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 | */ | |
73f62e60 | 70 | static void lkdtm_FORTIFIED_STRSCPY(void) |
febebaf3 FL |
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 | ||
5b777131 KC |
132 | pr_err("FAIL: strscpy() overflow not detected!\n"); |
133 | pr_expected_config(CONFIG_FORTIFY_SOURCE); | |
febebaf3 FL |
134 | |
135 | kfree(src); | |
136 | } | |
73f62e60 KC |
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 | }; |