LKML Archive on lore.kernel.org
 help / Atom feed
* [PATCH 0/7] CONFIG_FORTIFY_SOURCE
@ 2017-06-19 20:26 Kees Cook
  2017-06-19 20:26 ` [PATCH 1/7] efi: Avoid fortify checks in EFI stub Kees Cook
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Kees Cook, linux-kernel, Stephen Rothwell, Daniel Micay

Here are the outstanding fixes for CONFIG_FORTIFY_SOURCE, along with Daniel's
v5 patch and a tweak from me to add CONFIG_ARCH_HAS_FORTIFY_SOURCE to avoid
failing the build on architectures that have not hunted down all the needed
fixes yet.

This was in my for-next/kspp tree, but since it depends on fixes in other
trees, the preference is for these to all get carried in -mm instead of
in KSPP. The extra needed fixes in -next are:

        scsi: csiostor: Avoid content leaks and casts
        arm64, vdso: Define vdso_{start,end} as array
        staging/rts5208: Fix read overflow in memcpy
        libertas: Avoid reading past end of buffer
        ray_cs: Avoid reading past end of buffer

All the other fixes are already in Linus's tree.

-Kees

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/7] efi: Avoid fortify checks in EFI stub
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
@ 2017-06-19 20:26 ` Kees Cook
  2017-06-19 20:26 ` [PATCH 2/7] x86/power/64: Use char arrays for asm function names Kees Cook
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Kees Cook, Matt Fleming, linux-kernel, Stephen Rothwell, Daniel Micay

This avoids CONFIG_FORTIFY_SOURCE from being enabled during the EFI stub
build, as adding a panic() implementation may not work well. This can be
adjusted in the future.

Suggested-by: Daniel Micay <danielmicay@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Matt Fleming <matt@codeblueprint.co.uk>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
 drivers/firmware/efi/libstub/Makefile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index f7425960f6a5..37e24f525162 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -17,6 +17,7 @@ cflags-$(CONFIG_ARM)		:= $(subst -pg,,$(KBUILD_CFLAGS)) \
 cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
 
 KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
+				   -D__NO_FORTIFY \
 				   $(call cc-option,-ffreestanding) \
 				   $(call cc-option,-fno-stack-protector)
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 2/7] x86/power/64: Use char arrays for asm function names
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
  2017-06-19 20:26 ` [PATCH 1/7] efi: Avoid fortify checks in EFI stub Kees Cook
@ 2017-06-19 20:26 ` Kees Cook
  2017-06-19 20:26 ` [PATCH 3/7] kexec_file: Adjust declaration of kexec_purgatory Kees Cook
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Kees Cook, Daniel Micay, linux-kernel, Stephen Rothwell

This switches the hibernate_64.S function names into character arrays
to match other areas of the kernel where this is done (e.g., linker
scripts). Specifically this fixes a compile-time error noticed by the
future CONFIG_FORTIFY_SOURCE routines that complained about PAGE_SIZE
being copied out of the "single byte" core_restore_code variable.

Additionally drops the "acpi_save_state_mem" exern which does not
appear to be used anywhere else in the kernel.

Cc: Daniel Micay <danielmicay@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
---
 arch/x86/include/asm/suspend_64.h | 5 ++---
 arch/x86/power/hibernate_64.c     | 6 +++---
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/suspend_64.h b/arch/x86/include/asm/suspend_64.h
index 6136a18152af..2bd96b4df140 100644
--- a/arch/x86/include/asm/suspend_64.h
+++ b/arch/x86/include/asm/suspend_64.h
@@ -42,8 +42,7 @@ struct saved_context {
 	set_debugreg((thread)->debugreg##register, register)
 
 /* routines for saving/restoring kernel state */
-extern int acpi_save_state_mem(void);
-extern char core_restore_code;
-extern char restore_registers;
+extern char core_restore_code[];
+extern char restore_registers[];
 
 #endif /* _ASM_X86_SUSPEND_64_H */
diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c
index a6e21fee22ea..2ab1c5059a61 100644
--- a/arch/x86/power/hibernate_64.c
+++ b/arch/x86/power/hibernate_64.c
@@ -147,7 +147,7 @@ static int relocate_restore_code(void)
 	if (!relocated_restore_code)
 		return -ENOMEM;
 
-	memcpy((void *)relocated_restore_code, &core_restore_code, PAGE_SIZE);
+	memcpy((void *)relocated_restore_code, core_restore_code, PAGE_SIZE);
 
 	/* Make the page containing the relocated code executable */
 	pgd = (pgd_t *)__va(read_cr3()) + pgd_index(relocated_restore_code);
@@ -292,8 +292,8 @@ int arch_hibernation_header_save(void *addr, unsigned int max_size)
 
 	if (max_size < sizeof(struct restore_data_record))
 		return -EOVERFLOW;
-	rdr->jump_address = (unsigned long)&restore_registers;
-	rdr->jump_address_phys = __pa_symbol(&restore_registers);
+	rdr->jump_address = (unsigned long)restore_registers;
+	rdr->jump_address_phys = __pa_symbol(restore_registers);
 	rdr->cr3 = restore_cr3;
 	rdr->magic = RESTORE_MAGIC;
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 3/7] kexec_file: Adjust declaration of kexec_purgatory
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
  2017-06-19 20:26 ` [PATCH 1/7] efi: Avoid fortify checks in EFI stub Kees Cook
  2017-06-19 20:26 ` [PATCH 2/7] x86/power/64: Use char arrays for asm function names Kees Cook
@ 2017-06-19 20:26 ` Kees Cook
  2017-06-19 20:26 ` [PATCH 4/7] IB/rxe: Do not copy extra stack memory to skb Kees Cook
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Kees Cook, Daniel Micay, linux-kernel, Stephen Rothwell

Defining kexec_purgatory as a zero-length char array upsets compile
time size checking. Since this is built on a per-arch basis, define
it as an unsized char array (like is done for other similar things,
e.g. linker sections). This silences the warning generated by the future
CONFIG_FORTIFY_SOURCE, which did not like the memcmp() of a "0 byte"
array. This drops the __weak and uses an extern instead, since both
users define kexec_purgatory.

Cc: Daniel Micay <danielmicay@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
 kernel/kexec_file.c     | 7 -------
 kernel/kexec_internal.h | 2 ++
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b118735fea9d..7a147a7add2e 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -26,13 +26,6 @@
 #include <linux/vmalloc.h>
 #include "kexec_internal.h"
 
-/*
- * Declare these symbols weak so that if architecture provides a purgatory,
- * these will be overridden.
- */
-char __weak kexec_purgatory[0];
-size_t __weak kexec_purgatory_size = 0;
-
 static int kexec_calculate_store_digests(struct kimage *image);
 
 /* Architectures can provide this probe function */
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index 799a8a452187..50dfcb039a41 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -17,6 +17,8 @@ extern struct mutex kexec_mutex;
 #ifdef CONFIG_KEXEC_FILE
 #include <linux/purgatory.h>
 void kimage_file_post_load_cleanup(struct kimage *image);
+extern char kexec_purgatory[];
+extern size_t kexec_purgatory_size;
 #else /* CONFIG_KEXEC_FILE */
 static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
 #endif /* CONFIG_KEXEC_FILE */
-- 
2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 4/7] IB/rxe: Do not copy extra stack memory to skb
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
                   ` (2 preceding siblings ...)
  2017-06-19 20:26 ` [PATCH 3/7] kexec_file: Adjust declaration of kexec_purgatory Kees Cook
