File Index
Files and directories
Sign in
FILE

fortify.c

File preview with syntax highlighting.

linux-6.19.14/drivers/misc/lkdtm/fortify.c C
  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
 11static volatile int fortify_scratch_space;
 12
 13static 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
 37static 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
 71static 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
105static 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 */
144static 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
212static 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
220struct crashtype_category fortify_crashtypes = {
221	.crashtypes = crashtypes,
222	.len	    = ARRAY_SIZE(crashtypes),
223};
~/ Read-only file preview.