Skip to content

Commit 475ddf1

Browse files
committed
fortify: Split reporting and avoid passing string pointer
In preparation for KUnit testing and further improvements in fortify failure reporting, split out the report and encode the function and access failure (read or write overflow) into a single u8 argument. This mainly ends up saving a tiny bit of space in the data segment. For a defconfig with FORTIFY_SOURCE enabled: $ size gcc/vmlinux.before gcc/vmlinux.after text data bss dec hex filename 26132309 9760658 2195460 38088427 2452eeb gcc/vmlinux.before 26132386 9748382 2195460 38076228 244ff44 gcc/vmlinux.after Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com> Signed-off-by: Kees Cook <keescook@chromium.org>
1 parent 99db710 commit 475ddf1

6 files changed

Lines changed: 84 additions & 28 deletions

File tree

arch/arm/boot/compressed/misc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
154154
putstr(" done, booting the kernel.\n");
155155
}
156156

157-
void fortify_panic(const char *name)
157+
void __fortify_panic(const u8 reason)
158158
{
159159
error("detected buffer overflow");
160160
}

arch/arm/boot/compressed/misc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ void __div0(void);
1010
void
1111
decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
1212
unsigned long free_mem_ptr_end_p, int arch_id);
13-
void fortify_panic(const char *name);
13+
void __fortify_panic(const u8 reason);
1414
int atags_to_fdt(void *atag_list, void *fdt, int total_space);
1515
uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt);
1616
int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x));

arch/x86/boot/compressed/misc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ asmlinkage __visible void *extract_kernel(void *rmode, unsigned char *output)
496496
return output + entry_offset;
497497
}
498498

499-
void fortify_panic(const char *name)
499+
void __fortify_panic(const u8 reason)
500500
{
501501
error("detected buffer overflow");
502502
}

include/linux/fortify-string.h

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,52 @@
22
#ifndef _LINUX_FORTIFY_STRING_H_
33
#define _LINUX_FORTIFY_STRING_H_
44

5+
#include <linux/bitfield.h>
56
#include <linux/bug.h>
67
#include <linux/const.h>
78
#include <linux/limits.h>
89

910
#define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable
1011
#define __RENAME(x) __asm__(#x)
1112

12-
void fortify_panic(const char *name) __noreturn __cold;
13+
#define FORTIFY_REASON_DIR(r) FIELD_GET(BIT(0), r)
14+
#define FORTIFY_REASON_FUNC(r) FIELD_GET(GENMASK(7, 1), r)
15+
#define FORTIFY_REASON(func, write) (FIELD_PREP(BIT(0), write) | \
16+
FIELD_PREP(GENMASK(7, 1), func))
17+
18+
#define fortify_panic(func, write) \
19+
__fortify_panic(FORTIFY_REASON(func, write))
20+
21+
#define FORTIFY_READ 0
22+
#define FORTIFY_WRITE 1
23+
24+
#define EACH_FORTIFY_FUNC(macro) \
25+
macro(strncpy), \
26+
macro(strnlen), \
27+
macro(strlen), \
28+
macro(strscpy), \
29+
macro(strlcat), \
30+
macro(strcat), \
31+
macro(strncat), \
32+
macro(memset), \
33+
macro(memcpy), \
34+
macro(memmove), \
35+
macro(memscan), \
36+
macro(memcmp), \
37+
macro(memchr), \
38+
macro(memchr_inv), \
39+
macro(kmemdup), \
40+
macro(strcpy), \
41+
macro(UNKNOWN),
42+
43+
#define MAKE_FORTIFY_FUNC(func) FORTIFY_FUNC_##func
44+
45+
enum fortify_func {
46+
EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC)
47+
};
48+
49+
void __fortify_report(const u8 reason);
50+
void __fortify_panic(const u8 reason) __cold __noreturn;
1351
void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)");
1452
void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)");
1553
void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("detected read beyond size of field (2nd parameter); maybe use struct_group()?");
@@ -143,7 +181,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size)
143181
if (__compiletime_lessthan(p_size, size))
144182
__write_overflow();
145183
if (p_size < size)
146-
fortify_panic(__func__);
184+
fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE);
147185
return __underlying_strncpy(p, q, size);
148186
}
149187