@ 2017-06-19 20:26 ` Kees Cook
  2017-06-19 20:26 ` [PATCH 5/7] powerpc: Don't fortify prom_init Kees Cook
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Kees Cook, Moni Shoua, Doug Ledford, Sean Hefty, Daniel Micay,
	linux-kernel, Stephen Rothwell

This fixes a over-read condition detected by FORTIFY_SOURCE for this line:

	memcpy(SKB_TO_PKT(skb), &ack_pkt, sizeof(skb->cb));

The error was:

In file included from ./include/linux/bitmap.h:8:0,
                 from ./include/linux/cpumask.h:11,
                 from ./include/linux/mm_types_task.h:13,
                 from ./include/linux/mm_types.h:4,
                 from ./include/linux/kmemcheck.h:4,
                 from ./include/linux/skbuff.h:18,
                 from drivers/infiniband/sw/rxe/rxe_resp.c:34:
In function 'memcpy',
    inlined from 'send_atomic_ack.constprop' at drivers/infiniband/sw/rxe/rxe_resp.c:998:2,
    inlined from 'acknowledge' at drivers/infiniband/sw/rxe/rxe_resp.c:1026:3,
    inlined from 'rxe_responder' at drivers/infiniband/sw/rxe/rxe_resp.c:1286:10:
./include/linux/string.h:309:4: error: call to '__read_overflow2' declared with attribute error: detected read beyond size of object passed as 2nd parameter
    __read_overflow2();

Daniel Micay noted that struct rxe_pkt_info is 32 bytes on 32-bit
architectures, but skb->cb is still 64. The memcpy() over-reads 32
bytes. This fixes it by zeroing the unused bytes in skb->cb.

Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Moni Shoua <monis@mellanox.com>
Cc: Doug Ledford <dledford@redhat.com>
Cc: Sean Hefty <sean.hefty@intel.com>
Cc: Daniel Micay <danielmicay@gmail.com>
---
 drivers/infiniband/sw/rxe/rxe_resp.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 23039768f541..be944d5aa9af 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -995,7 +995,9 @@ static int send_atomic_ack(struct rxe_qp *qp, struct rxe_pkt_info *pkt,
 	free_rd_atomic_resource(qp, res);
 	rxe_advance_resp_resource(qp);
 
-	memcpy(SKB_TO_PKT(skb), &ack_pkt, sizeof(skb->cb));
+	memcpy(SKB_TO_PKT(skb), &ack_pkt, sizeof(ack_pkt));
+	memset((unsigned char *)SKB_TO_PKT(skb) + sizeof(ack_pkt), 0,
+	       sizeof(skb->cb) - sizeof(ack_pkt));
 
 	res->type = RXE_ATOMIC_MASK;
 	res->atomic.skb = skb;
-- 
2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 5/7] powerpc: Don't fortify prom_init
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
                   ` (3 preceding siblings ...)
  2017-06-19 20:26 ` [PATCH 4/7] IB/rxe: Do not copy extra stack memory to skb Kees Cook
@ 2017-06-19 20:26 ` Kees Cook
  2017-06-19 20:26 ` [PATCH 6/7] powerpc: Make feature-fixup tests fortify-safe Kees Cook
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Kees Cook, Daniel Axtens, Daniel Micay, linux-kernel, Stephen Rothwell

From: Daniel Axtens <dja@axtens.net>

prom_init is a bit special; in theory it should be able to be
linked separately to the kernel. To keep this from getting too
complex, the symbols that prom_init.c uses are checked.

Fortification adds symbols, and it gets quite messy as it includes
things like panic(). So just don't fortify prom_init.c for now.

Cc: Kees Cook <keescook@chromium.org>
Cc: Daniel Micay <danielmicay@gmail.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Acked-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/powerpc/kernel/prom_init.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index dd8a04f3053a..613f79f03877 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -15,6 +15,9 @@
 
 #undef DEBUG_PROM
 
+/* we cannot use FORTIFY as it brings in new symbols */
+#define __NO_FORTIFY
+
 #include <stdarg.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
-- 
2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 6/7] powerpc: Make feature-fixup tests fortify-safe
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
                   ` (4 preceding siblings ...)
  2017-06-19 20:26 ` [PATCH 5/7] powerpc: Don't fortify prom_init Kees Cook
