[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20201016123808.10007-1-laniel_francis@privacyrequired.com>
Date: Fri, 16 Oct 2020 14:38:09 +0200
From: laniel_francis@...vacyrequired.com
To: linux-hardening@...r.kernel.org
Cc: Francis Laniel <laniel_francis@...vacyrequired.com>
Subject: [RFC][PATCH v2] Fortify string function strscpy.
From: Francis Laniel <laniel_francis@...vacyrequired.com>
Thanks to kees advices (see:
https://github.com/KSPP/linux/issues/96#issuecomment-709620337) I wrote a LKDTM
test for the fortified version of strscpy I added in the v1 of this patch.
The test panics due to write overflow.
Signed-off-by: Francis Laniel <laniel_francis@...vacyrequired.com>
---
drivers/misc/lkdtm/Makefile | 1 +
drivers/misc/lkdtm/core.c | 1 +
drivers/misc/lkdtm/fortify.c | 37 +++++++++++++++++++++++++++++
drivers/misc/lkdtm/lkdtm.h | 17 ++++++++------
include/linux/string.h | 45 ++++++++++++++++++++++++++++++++++++
5 files changed, 94 insertions(+), 7 deletions(-)
create mode 100644 drivers/misc/lkdtm/fortify.c
diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile
index c70b3822013f..d898f7b22045 100644
--- a/drivers/misc/lkdtm/Makefile
+++ b/drivers/misc/lkdtm/Makefile
@@ -10,6 +10,7 @@ lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o
lkdtm-$(CONFIG_LKDTM) += usercopy.o
lkdtm-$(CONFIG_LKDTM) += stackleak.o
lkdtm-$(CONFIG_LKDTM) += cfi.o
+lkdtm-$(CONFIG_LKDTM) += fortify.o
KASAN_SANITIZE_stackleak.o := n
KCOV_INSTRUMENT_rodata.o := n
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
index a5e344df9166..979f9e3feefd 100644
--- a/drivers/misc/lkdtm/core.c
+++ b/drivers/misc/lkdtm/core.c
@@ -178,6 +178,7 @@ static const struct crashtype crashtypes[] = {
#ifdef CONFIG_X86_32
CRASHTYPE(DOUBLE_FAULT),
#endif
+ CRASHTYPE(FORTIFIED_STRSCPY),
};
diff --git a/drivers/misc/lkdtm/fortify.c b/drivers/misc/lkdtm/fortify.c
new file mode 100644
index 000000000000..0397d2def66d
--- /dev/null
+++ b/drivers/misc/lkdtm/fortify.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Francis Laniel <laniel_francis@...vacyrequired.com>
+ *
+ * Add tests related to fortified functions in this file.
+ */
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "lkdtm.h"
+
+
+/*
+ * Calls fortified strscpy to generate a panic because there is a write
+ * overflow (i.e. src length is greater than dst length).
+ */
+void lkdtm_FORTIFIED_STRSCPY(void)
+{
+#if !defined(__NO_FORTIFY) && defined(__OPTIMIZE__) && defined(CONFIG_FORTIFY_SOURCE)
+ char *src;
+ char dst[3];
+
+ src = kmalloc(7, GFP_KERNEL);
+ src[0] = 'f';
+ src[1] = 'o';
+ src[2] = 'o';
+ src[3] = 'b';
+ src[4] = 'a';
+ src[5] = 'r';
+ src[6] = '\0';
+
+ strscpy(dst, src, 1000);
+
+ kfree(dst);
+
+ pr_info("Fail: No overflow in above strscpy call!\n");
+#endif
+}
diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index 8878538b2c13..8e5e90eb0e00 100644
--- a/drivers/misc/lkdtm/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
@@ -6,7 +6,7 @@
#include <linux/kernel.h>
-/* lkdtm_bugs.c */
+/* bugs.c */
void __init lkdtm_bugs_init(int *recur_param);
void lkdtm_PANIC(void);
void lkdtm_BUG(void);
@@ -34,7 +34,7 @@ void lkdtm_UNSET_SMEP(void);
void lkdtm_DOUBLE_FAULT(void);
void lkdtm_CORRUPT_PAC(void);
-/* lkdtm_heap.c */
+/* heap.c */
void __init lkdtm_heap_init(void);
void __exit lkdtm_heap_exit(void);
void lkdtm_OVERWRITE_ALLOCATION(void);
@@ -46,7 +46,7 @@ void lkdtm_SLAB_FREE_DOUBLE(void);
void lkdtm_SLAB_FREE_CROSS(void);
void lkdtm_SLAB_FREE_PAGE(void);
-/* lkdtm_perms.c */
+/* perms.c */
void __init lkdtm_perms_init(void);
void lkdtm_WRITE_RO(void);
void lkdtm_WRITE_RO_AFTER_INIT(void);
@@ -61,7 +61,7 @@ void lkdtm_EXEC_NULL(void);
void lkdtm_ACCESS_USERSPACE(void);
void lkdtm_ACCESS_NULL(void);
-/* lkdtm_refcount.c */
+/* refcount.c */
void lkdtm_REFCOUNT_INC_OVERFLOW(void);
void lkdtm_REFCOUNT_ADD_OVERFLOW(void);
void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void);
@@ -82,10 +82,10 @@ void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void);
void lkdtm_REFCOUNT_TIMING(void);
void lkdtm_ATOMIC_TIMING(void);
-/* lkdtm_rodata.c */
+/* rodata.c */
void lkdtm_rodata_do_nothing(void);
-/* lkdtm_usercopy.c */
+/* usercopy.c */
void __init lkdtm_usercopy_init(void);
void __exit lkdtm_usercopy_exit(void);
void lkdtm_USERCOPY_HEAP_SIZE_TO(void);
@@ -98,10 +98,13 @@ void lkdtm_USERCOPY_STACK_BEYOND(void);
void lkdtm_USERCOPY_KERNEL(void);
void lkdtm_USERCOPY_KERNEL_DS(void);
-/* lkdtm_stackleak.c */
+/* stackleak.c */
void lkdtm_STACKLEAK_ERASING(void);
/* cfi.c */
void lkdtm_CFI_FORWARD_PROTO(void);
+/* fortify.c */
+void lkdtm_FORTIFIED_STRSCPY(void);
+
#endif
diff --git a/include/linux/string.h b/include/linux/string.h
index b1f3894a0a3e..b661863619e0 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -6,6 +6,8 @@
#include <linux/compiler.h> /* for inline */
#include <linux/types.h> /* for size_t */
#include <linux/stddef.h> /* for NULL */
+#include <linux/bug.h> /* for WARN_ON_ONCE */
+#include <linux/errno.h> /* for E2BIG */
#include <stdarg.h>
#include <uapi/linux/string.h>
@@ -357,6 +359,49 @@ __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size)
return ret;
}
+/* defined after fortified strlen to reuse it */
+extern ssize_t __real_strscpy(char *, const char *, size_t) __RENAME(strscpy);
+__FORTIFY_INLINE ssize_t strscpy(char *p, const char *q, size_t count)
+{
+ size_t len;
+ size_t p_size = __builtin_object_size(p, 0);
+ size_t q_size = __builtin_object_size(q, 0);
+ /*
+ * If p_size and q_size cannot be known at compile time we just had to
+ * trust this function caller.
+ */
+ if (p_size == (size_t)-1 && q_size == (size_t)-1)
+ return __real_strscpy(p, q, count);
+ len = strlen(q);
+ if (count) {
+ /* If count is bigger than INT_MAX, strscpy returns -E2BIG. */
+ if (WARN_ON_ONCE(count > INT_MAX))
+ return -E2BIG;
+ /*
+ * strscpy handles read overflows by stop reading q when '\0' is
+ * met.
+ * We stick to this behavior here.
+ */
+ len = (len >= count) ? count : len;
+ /*
+ * If len can be known at compile time and is greater than
+ * p_size, generate a compile time write overflow error.
+ */
+ if (__builtin_constant_p(len) && len > p_size)
+ __write_overflow();
+ /* Otherwise generate a runtime write overflow error. */
+ if (len > p_size)
+ fortify_panic(__func__);
+ /*
+ * Still use count as third argument to correctly compute max
+ * inside strscpy.
+ */
+ return __real_strscpy(p, q, count);
+ }
+ /* If count is 0, strscpy return -E2BIG. */
+ return -E2BIG;
+}
+
/* defined after fortified strlen and strnlen to reuse them */
__FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count)
{
--
2.20.1
Powered by blists - more mailing lists