@@ -174,7 +212,7 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size
174212
/* Do not check characters beyond the end of p. */
175213
ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size);
176214
if (p_size <= ret && maxlen != ret)
177-
fortify_panic(__func__);
215+
fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ);
178216
return ret;
179217
}
180218

@@ -210,7 +248,7 @@ __kernel_size_t __fortify_strlen(const char * const POS p)
210248
return __underlying_strlen(p);
211249
ret = strnlen(p, p_size);
212250
if (p_size <= ret)
213-
fortify_panic(__func__);
251+
fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ);
214252
return ret;
215253
}
216254

@@ -261,7 +299,7 @@ __FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const PO
261299
* p_size.
262300
*/
263301
if (len > p_size)
264-
fortify_panic(__func__);
302+
fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE);
265303

266304
/*
267305
* We can now safely call vanilla strscpy because we are protected from:
@@ -319,7 +357,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail)
319357

320358
/* Give up if string is already overflowed. */
321359
if (p_size <= p_len)
322-
fortify_panic(__func__);
360+
fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ);
323361

324362
if (actual >= avail) {
325363
copy_len = avail - p_len - 1;
@@ -328,7 +366,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail)
328366

329367
/* Give up if copy will overflow. */
330368
if (p_size <= actual)
331-
fortify_panic(__func__);
369+
fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE);
332370
__underlying_memcpy(p + p_len, q, copy_len);
333371
p[actual] = '\0';
334372

@@ -357,7 +395,7 @@ char *strcat(char * const POS p, const char *q)
357395
const size_t p_size = __member_size(p);
358396

359397
if (strlcat(p, q, p_size) >= p_size)
360-
fortify_panic(__func__);
398+
fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE);
361399
return p;
362400
}
363401

@@ -393,7 +431,7 @@ char *strncat(char * const POS p, const char * const POS q, __kernel_size_t coun
393431
p_len = strlen(p);
394432
copy_len = strnlen(q, count);
395433
if (p_size < p_len + copy_len + 1)
396-
fortify_panic(__func__);
434+
fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE);
397435
__underlying_memcpy(p + p_len, q, copy_len);
398436
p[p_len + copy_len] = '\0';
399437
return p;
@@ -434,7 +472,7 @@ __FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size,
434472
* lengths are unknown.)
435473
*/
436474
if (p_size != SIZE_MAX && p_size < size)
437-
fortify_panic("memset");
475+
fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE);
438476
}
439477