@ 2017-06-19 20:26 ` Kees Cook
  2017-06-19 20:26 ` [PATCH 7/7] include/linux/string.h: add the option of fortified string.h functions Kees Cook
  2017-06-19 21:50 ` [PATCH 0/7] CONFIG_FORTIFY_SOURCE Andrew Morton
  7 siblings, 0 replies; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Kees Cook, Daniel Axtens, Daniel Micay, linux-kernel, Stephen Rothwell

From: Daniel Axtens <dja@axtens.net>

Testing the fortified string functions[1] would cause a kernel
panic on boot in test_feature_fixups() due to a buffer overflow
in memcmp.

This boils down to things like this:

  extern unsigned int ftr_fixup_test1;
  extern unsigned int ftr_fixup_test1_orig;

  check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);

We know that these are asm labels so it is safe to read up to
'size' bytes at those addresses.

However, because we have passed the address of a single unsigned
int to memcmp, the compiler believes the underlying object is in
fact a single unsigned int. So if size > sizeof(unsigned int),
there will be a panic at runtime.

We can fix this by changing the types: instead of calling the asm
labels unsigned ints, call them unsigned int[]s. Therefore the
size isn't incorrectly determined at compile time and we get a
regular unsafe memcmp and no panic.

[1] http://openwall.com/lists/kernel-hardening/2017/05/09/2

Suggested-by: Michael Ellerman <mpe@ellerman.id.au>
Cc: Kees Cook <keescook@chromium.org>
Cc: Daniel Micay <danielmicay@gmail.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Tested-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/powerpc/lib/feature-fixups.c | 180 +++++++++++++++++++-------------------
 1 file changed, 90 insertions(+), 90 deletions(-)

diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c
index f3917705c686..41cf5ae273cf 100644
--- a/arch/powerpc/lib/feature-fixups.c
+++ b/arch/powerpc/lib/feature-fixups.c
@@ -233,192 +233,192 @@ static long calc_offset(struct fixup_entry *entry, unsigned int *p)
 
 static void test_basic_patching(void)
 {
-	extern unsigned int ftr_fixup_test1;
-	extern unsigned int end_ftr_fixup_test1;
-	extern unsigned int ftr_fixup_test1_orig;
-	extern unsigned int ftr_fixup_test1_expected;
-	int size = &end_ftr_fixup_test1 - &ftr_fixup_test1;
+	extern unsigned int ftr_fixup_test1[];
+	extern unsigned int end_ftr_fixup_test1[];
+	extern unsigned int ftr_fixup_test1_orig[];
+	extern unsigned int ftr_fixup_test1_expected[];
+	int size = end_ftr_fixup_test1 - ftr_fixup_test1;
 
 	fixup.value = fixup.mask = 8;
-	fixup.start_off = calc_offset(&fixup, &ftr_fixup_test1 + 1);
-	fixup.end_off = calc_offset(&fixup, &ftr_fixup_test1 + 2);
+	fixup.start_off = calc_offset(&fixup, ftr_fixup_test1 + 1);
+	fixup.end_off = calc_offset(&fixup, ftr_fixup_test1 + 2);
 	fixup.alt_start_off = fixup.alt_end_off = 0;
 
 	/* Sanity check */
-	check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
+	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
 
 	/* Check we don't patch if the value matches */
 	patch_feature_section(8, &fixup);
-	check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
+	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
 
 	/* Check we do patch if the value doesn't match */
 	patch_feature_section(0, &fixup);
-	check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
+	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);
 
 	/* Check we do patch if the mask doesn't match */
-	memcpy(&ftr_fixup_test1, &ftr_fixup_test1_orig, size);
-	check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
+	memcpy(ftr_fixup_test1, ftr_fixup_test1_orig, size);
+	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
 	patch_feature_section(~8, &fixup);
-	check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
+	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);
 }
 
 static void test_alternative_patching(void)
 {
-	extern unsigned int ftr_fixup_test2;
-	extern unsigned int end_ftr_fixup_test2;
-	extern unsigned int ftr_fixup_test2_orig;
-	extern unsigned int ftr_fixup_test2_alt;
-	extern unsigned int ftr_fixup_test2_expected;
-	int size = &end_ftr_fixup_test2 - &ftr_fixup_test2;
+	extern unsigned int ftr_fixup_test2[];
+	extern unsigned int end_ftr_fixup_test2[];
+	extern unsigned int ftr_fixup_test2_orig[];
+	extern unsigned int ftr_fixup_test2_alt[];
+	extern unsigned int ftr_fixup_test2_expected[];
+	int size = end_ftr_fixup_test2 - ftr_fixup_test2;
 
 	fixup.value = fixup.mask = 0xF;
-	fixup.start_off = calc_offset(&fixup, &ftr_fixup_test2 + 1);
-	fixup.end_off = calc_offset(&fixup, &ftr_fixup_test2 + 2);
-	fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test2_alt);
-	fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test2_alt + 1);
+	fixup.start_off = calc_offset(&fixup, ftr_fixup_test2 + 1);
+	fixup.end_off = calc_offset(&fixup, ftr_fixup_test2 + 2);
+	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test2_alt);
+	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test2_alt + 1);
 
 	/* Sanity check */
-	check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
+	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
 
 	/* Check we don't patch if the value matches */
 	patch_feature_section(0xF, &fixup);
-	check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
+	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
 
 	/* Check we do patch if the value doesn't match */
 	patch_feature_section(0, &fixup);
-	check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
+	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);
 
 	/* Check we do patch if the mask doesn't match */
-	memcpy(&ftr_fixup_test2, &ftr_fixup_test2_orig, size);
-	check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
+	memcpy(ftr_fixup_test2, ftr_fixup_test2_orig, size);
+	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
 	patch_feature_section(~0xF, &fixup);
-	check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
+	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);
 }
 
 static void test_alternative_case_too_big(void)
 {
-	extern unsigned int ftr_fixup_test3;
-	extern unsigned int end_ftr_fixup_test3;
-	extern unsigned int ftr_fixup_test3_orig;
-	extern unsigned int ftr_fixup_test3_alt;
-	int size = &end_ftr_fixup_test3 - &ftr_fixup_test3;
+	extern unsigned int ftr_fixup_test3[];
+	extern unsigned int end_ftr_fixup_test3[];
+	extern unsigned int ftr_fixup_test3_orig[];
+	extern unsigned int ftr_fixup_test3_alt[];
+	int size = end_ftr_fixup_test3 - ftr_fixup_test3;
 
 	fixup.value = fixup.mask = 0xC;
-	fixup.start_off = calc_offset(&fixup, &ftr_fixup_test3 + 1);
-	fixup.end_off = calc_offset(&fixup, &ftr_fixup_test3 + 2);
-	fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test3_alt);
-	fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test3_alt + 2);
+	fixup.start_off = calc_offset(&fixup, ftr_fixup_test3 + 1);
+	fixup.end_off = calc_offset(&fixup, ftr_fixup_test3 + 2);
+	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test3_alt);
+	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test3_alt + 2);
 
 	/* Sanity check */
-	check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
+	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 
 	/* Expect nothing to be patched, and the error returned to us */
 	check(patch_feature_section(0xF, &fixup) == 1);
-	check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
+	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 	check(patch_feature_section(0, &fixup) == 1);
-	check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
+	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 	check(patch_feature_section(~0xF, &fixup) == 1);
-	check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
+	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 }
 
 static void test_alternative_case_too_small(void)
 {
-	extern unsigned int ftr_fixup_test4;
-	extern unsigned int end_ftr_fixup_test4;
-	extern unsigned int ftr_fixup_test4_orig;
-	extern unsigned int ftr_fixup_test4_alt;
-	extern unsigned int ftr_fixup_test4_expected;
-	int size = &end_ftr_fixup_test4 - &ftr_fixup_test4;
+	extern unsigned int ftr_fixup_test4[];
+	extern unsigned int end_ftr_fixup_test4[];
+	extern unsigned int ftr_fixup_test4_orig[];
+	extern unsigned int ftr_fixup_test4_alt[];
+	extern unsigned int ftr_fixup_test4_expected[];
+	int size = end_ftr_fixup_test4 - ftr_fixup_test4;
 	unsigned long flag;
 
 	/* Check a high-bit flag */
 	flag = 1UL << ((sizeof(unsigned long) - 1) * 8);
 	fixup.value = fixup.mask = flag;
-	fixup.start_off = calc_offset(&fixup, &ftr_fixup_test4 + 1);
-	fixup.end_off = calc_offset(&fixup, &ftr_fixup_test4 + 5);
-	fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test4_alt);
-	fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test4_alt + 2);
+	fixup.start_off = calc_offset(&fixup, ftr_fixup_test4 + 1);
+	fixup.end_off = calc_offset(&fixup, ftr_fixup_test4 + 5);
+	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test4_alt);
+	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test4_alt + 2);
 
 	/* Sanity check */
-	check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
+	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
 
 	/* Check we don't patch if the value matches */
 	patch_feature_section(flag, &fixup);
-	check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
+	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
 
 	/* Check we do patch if the value doesn't match */
 	patch_feature_section(0, &fixup);
-	check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
+	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);
 
 	/* Check we do patch if the mask doesn't match */
-	memcpy(&ftr_fixup_test4, &ftr_fixup_test4_orig, size);
-	check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
+	memcpy(ftr_fixup_test4, ftr_fixup_test4_orig, size);
+	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
 	patch_feature_section(~flag, &fixup);
-	check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
+	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);
 }
 
 static void test_alternative_case_with_branch(void)
 {
-	extern unsigned int ftr_fixup_test5;
-	extern unsigned int end_ftr_fixup_test5;
-	extern unsigned int ftr_fixup_test5_expected;
-	int size = &end_ftr_fixup_test5 - &ftr_fixup_test5;
+	extern unsigned int ftr_fixup_test5[];
+	extern unsigned int end_ftr_fixup_test5[];
+	extern unsigned int ftr_fixup_test5_expected[];
+	int size = end_ftr_fixup_test5 - ftr_fixup_test5;
 
-	check(memcmp(&ftr_fixup_test5, &ftr_fixup_test5_expected, size) == 0);
+	check(memcmp(ftr_fixup_test5, ftr_fixup_test5_expected, size) == 0);
 }
 
 static void test_alternative_case_with_external_branch(void)
 {
-	extern unsigned int ftr_fixup_test6;
-	extern unsigned int end_ftr_fixup_test6;
-	extern unsigned int ftr_fixup_test6_expected;
-	int size = &end_ftr_fixup_test6 - &ftr_fixup_test6;
+	extern unsigned int ftr_fixup_test6[];
+	extern unsigned int end_ftr_fixup_test6[];
+	extern unsigned int ftr_fixup_test6_expected[];
+	int size = end_ftr_fixup_test6 - ftr_fixup_test6;
 
-	check(memcmp(&ftr_fixup_test6, &ftr_fixup_test6_expected, size) == 0);
+	check(memcmp(ftr_fixup_test6, ftr_fixup_test6_expected, size) == 0);
 }
 
 static void test_cpu_macros(void)
 {
-	extern u8 ftr_fixup_test_FTR_macros;
-	extern u8 ftr_fixup_test_FTR_macros_expected;
-	unsigned long size = &ftr_fixup_test_FTR_macros_expected -
-			     &ftr_fixup_test_FTR_macros;
+	extern u8 ftr_fixup_test_FTR_macros[];
+	extern u8 ftr_fixup_test_FTR_macros_expected[];
+	unsigned long size = ftr_fixup_test_FTR_macros_expected -
+			     ftr_fixup_test_FTR_macros;
 
 	/* The fixups have already been done for us during boot */
-	check(memcmp(&ftr_fixup_test_FTR_macros,
-		     &ftr_fixup_test_FTR_macros_expected, size) == 0);
+	check(memcmp(ftr_fixup_test_FTR_macros,
+		     ftr_fixup_test_FTR_macros_expected, size) == 0);
 }
 
 static void test_fw_macros(void)
 {
 #ifdef CONFIG_PPC64
-	extern u8 ftr_fixup_test_FW_FTR_macros;
-	extern u8 ftr_fixup_test_FW_FTR_macros_expected;
-	unsigned long size = &ftr_fixup_test_FW_FTR_macros_expected -
-			     &ftr_fixup_test_FW_FTR_macros;
+	extern u8 ftr_fixup_test_FW_FTR_macros[];
+	extern u8 ftr_fixup_test_FW_FTR_macros_expected[];
+	unsigned long size = ftr_fixup_test_FW_FTR_macros_expected -
+			     ftr_fixup_test_FW_FTR_macros;
 
 	/* The fixups have already been done for us during boot */
-	check(memcmp(&ftr_fixup_test_FW_FTR_macros,
-		     &ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
+	check(memcmp(ftr_fixup_test_FW_FTR_macros,
+		     ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
 #endif
 }
 
 static void test_lwsync_macros(void)
 {
-	extern u8 lwsync_fixup_test;
-	extern u8 end_lwsync_fixup_test;
-	extern u8 lwsync_fixup_test_expected_LWSYNC;
-	extern u8 lwsync_fixup_test_expected_SYNC;
-	unsigned long size = &end_lwsync_fixup_test -
-			     &lwsync_fixup_test;
+	extern u8 lwsync_fixup_test[];
+	extern u8 end_lwsync_fixup_test[];
+	extern u8 lwsync_fixup_test_expected_LWSYNC[];
+	extern u8 lwsync_fixup_test_expected_SYNC[];
+	unsigned long size = end_lwsync_fixup_test -
+			     lwsync_fixup_test;
 
 	/* The fixups have already been done for us during boot */
 	if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) {
-		check(memcmp(&lwsync_fixup_test,
-			     &lwsync_fixup_test_expected_LWSYNC, size) == 0);
+		check(memcmp(lwsync_fixup_test,
+			     lwsync_fixup_test_expected_LWSYNC, size) == 0);
 	} else {
-		check(memcmp(&lwsync_fixup_test,
-			     &lwsync_fixup_test_expected_SYNC, size) == 0);
+		check(memcmp(lwsync_fixup_test,
+			     lwsync_fixup_test_expected_SYNC, size) == 0);
 	}
 }
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 7/7] include/linux/string.h: add the option of fortified string.h functions
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
                   ` (5 preceding siblings ...)
  2017-06-19 20:26 ` [PATCH 6/7] powerpc: Make feature-fixup tests fortify-safe Kees Cook
@ 2017-06-19 20:26 ` Kees Cook
  2017-09-10 10:55   ` Geert Uytterhoeven
  2017-06-19 21:50 ` [PATCH 0/7] CONFIG_FORTIFY_SOURCE Andrew Morton
  7 siblings, 1 reply; 13+ messages in thread
From: Kees Cook @ 2017-06-19 20:26 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Kees Cook, Daniel Micay, Mark Rutland, Daniel Axtens,
	Rasmus Villemoes, Andy Shevchenko, Chris Metcalf,
	Thomas Gleixner, H. Peter Anvin, Ingo Molnar, linux-kernel,
	Stephen Rothwell

From: Daniel Micay <danielmicay@gmail.com>

This adds support for compiling with a rough equivalent to the glibc
_FORTIFY_SOURCE=1 feature, providing compile-time and runtime buffer
overflow checks for string.h functions when the compiler determines the
size of the source or destination buffer at compile-time.  Unlike glibc,
it covers buffer reads in addition to writes.

GNU C __builtin_*_chk intrinsics are avoided because they would force a
much more complex implementation.  They aren't designed to detect read
overflows and offer no real benefit when using an implementation based on
inline checks.  Inline checks don't add up to much code size and allow
full use of the regular string intrinsics while avoiding the need for a
bunch of _chk functions and per-arch assembly to avoid wrapper overhead.

This detects various overflows at compile-time in various drivers and some
non-x86 core kernel code.  There will likely be issues caught in regular
use at runtime too.

Future improvements left out of initial implementation for simplicity,
as it's all quite optional and can be done incrementally:

* Some of the fortified string functions (strncpy, strcat), don't yet
  place a limit on reads from the source based on __builtin_object_size of
  the source buffer.