440478
#define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({ \
@@ -488,7 +526,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
488526
const size_t q_size,
489527
const size_t p_size_field,
490528
const size_t q_size_field,
491-
const char *func)
529+
const u8 func)
492530
{
493531
if (__builtin_constant_p(size)) {
494532
/*
@@ -532,9 +570,10 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
532570
* (The SIZE_MAX test is to optimize away checks where the buffer
533571
* lengths are unknown.)
534572
*/
535-
if ((p_size != SIZE_MAX && p_size < size) ||
536-
(q_size != SIZE_MAX && q_size < size))
537-
fortify_panic(func);
573+
if (p_size != SIZE_MAX && p_size < size)
574+
fortify_panic(func, FORTIFY_WRITE);
575+
else if (q_size != SIZE_MAX && q_size < size)
576+
fortify_panic(func, FORTIFY_READ);
538577

539578
/*
540579
* Warn when writing beyond destination field size.
@@ -567,7 +606,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
567606
const size_t __q_size_field = (q_size_field); \
568607
WARN_ONCE(fortify_memcpy_chk(__fortify_size, __p_size, \
569608
__q_size, __p_size_field, \
570-
__q_size_field, #op), \
609+
__q_size_field, FORTIFY_FUNC_ ##op), \
571610
#op ": detected field-spanning write (size %zu) of single %s (size %zu)\n", \
572611
__fortify_size, \
573612
"field \"" #p "\" at " FILE_LINE, \
@@ -634,7 +673,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size)
634673
if (__compiletime_lessthan(p_size, size))
635674
__read_overflow();
636675
if (p_size < size)
637-
fortify_panic(__func__);
676+
fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ);
638677
return __real_memscan(p, c, size);
639678
}
640679

@@ -651,7 +690,7 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t
651690
__read_overflow2();
652691
}
653692
if (p_size < size || q_size < size)
654-
fortify_panic(__func__);
693+
fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ);
655694
return __underlying_memcmp(p, q, size);
656695
}
657696

@@ -663,7 +702,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size)
663702
if (__compiletime_lessthan(p_size, size))
664703
__read_overflow();
665704
if (p_size < size)
666-
fortify_panic(__func__);
705+
fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ);
667706
return __underlying_memchr(p, c, size);
668707
}
669708

@@ -675,7 +714,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size)
675714
if (__compiletime_lessthan(p_size, size))
676715
__read_overflow();
677716
if (p_size < size)
678-
fortify_panic(__func__);
717+
fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ);
679718
return __real_memchr_inv(p, c, size);
680719
}
681720

@@ -688,7 +727,7 @@ __FORTIFY_INLINE void *kmemdup(const void * const POS0 p, size_t size, gfp_t gfp
688727
if (__compiletime_lessthan(p_size, size))
689728
__read_overflow();
690729
if (p_size < size)
691-
fortify_panic(__func__);
730+
fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ);
692731
return __real_kmemdup(p, size, gfp);
693732
}
694733

@@ -725,7 +764,7 @@ char *strcpy(char * const POS p, const char * const POS q)
725764
__write_overflow();
726765
/* Run-time check for dynamic size overflow. */
727766
if (p_size < size)
728-
fortify_panic(__func__);
767+
fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE);
729768
__underlying_memcpy(p, q, size);
730769
return p;
731770
}

lib/string_helpers.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,10 +1008,27 @@ EXPORT_SYMBOL(__read_overflow2_field);
10081008
void __write_overflow_field(size_t avail, size_t wanted) { }
10091009
EXPORT_SYMBOL(__write_overflow_field);
10101010

1011-
void fortify_panic(const char *name)
1011+
static const char * const fortify_func_name[] = {
1012+
#define MAKE_FORTIFY_FUNC_NAME(func) [MAKE_FORTIFY_FUNC(func)] = #func
1013+
EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC_NAME)
1014+
#undef MAKE_FORTIFY_FUNC_NAME
1015+
};
1016+
1017+
void __fortify_report(const u8 reason)
1018+
{
1019+
const u8 func = FORTIFY_REASON_FUNC(reason);
1020+
const bool write = FORTIFY_REASON_DIR(reason);
1021+
const char *name;
1022+
1023+
name = fortify_func_name[umin(func, FORTIFY_FUNC_UNKNOWN)];
1024+
WARN(1, "%s: detected buffer %s overflow\n", name, str_read_write(!write));
1025+
}
1026+
EXPORT_SYMBOL(__fortify_report);
1027+
1028+
void __fortify_panic(const u8 reason)
10121029
{
1013-
pr_emerg("detected buffer overflow in %s\n", name);
1030+
__fortify_report(reason);
10141031
BUG();
10151032
}
1016-
EXPORT_SYMBOL(fortify_panic);
1033+
EXPORT_SYMBOL(__fortify_panic);
10171034
#endif /* CONFIG_FORTIFY_SOURCE */

tools/objtool/noreturns.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*
77
* Yes, this is unfortunate. A better solution is in the works.
88
*/
9+
NORETURN(__fortify_panic)
910
NORETURN(__kunit_abort)
1011
NORETURN(__module_put_and_kthread_exit)
1112
NORETURN(__reiserfs_panic)
@@ -22,7 +23,6 @@ NORETURN(do_exit)
2223
NORETURN(do_group_exit)
2324
NORETURN(do_task_dead)
2425
NORETURN(ex_handler_msr_mce)
25-
NORETURN(fortify_panic)
2626
NORETURN(hlt_play_dead)
2727
NORETURN(hv_ghcb_terminate)
2828
NORETURN(kthread_complete_and_exit)

0 commit comments

Comments
 (0)