* Extending coverage to more string functions like strlcat.

* It should be possible to optionally use __builtin_object_size(x, 1) for
  some functions (C strings) to detect intra-object overflows (like
  glibc's _FORTIFY_SOURCE=2), but for now this takes the conservative
  approach to avoid likely compatibility issues.

* The compile-time checks should be made available via a separate config
  option which can be enabled by default (or always enabled) once enough
  time has passed to get the issues it catches fixed.

Kees said:

: This is great to have.  While it was out-of-tree code, it would have
: blocked at least CVE-2016-3858 from being exploitable (improper size
: argument to strlcpy()).  I've sent a number of fixes for
: out-of-bounds-reads that this detected upstream already.

Link: http://lkml.kernel.org/r/20170526095404.20439-1-danielmicay@gmail.com
Signed-off-by: Daniel Micay <danielmicay@gmail.com>
Acked-by: Kees Cook <keescook@chromium.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Daniel Axtens <dja@axtens.net>
Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Chris Metcalf <cmetcalf@ezchip.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
[kees: move from -mm, add ARCH_HAS_FORTIFY_SOURCE, tweak Kconfig help]
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/Kconfig                     |   6 ++
 arch/arm64/Kconfig               |   1 +
 arch/arm64/include/asm/string.h  |   5 +
 arch/powerpc/Kconfig             |   1 +
 arch/x86/Kconfig                 |   1 +
 arch/x86/boot/compressed/misc.c  |   5 +
 arch/x86/include/asm/string_32.h |   9 ++
 arch/x86/include/asm/string_64.h |   7 ++
 include/linux/string.h           | 200 +++++++++++++++++++++++++++++++++++++++
 lib/string.c                     |   6 ++
 security/Kconfig                 |   7 ++
 11 files changed, 248 insertions(+)

diff --git a/arch/Kconfig b/arch/Kconfig
index 6c00e5b00f8b..f8e433b5a685 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -226,6 +226,12 @@ config GENERIC_SMP_IDLE_THREAD
 config GENERIC_IDLE_POLL_SETUP
        bool
 
+config ARCH_HAS_FORTIFY_SOURCE
+	bool
+	help
+	  An architecture should select this when it can successfully
+	  build and run with CONFIG_FORTIFY_SOURCE.
+
 # Select if arch has all set_memory_ro/rw/x/nx() functions in asm/cacheflush.h
 config ARCH_HAS_SET_MEMORY
 	bool
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3dcd7ec69bca..db72cc1f3092 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -11,6 +11,7 @@ config ARM64
 	select ARCH_HAS_DEVMEM_IS_ALLOWED
 	select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
 	select ARCH_HAS_ELF_RANDOMIZE
+	select ARCH_HAS_FORTIFY_SOURCE
 	select ARCH_HAS_GCOV_PROFILE_ALL
 	select ARCH_HAS_GIGANTIC_PAGE
 	select ARCH_HAS_KCOV
diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h
index 2eb714c4639f..d0aa42907569 100644
--- a/arch/arm64/include/asm/string.h
+++ b/arch/arm64/include/asm/string.h
@@ -63,6 +63,11 @@ extern int memcmp(const void *, const void *, size_t);
 #define memcpy(dst, src, len) __memcpy(dst, src, len)
 #define memmove(dst, src, len) __memmove(dst, src, len)
 #define memset(s, c, n) __memset(s, c, n)
+
+#ifndef __NO_FORTIFY
+#define __NO_FORTIFY /* FORTIFY_SOURCE uses __builtin_memcpy, etc. */
+#endif
+
 #endif
 
 #endif
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index f7c8f9972f61..7ef80b26ee6a 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -133,6 +133,7 @@ config PPC
 	select ARCH_HAS_DEVMEM_IS_ALLOWED
 	select ARCH_HAS_DMA_SET_COHERENT_MASK
 	select ARCH_HAS_ELF_RANDOMIZE
+	select ARCH_HAS_FORTIFY_SOURCE
 	select ARCH_HAS_GCOV_PROFILE_ALL
 	select ARCH_HAS_SCALED_CPUTIME		if VIRT_CPU_ACCOUNTING_NATIVE
 	select ARCH_HAS_SG_CHAIN
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index cd18994a9555..a84ca4694a75 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -50,6 +50,7 @@ config X86
 	select ARCH_HAS_DEVMEM_IS_ALLOWED
 	select ARCH_HAS_ELF_RANDOMIZE
 	select ARCH_HAS_FAST_MULTIPLIER
+	select ARCH_HAS_FORTIFY_SOURCE
 	select ARCH_HAS_GCOV_PROFILE_ALL
 	select ARCH_HAS_KCOV			if X86_64
 	select ARCH_HAS_MMIO_FLUSH
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index b3c5a5f030ce..43691238a21d 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -409,3 +409,8 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
 	debug_putstr("done.\nBooting the kernel.\n");
 	return output;
 }
+
+void fortify_panic(const char *name)
+{
+	error("detected buffer overflow");
+}
diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h
index 3d3e8353ee5c..e9ee84873de5 100644
--- a/arch/x86/include/asm/string_32.h
+++ b/arch/x86/include/asm/string_32.h
@@ -142,7 +142,9 @@ static __always_inline void *__constant_memcpy(void *to, const void *from,
 }
 
 #define __HAVE_ARCH_MEMCPY
+extern void *memcpy(void *, const void *, size_t);
 
+#ifndef CONFIG_FORTIFY_SOURCE
 #ifdef CONFIG_X86_USE_3DNOW
 
 #include <asm/mmx.h>
@@ -195,11 +197,15 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len)
 #endif
 
 #endif
+#endif /* !CONFIG_FORTIFY_SOURCE */
 
 #define __HAVE_ARCH_MEMMOVE
 void *memmove(void *dest, const void *src, size_t n);
 
+extern int memcmp(const void *, const void *, size_t);
+#ifndef CONFIG_FORTIFY_SOURCE
 #define memcmp __builtin_memcmp
+#endif
 
 #define __HAVE_ARCH_MEMCHR
 extern void *memchr(const void *cs, int c, size_t count);
@@ -321,6 +327,8 @@ void *__constant_c_and_count_memset(void *s, unsigned long pattern,
 	 : __memset_generic((s), (c), (count)))
 
 #define __HAVE_ARCH_MEMSET
+extern void *memset(void *, int, size_t);
+#ifndef CONFIG_FORTIFY_SOURCE
 #if (__GNUC__ >= 4)
 #define memset(s, c, count) __builtin_memset(s, c, count)
 #else
@@ -330,6 +338,7 @@ void *__constant_c_and_count_memset(void *s, unsigned long pattern,
 				 (count))				\
 	 : __memset((s), (c), (count)))
 #endif
+#endif /* !CONFIG_FORTIFY_SOURCE */
 
 /*
  * find the first occurrence of byte 'c', or 1 past the area if none
diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index 733bae07fb29..309fe644569f 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -31,6 +31,7 @@ static __always_inline void *__inline_memcpy(void *to, const void *from, size_t
 extern void *memcpy(void *to, const void *from, size_t len);
 extern void *__memcpy(void *to, const void *from, size_t len);
 
+#ifndef CONFIG_FORTIFY_SOURCE
 #ifndef CONFIG_KMEMCHECK
 #if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4
 #define memcpy(dst, src, len)					\
@@ -51,6 +52,7 @@ extern void *__memcpy(void *to, const void *from, size_t len);
  */
 #define memcpy(dst, src, len) __inline_memcpy((dst), (src), (len))
 #endif
+#endif /* !CONFIG_FORTIFY_SOURCE */
 
 #define __HAVE_ARCH_MEMSET
 void *memset(void *s, int c, size_t n);
@@ -77,6 +79,11 @@ int strcmp(const char *cs, const char *ct);
 #define memcpy(dst, src, len) __memcpy(dst, src, len)
 #define memmove(dst, src, len) __memmove(dst, src, len)
 #define memset(s, c, n) __memset(s, c, n)
+
+#ifndef __NO_FORTIFY
+#define __NO_FORTIFY /* FORTIFY_SOURCE uses __builtin_memcpy, etc. */
+#endif
+
 #endif
 
 #define __HAVE_ARCH_MEMCPY_MCSAFE 1
diff --git a/include/linux/string.h b/include/linux/string.h
index 537918f8a98e..2215431b7a04 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -187,4 +187,204 @@ static inline const char *kbasename(const char *path)
 	return tail ? tail + 1 : path;
 }
 
+#define __FORTIFY_INLINE extern __always_inline __attribute__((gnu_inline))
+#define __RENAME(x) __asm__(#x)
+
+void fortify_panic(const char *name) __noreturn __cold;
+void __read_overflow(void) __compiletime_error("detected read beyond size of object passed as 1st parameter");
+void __read_overflow2(void) __compiletime_error("detected read beyond size of object passed as 2nd parameter");
+void __write_overflow(void) __compiletime_error("detected write beyond size of object passed as 1st parameter");
+
+#if !defined(__NO_FORTIFY) && defined(__OPTIMIZE__) && defined(CONFIG_FORTIFY_SOURCE)
+__FORTIFY_INLINE char *strcpy(char *p, const char *q)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	size_t q_size = __builtin_object_size(q, 0);
+	if (p_size == (size_t)-1 && q_size == (size_t)-1)
+		return __builtin_strcpy(p, q);
+	if (strscpy(p, q, p_size < q_size ? p_size : q_size) < 0)
+		fortify_panic(__func__);
+	return p;
+}
+
+__FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	if (__builtin_constant_p(size) && p_size < size)
+		__write_overflow();
+	if (p_size < size)
+		fortify_panic(__func__);
+	return __builtin_strncpy(p, q, size);
+}
+
+__FORTIFY_INLINE char *strcat(char *p, const char *q)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	if (p_size == (size_t)-1)
+		return __builtin_strcat(p, q);
+	if (strlcat(p, q, p_size) >= p_size)
+		fortify_panic(__func__);
+	return p;
+}
+
+__FORTIFY_INLINE __kernel_size_t strlen(const char *p)
+{
+	__kernel_size_t ret;
+	size_t p_size = __builtin_object_size(p, 0);
+	if (p_size == (size_t)-1)
+		return __builtin_strlen(p);
+	ret = strnlen(p, p_size);
+	if (p_size <= ret)
+		fortify_panic(__func__);
+	return ret;
+}
+
+extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen);
+__FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	__kernel_size_t ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size);
+	if (p_size <= ret && maxlen != ret)
+		fortify_panic(__func__);
+	return ret;
+}
+
+/* defined after fortified strlen to reuse it */
+extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy);
+__FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size)
+{
+	size_t ret;
+	size_t p_size = __builtin_object_size(p, 0);
+	size_t q_size = __builtin_object_size(q, 0);
+	if (p_size == (size_t)-1 && q_size == (size_t)-1)
+		return __real_strlcpy(p, q, size);
+	ret = strlen(q);
+	if (size) {
+		size_t len = (ret >= size) ? size - 1 : ret;
+		if (__builtin_constant_p(len) && len >= p_size)
+			__write_overflow();
+		if (len >= p_size)
+			fortify_panic(__func__);
+		__builtin_memcpy(p, q, len);
+		p[len] = '\0';
+	}
+	return ret;
+}
+
+/* defined after fortified strlen and strnlen to reuse them */
+__FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count)
+{
+	size_t p_len, copy_len;
+	size_t p_size = __builtin_object_size(p, 0);
+	size_t q_size = __builtin_object_size(q, 0);
+	if (p_size == (size_t)-1 && q_size == (size_t)-1)
+		return __builtin_strncat(p, q, count);
+	p_len = strlen(p);
+	copy_len = strnlen(q, count);
+	if (p_size < p_len + copy_len + 1)
+		fortify_panic(__func__);
+	__builtin_memcpy(p + p_len, q, copy_len);
+	p[p_len + copy_len] = '\0';
+	return p;
+}
+
+__FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	if (__builtin_constant_p(size) && p_size < size)
+		__write_overflow();
+	if (p_size < size)
+		fortify_panic(__func__);
+	return __builtin_memset(p, c, size);
+}
+
+__FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	size_t q_size = __builtin_object_size(q, 0);
+	if (__builtin_constant_p(size)) {
+		if (p_size < size)
+			__write_overflow();
+		if (q_size < size)
+			__read_overflow2();
+	}
+	if (p_size < size || q_size < size)
+		fortify_panic(__func__);
+	return __builtin_memcpy(p, q, size);
+}
+
+__FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	size_t q_size = __builtin_object_size(q, 0);
+	if (__builtin_constant_p(size)) {
+		if (p_size < size)
+			__write_overflow();
+		if (q_size < size)
+			__read_overflow2();
+	}
+	if (p_size < size || q_size < size)
+		fortify_panic(__func__);
+	return __builtin_memmove(p, q, size);
+}
+
+extern void *__real_memscan(void *, int, __kernel_size_t) __RENAME(memscan);
+__FORTIFY_INLINE void *memscan(void *p, int c, __kernel_size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	if (__builtin_constant_p(size) && p_size < size)
+		__read_overflow();
+	if (p_size < size)
+		fortify_panic(__func__);
+	return __real_memscan(p, c, size);
+}
+
+__FORTIFY_INLINE int memcmp(const void *p, const void *q, __kernel_size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	size_t q_size = __builtin_object_size(q, 0);
+	if (__builtin_constant_p(size)) {
+		if (p_size < size)
+			__read_overflow();
+		if (q_size < size)
+			__read_overflow2();
+	}
+	if (p_size < size || q_size < size)
+		fortify_panic(__func__);
+	return __builtin_memcmp(p, q, size);
+}
+
+__FORTIFY_INLINE void *memchr(const void *p, int c, __kernel_size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	if (__builtin_constant_p(size) && p_size < size)
+		__read_overflow();
+	if (p_size < size)
+		fortify_panic(__func__);
+	return __builtin_memchr(p, c, size);
+}
+
+void *__real_memchr_inv(const void *s, int c, size_t n) __RENAME(memchr_inv);
+__FORTIFY_INLINE void *memchr_inv(const void *p, int c, size_t size)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	if (__builtin_constant_p(size) && p_size < size)
+		__read_overflow();
+	if (p_size < size)
+		fortify_panic(__func__);
+	return __real_memchr_inv(p, c, size);
+}
+
+extern void *__real_kmemdup(const void *src, size_t len, gfp_t gfp) __RENAME(kmemdup);
+__FORTIFY_INLINE void *kmemdup(const void *p, size_t size, gfp_t gfp)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	if (__builtin_constant_p(size) && p_size < size)
+		__read_overflow();
+	if (p_size < size)
+		fortify_panic(__func__);
+	return __real_kmemdup(p, size, gfp);
+}
+#endif
+
 #endif /* _LINUX_STRING_H_ */
diff --git a/lib/string.c b/lib/string.c
index 1c1fc9187b05..a6ee1955a701 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -978,3 +978,9 @@ char *strreplace(char *s, char old, char new)
 	return s;
 }
 EXPORT_SYMBOL(strreplace);
+
+void fortify_panic(const char *name)
+{
+	panic("detected buffer overflow in %s", name);
+}
+EXPORT_SYMBOL(fortify_panic);
diff --git a/security/Kconfig b/security/Kconfig
index 93027fdf47d1..1a7dc352330f 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -154,6 +154,13 @@ config HARDENED_USERCOPY_PAGESPAN
 	  been removed. This config is intended to be used only while
 	  trying to find such users.
 
+config FORTIFY_SOURCE
+	bool "Harden common str/mem functions against buffer overflows"
+	depends on ARCH_HAS_FORTIFY_SOURCE
+	help
+	  Detect overflows of buffers in common string and memory functions
+	  where the compiler can determine and validate the buffer sizes.
+
 config STATIC_USERMODEHELPER
 	bool "Force all usermode helper calls through a single binary"
 	help
-- 
2.7.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/7] CONFIG_FORTIFY_SOURCE
  2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
                   ` (6 preceding siblings ...)
  2017-06-19 20:26 ` [PATCH 7/7] include/linux/string.h: add the option of fortified string.h functions Kees Cook
@ 2017-06-19 21:50 ` Andrew Morton
  2017-06-19 22:12   ` Kees Cook
  7 siblings, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2017-06-19 21:50 UTC (permalink / raw)
  To: Kees Cook; +Cc: linux-kernel, Stephen Rothwell, Daniel Micay

On Mon, 19 Jun 2017 13:26:20 -0700 Kees Cook <keescook@chromium.org> wrote:

> Here are the outstanding fixes for CONFIG_FORTIFY_SOURCE, along with Daniel's
> v5 patch and a tweak from me to add CONFIG_ARCH_HAS_FORTIFY_SOURCE to avoid
> failing the build on architectures that have not hunted down all the needed
> fixes yet.
> 
> This was in my for-next/kspp tree, but since it depends on fixes in other
> trees, the preference is for these to all get carried in -mm instead of
> in KSPP.

All the patches you sent are already in -next (from the kspp tree?) so
I can't use them.

> The extra needed fixes in -next are:
> 
>         scsi: csiostor: Avoid content leaks and casts
>         arm64, vdso: Define vdso_{start,end} as array
>         staging/rts5208: Fix read overflow in memcpy
>         libertas: Avoid reading past end of buffer
>         ray_cs: Avoid reading past end of buffer

These didn't get sent out?

If the kspp tree is already in -next then how about leaving things that
way, and send Linus a pull request for -rc1?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/7] CONFIG_FORTIFY_SOURCE
  2017-06-19 21:50 ` [PATCH 0/7] CONFIG_FORTIFY_SOURCE Andrew Morton
@ 2017-06-19 22:12   ` Kees Cook
  2017-06-19 22:31     ` Andrew Morton
  0 siblings, 1 reply; 13+ messages in thread
From: Kees Cook @ 2017-06-19 22:12 UTC (permalink / raw)
  To: Andrew Morton, Stephen Rothwell; +Cc: LKML, Daniel Micay

On Mon, Jun 19, 2017 at 2:50 PM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Mon, 19 Jun 2017 13:26:20 -0700 Kees Cook <keescook@chromium.org> wrote:
>
>> Here are the outstanding fixes for CONFIG_FORTIFY_SOURCE, along with Daniel's
>> v5 patch and a tweak from me to add CONFIG_ARCH_HAS_FORTIFY_SOURCE to avoid
>> failing the build on architectures that have not hunted down all the needed
>> fixes yet.
>>
>> This was in my for-next/kspp tree, but since it depends on fixes in other
>> trees, the preference is for these to all get carried in -mm instead of
>> in KSPP.
>
> All the patches you sent are already in -next (from the kspp tree?) so
> I can't use them.

Err... that's what you asked me to send? And I had removed them from
kspp so you could carry them.

>> The extra needed fixes in -next are:
>>
>>         scsi: csiostor: Avoid content leaks and casts
>>         arm64, vdso: Define vdso_{start,end} as array
>>         staging/rts5208: Fix read overflow in memcpy
>>         libertas: Avoid reading past end of buffer
>>         ray_cs: Avoid reading past end of buffer
>
> These didn't get sent out?

These are all already in -next from other non-kspp trees. I was just
trying to be complete about showing where all the needed fixes were.

> If the kspp tree is already in -next then how about leaving things that
> way, and send Linus a pull request for -rc1?

*sob* I'm happy to do that. I just want you and sfr to agree. :P If I
carry them in my kspp tree, it'll depend on -next (which I'm fine
with, but sfr does not like).

I can add it all back to kspp, just let me what you both can agree on. :P

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/7] CONFIG_FORTIFY_SOURCE
  2017-06-19 22:12   ` Kees Cook
@ 2017-06-19 22:31     ` Andrew Morton
  0 siblings, 0 replies; 13+ messages in thread
From: Andrew Morton @ 2017-06-19 22:31 UTC (permalink / raw)
  To: Kees Cook; +Cc: Stephen Rothwell, LKML, Daniel Micay

On Mon, 19 Jun 2017 15:12:22 -0700 Kees Cook <keescook@chromium.org> wrote:

> >> This was in my for-next/kspp tree, but since it depends on fixes in other
> >> trees, the preference is for these to all get carried in -mm instead of
> >> in KSPP.
> >
> > All the patches you sent are already in -next (from the kspp tree?) so
> > I can't use them.
> 
> Err... that's what you asked me to send? And I had removed them from
> kspp so you could carry them.

Oh, OK, I'll take a look later in the week after -next has caught up.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/7] include/linux/string.h: add the option of fortified string.h functions
  2017-06-19 20:26 ` [PATCH 7/7] include/linux/string.h: add the option of fortified string.h functions Kees Cook
@ 2017-09-10 10:55   ` Geert Uytterhoeven
  2017-09-10 13:51     ` Arnd Bergmann
  0 siblings, 1 reply; 13+ messages in thread
From: Geert Uytterhoeven @ 2017-09-10 10:55 UTC (permalink / raw)
  To: Kees Cook, Daniel Micay
  Cc: Andrew Morton, Mark Rutland, Daniel Axtens, Rasmus Villemoes,
	Andy Shevchenko, Chris Metcalf, Thomas Gleixner, H. Peter Anvin,
	Ingo Molnar, linux-kernel, Stephen Rothwell, Arnd Bergmann

Hi Kees, Daniel,

On Mon, Jun 19, 2017 at 10:26 PM, Kees Cook <keescook@chromium.org> wrote:
> From: Daniel Micay <danielmicay@gmail.com>
>
> This adds support for compiling with a rough equivalent to the glibc
> _FORTIFY_SOURCE=1 feature, providing compile-time and runtime buffer
> overflow checks for string.h functions when the compiler determines the
> size of the source or destination buffer at compile-time.  Unlike glibc,
> it covers buffer reads in addition to writes.

[...]

> Link: http://lkml.kernel.org/r/20170526095404.20439-1-danielmicay@gmail.com
> Signed-off-by: Daniel Micay <danielmicay@gmail.com>
> Acked-by: Kees Cook <keescook@chromium.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Daniel Axtens <dja@axtens.net>
> Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Chris Metcalf <cmetcalf@ezchip.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> [kees: move from -mm, add ARCH_HAS_FORTIFY_SOURCE, tweak Kconfig help]
> Signed-off-by: Kees Cook <keescook@chromium.org>

This is now commit 6974f0c4555e285a upstream.

> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -187,4 +187,204 @@ static inline const char *kbasename(const char *path)
>         return tail ? tail + 1 : path;
>  }
>
> +#define __FORTIFY_INLINE extern __always_inline __attribute__((gnu_inline))

With gcc-4.1.2, I now get zillions of:

    include/linux/string.h:439: warning: ‘gnu_inline’ attribute
directive ignored

This attribute seems to be supported as of gcc 4.2?

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/7] include/linux/string.h: add the option of fortified string.h functions
  2017-09-10 10:55   ` Geert Uytterhoeven
@ 2017-09-10 13:51     ` Arnd Bergmann
  0 siblings, 0 replies; 13+ messages in thread
From: Arnd Bergmann @ 2017-09-10 13:51 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Kees Cook, Daniel Micay, Andrew Morton, Mark Rutland,
	Daniel Axtens, Rasmus Villemoes, Andy Shevchenko, Chris Metcalf,
	Thomas Gleixner, H. Peter Anvin, Ingo Molnar, linux-kernel,
	Stephen Rothwell

On Sun, Sep 10, 2017 at 12:55 PM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
55e285a upstream.
>
>> --- a/include/linux/string.h
>> +++ b/include/linux/string.h
>> @@ -187,4 +187,204 @@ static inline const char *kbasename(const char *path)
>>         return tail ? tail + 1 : path;
>>  }
>>
>> +#define __FORTIFY_INLINE extern __always_inline __attribute__((gnu_inline))
>
> With gcc-4.1.2, I now get zillions of:
>
>     include/linux/string.h:439: warning: ‘gnu_inline’ attribute
> directive ignored
>
> This attribute seems to be supported as of gcc 4.2?
>

I think in older compilers this was the default, so we could add a macro in
compiler.h that makes becomes an empty string there. If we do that,
we should also address these new warning on gcc-4.3:

arch/x86/include/asm/string_32.h:30: warning: 'strlen' declared inline
after being called
arch/x86/include/asm/string_32.h:252: warning: 'strnlen' declared
inline after being called
arch/x86/include/asm/string_32.h:252: warning: previous declaration of
'strnlen' was here
arch/x86/include/asm/string_32.h:145: warning: 'memcpy' declared
inline after being called
include/linux/string.h:81: warning: 'strlen' declared inline after being called
include/linux/string.h:81: warning: previous declaration of 'strlen' was here
include/linux/string.h:84: warning: 'strnlen' declared inline after being called
include/linux/string.h:84: warning: previous declaration of 'strnlen' was here

      Arnd

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, back to index

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-19 20:26 [PATCH 0/7] CONFIG_FORTIFY_SOURCE Kees Cook
2017-06-19 20:26 ` [PATCH 1/7] efi: Avoid fortify checks in EFI stub Kees Cook
2017-06-19 20:26 ` [PATCH 2/7] x86/power/64: Use char arrays for asm function names Kees Cook
2017-06-19 20:26 ` [PATCH 3/7] kexec_file: Adjust declaration of kexec_purgatory Kees Cook
2017-06-19 20:26 ` [PATCH 4/7] IB/rxe: Do not copy extra stack memory to skb Kees Cook
2017-06-19 20:26 ` [PATCH 5/7] powerpc: Don't fortify prom_init Kees Cook
2017-06-19 20:26 ` [PATCH 6/7] powerpc: Make feature-fixup tests fortify-safe Kees Cook
2017-06-19 20:26 ` [PATCH 7/7] include/linux/string.h: add the option of fortified string.h functions Kees Cook
2017-09-10 10:55   ` Geert Uytterhoeven
2017-09-10 13:51     ` Arnd Bergmann
2017-06-19 21:50 ` [PATCH 0/7] CONFIG_FORTIFY_SOURCE Andrew Morton
2017-06-19 22:12   ` Kees Cook
2017-06-19 22:31     ` Andrew Morton

LKML Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/lkml/0 lkml/git/0.git
	git clone --mirror https://lore.kernel.org/lkml/1 lkml/git/1.git
	git clone --mirror https://lore.kernel.org/lkml/2 lkml/git/2.git
	git clone --mirror https://lore.kernel.org/lkml/3 lkml/git/3.git
	git clone --mirror https://lore.kernel.org/lkml/4 lkml/git/4.git
	git clone --mirror https://lore.kernel.org/lkml/5 lkml/git/5.git
	git clone --mirror https://lore.kernel.org/lkml/6 lkml/git/6.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 lkml lkml/ https://lore.kernel.org/lkml \
		linux-kernel@vger.kernel.org linux-kernel@archiver.kernel.org
	public-inbox-index lkml


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-kernel


AGPL code for this site: git clone https://public-inbox.org/ public-inbox