Large diffs are not rendered by default.

@@ -0,0 +1,184 @@
From patchwork Mon Apr 23 12:54:57 2018
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: "igor.stoppa@gmail.com" <igor.stoppa@gmail.com>
X-Patchwork-Id: 10356979
Return-Path:
<kernel-hardening-return-13096-patchwork-kernel-hardening=patchwork.kernel.org@lists.openwall.com>
Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org
[172.30.200.125])
by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id
18EBF601D3 for <patchwork-kernel-hardening@patchwork.kernel.org>;
Mon, 23 Apr 2018 12:57:14 +0000 (UTC)
Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1])
by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 08A6428AB0
for <patchwork-kernel-hardening@patchwork.kernel.org>;
Mon, 23 Apr 2018 12:57:14 +0000 (UTC)
Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486)
id F107228ABE; Mon, 23 Apr 2018 12:57:13 +0000 (UTC)
X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on
pdx-wl-mail.web.codeaurora.org
X-Spam-Level:
X-Spam-Status: No, score=-5.3 required=2.0 tests=BAYES_00,DKIM_SIGNED,
DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, MAILING_LIST_MULTI,
RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1
Received: from mother.openwall.net (mother.openwall.net [195.42.179.200])
by mail.wl.linuxfoundation.org (Postfix) with SMTP id ED8C228AC6
for <patchwork-kernel-hardening@patchwork.kernel.org>;
Mon, 23 Apr 2018 12:57:12 +0000 (UTC)
Received: (qmail 24261 invoked by uid 550); 23 Apr 2018 12:56:07 -0000
Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm
Precedence: bulk
List-Post: <mailto:kernel-hardening@lists.openwall.com>
List-Help: <mailto:kernel-hardening-help@lists.openwall.com>
List-Unsubscribe: <mailto:kernel-hardening-unsubscribe@lists.openwall.com>
List-Subscribe: <mailto:kernel-hardening-subscribe@lists.openwall.com>
List-ID: <kernel-hardening.lists.openwall.com>
Delivered-To: mailing list kernel-hardening@lists.openwall.com
Received: (qmail 24136 invoked from network); 23 Apr 2018 12:56:04 -0000
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=20161025;
h=from:to:cc:subject:date:message-id:in-reply-to:references;
bh=U09LcbMGiUra9N5GfBrZ9Z2qeNHUrpNbiO9P5fSzuZo=;
b=Xq0nRWIzsNFVfRgaixwK4d1eP8JPeOGZar3QT2Iz76LE9wA2SfQNH/T6SRrJeVGs2D
L7WLLC/wM6UlMKdCt+MmlzClr3quHmDnsIL2zERqQc6FVGkVwImJY5AJZPW3Xzu2aRcS
PIyS06ne8RCfkNJyXm1/VDCwmvNjiXTZo+dMtwX4ngKECAJZEInYy7tXpaGns8LrfLuN
Yq1Zs5rzBYN90zeQ8nUtdc9ZjyIc3yIKkrF6BGBbGt/GJCg1zweFKUDcnUzIR6p10AGe
hNK3piYoAlOGBLBDiGJcf6MjxYJdeV7HwIgD6M9ldoZxrPFHMatYRBFJPP66FW05hYio
hKyg==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20161025;
h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to
:references;
bh=U09LcbMGiUra9N5GfBrZ9Z2qeNHUrpNbiO9P5fSzuZo=;
b=ldVooaUdWm4BaFmsvrCtzURIVyeNPNQ9AEF2mXMNplTqfdK4VFHL18+N1ODIXWSdR5
tEqYDuVqNO5BrIKqVR4dTmiG/rZo2n3FFCOD8jcojoXyeE+tkZw7S0vxR6I3Oa80ABIk
LDWflZQ+19T1Aql6GnBa3Yv5Y3k48lZ4OelguIJgHQExAFhY0dyDYZO+ly2GHfGKJIIZ
N1swpQD9sTmv3G/PbXVkc5IKcKHw9pqvuY65zmoQ9DGtU1Z5YlIOWAormEz10TEBr5HQ
GUvPpORE8320PpG7oafbPoFC/V0d2Z4+rbMIb7p446tPeQLEtz3GHvwkJr3YScJz8k97
FTeQ==
X-Gm-Message-State: ALQs6tCT4AXCcF+ooR7reR4pyY0eqj1d3iOTe6EgsPJUUQOk4tEdOCOz
8Jm+V2XVlzSQw5eNBs4IXqg=
X-Google-Smtp-Source:
AB8JxZpjg7PDkDI2ZW0SR+RASgkcPtvVpwZRUiri7WbP3zghif8OJQ+lWVd5SfHFymB09G1o1gijMg==
X-Received: by 2002:a6b:c882:: with SMTP id
y124-v6mr22567384iof.64.1524488152791;
Mon, 23 Apr 2018 05:55:52 -0700 (PDT)
From: Igor Stoppa <igor.stoppa@gmail.com>
X-Google-Original-From: Igor Stoppa <igor.stoppa@huawei.com>
To: willy@infradead.org, keescook@chromium.org, paul@paul-moore.com,
sds@tycho.nsa.gov, mhocko@kernel.org, corbet@lwn.net
Cc: labbott@redhat.com, linux-cc=david@fromorbit.com,
--cc=rppt@linux.vnet.ibm.com, --security-module@vger.kernel.org,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
kernel-hardening@lists.openwall.com, igor.stoppa@gmail.com,
Igor Stoppa <igor.stoppa@huawei.com>
Subject: [PATCH 8/9] Preliminary self test for pmalloc rare write
Date: Mon, 23 Apr 2018 16:54:57 +0400
Message-Id: <20180423125458.5338-9-igor.stoppa@huawei.com>
X-Mailer: git-send-email 2.14.1
In-Reply-To: <20180423125458.5338-1-igor.stoppa@huawei.com>
References: <20180423125458.5338-1-igor.stoppa@huawei.com>
X-Virus-Scanned: ClamAV using ClamSMTP

Try to alter locked but modifiable pools.
The test neds some cleanup and expansion.
It is provided primarily as reference.

Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com>
---
mm/test_pmalloc.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)

diff --git a/mm/test_pmalloc.c b/mm/test_pmalloc.c
index c8835207a400..e8e945e4a4a3 100644
--- a/mm/test_pmalloc.c
+++ b/mm/test_pmalloc.c
@@ -122,6 +122,80 @@ static void test_oovm(void)
pmalloc_destroy_pool(pool);
}

+#define REGION_SIZE (PAGE_SIZE / 4)
+#define REGION_NUMBERS 12
+static inline void fill_region(char *addr, char c)
+{
+ size_t i;
+
+ for (i = 0; i < REGION_SIZE - 1; i++)
+ addr[i] = c;
+ addr[i] = '\0';
+}
+
+static inline void init_regions(char *array)
+{
+ size_t i;
+
+ for (i = 0; i < REGION_NUMBERS; i++)
+ fill_region(array + REGION_SIZE * i, i + 'A');
+}
+
+static inline void show_regions(char *array)
+{
+ size_t i;
+
+ for (i = 0; i < REGION_NUMBERS; i++)
+ pr_info("%s", array + REGION_SIZE * i);
+}
+
+static inline void init_big_injection(char *big_injection)
+{
+ size_t i;
+
+ for (i = 0; i < PAGE_SIZE * 3; i++)
+ big_injection[i] = 'X';
+}
+
+/* Verify rewritable feature. */
+static int test_rare_write(void)
+{
+ struct pmalloc_pool *pool;
+ char *array;
+ char injection[] = "123456789";
+ unsigned short size = sizeof(injection);
+ char *big_injection;
+
+
+ pr_notice("Test pmalloc_rare_write()");
+ pool = pmalloc_create_pool(PMALLOC_RW);
+ array = pzalloc(pool, REGION_SIZE * REGION_NUMBERS);
+ init_regions(array);
+ pmalloc_protect_pool(pool);
+ pr_info("------------------------------------------------------");
+ pmalloc_rare_write(pool, array, injection, size);
+ pmalloc_rare_write(pool, array + REGION_SIZE, injection, size);
+ pmalloc_rare_write(pool,
+ array + 5 * REGION_SIZE / 2 - size / 2,
+ injection, size);
+ pmalloc_rare_write(pool, array + 3 * REGION_SIZE - size / 2,
+ injection, size);
+ show_regions(array);
+ pmalloc_destroy_pool(pool);
+ pr_info("------------------------------------------------------");
+ pool = pmalloc_create_pool(PMALLOC_RW);
+ array = pzalloc(pool, REGION_SIZE * REGION_NUMBERS);
+ init_regions(array);
+ pmalloc_protect_pool(pool);
+ big_injection = vmalloc(PAGE_SIZE * 3);
+ init_big_injection(big_injection);
+ pmalloc_rare_write(pool, array + REGION_SIZE / 2, big_injection,
+ PAGE_SIZE * 2);
+ show_regions(array);
+ pr_info("------------------------------------------------------");
+ return 0;
+}
+
/**
* test_pmalloc() -main entry point for running the test cases
*/
@@ -135,4 +209,5 @@ void test_pmalloc(void)
test_is_pmalloc_object())))
return;
test_oovm();
+ test_rare_write();
}

Large diffs are not rendered by default.

@@ -0,0 +1,63 @@
From 37e980100c3a4c3906f39f5bbeba89087f5c12c1 Mon Sep 17 00:00:00 2001
From: Alexander Popov <alex.popov@linux.com>
Date: Wed, 11 Jul 2018 23:36:35 +0300
Subject: [PATCH 1/6] gcc-plugins: Clean up the cgraph_create_edge* macros

Drop useless redefinitions of cgraph_create_edge* macros. Drop the unused
nest argument. Also support gcc-8, which doesn't have freq argument.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
Message-Id: <1531341400-12077-2-git-send-email-alex.popov@linux.com>
Signed-off-by: Steve Arnold <nerdboy@gentoo.org>
---
scripts/gcc-plugins/gcc-common.h | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index f46750053377..552d5efd7cb7 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -392,13 +392,6 @@ static inline struct cgraph_node *cgraph_alias_target(struct cgraph_node *n)
}
#endif

-#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION <= 4009
-#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \
- cgraph_create_edge((caller), (callee), (call_stmt), (count), (freq))
-#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \
- cgraph_create_edge_including_clones((caller), (callee), (old_call_stmt), (call_stmt), (count), (freq), (reason))
-#endif
-
#if BUILDING_GCC_VERSION <= 4008
#define ENTRY_BLOCK_PTR_FOR_FN(FN) ENTRY_BLOCK_PTR_FOR_FUNCTION(FN)
#define EXIT_BLOCK_PTR_FOR_FN(FN) EXIT_BLOCK_PTR_FOR_FUNCTION(FN)
@@ -723,10 +716,23 @@ static inline const char *get_decl_section_name(const_tree decl)
#define varpool_get_node(decl) varpool_node::get(decl)
#define dump_varpool_node(file, node) (node)->dump(file)

-#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \
+#if BUILDING_GCC_VERSION >= 8000
+#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \
+ (caller)->create_edge((callee), (call_stmt), (count))
+
+#define cgraph_create_edge_including_clones(caller, callee, \
+ old_call_stmt, call_stmt, count, freq, reason) \
+ (caller)->create_edge_including_clones((callee), \
+ (old_call_stmt), (call_stmt), (count), (reason))
+#else
+#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \
(caller)->create_edge((callee), (call_stmt), (count), (freq))
-#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \
- (caller)->create_edge_including_clones((callee), (old_call_stmt), (call_stmt), (count), (freq), (reason))
+
+#define cgraph_create_edge_including_clones(caller, callee, \
+ old_call_stmt, call_stmt, count, freq, reason) \
+ (caller)->create_edge_including_clones((callee), \
+ (old_call_stmt), (call_stmt), (count), (freq), (reason))
+#endif

typedef struct cgraph_node *cgraph_node_ptr;
typedef struct cgraph_edge *cgraph_edge_p;
--
2.16.1

@@ -0,0 +1,378 @@
From 60631b561a755c20590eef1ecc5d01e7bf66406e Mon Sep 17 00:00:00 2001
From: Alexander Popov <alex.popov@linux.com>
Date: Wed, 11 Jul 2018 23:36:36 +0300
Subject: [PATCH 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the
end of syscalls

The STACKLEAK feature erases the kernel stack before returning from
syscalls. That reduces the information which kernel stack leak bugs can
reveal and blocks some uninitialized stack variable attacks. Moreover,
STACKLEAK blocks kernel stack depth overflow caused by alloca(), aka
Stack Clash attack.

This commit introduces the code filling the used part of the kernel
stack with a poison value before returning to userspace. Full
STACKLEAK feature also contains the gcc plugin which comes in a
separate commit.

The STACKLEAK feature is ported from grsecurity/PaX. More information at:
https://grsecurity.net/
https://pax.grsecurity.net/

This code is modified from Brad Spengler/PaX Team's code in the last
public patch of grsecurity/PaX based on our understanding of the code.
Changes or omissions from the original code are ours and don't reflect
the original grsecurity/PaX code.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Message-Id: <1531341400-12077-3-git-send-email-alex.popov@linux.com>
Signed-off-by: Steve Arnold <nerdboy@gentoo.org>
---
Documentation/x86/x86_64/mm.txt | 2 ++
arch/Kconfig | 27 ++++++++++++++++++
arch/x86/Kconfig | 1 +
arch/x86/entry/calling.h | 14 +++++++++
arch/x86/entry/entry_32.S | 7 +++++
arch/x86/entry/entry_64.S | 3 ++
arch/x86/entry/entry_64_compat.S | 5 ++++
include/linux/sched.h | 4 +++
include/linux/stackleak.h | 23 +++++++++++++++
kernel/Makefile | 4 +++
kernel/fork.c | 3 ++
kernel/stackleak.c | 61 ++++++++++++++++++++++++++++++++++++++++
12 files changed, 154 insertions(+)
create mode 100644 include/linux/stackleak.h
create mode 100644 kernel/stackleak.c

diff --git a/Documentation/x86/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt
index 5432a96d31ff..600bc2afa27d 100644
--- a/Documentation/x86/x86_64/mm.txt
+++ b/Documentation/x86/x86_64/mm.txt
@@ -24,6 +24,7 @@ ffffffffa0000000 - fffffffffeffffff (1520 MB) module mapping space
[fixmap start] - ffffffffff5fffff kernel-internal fixmap range
ffffffffff600000 - ffffffffff600fff (=4 kB) legacy vsyscall ABI
ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole
+STACKLEAK_POISON value in this last hole: ffffffffffff4111

Virtual memory map with 5 level page tables:

@@ -50,6 +51,7 @@ ffffffffa0000000 - fffffffffeffffff (1520 MB) module mapping space
[fixmap start] - ffffffffff5fffff kernel-internal fixmap range
ffffffffff600000 - ffffffffff600fff (=4 kB) legacy vsyscall ABI
ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole
+STACKLEAK_POISON value in this last hole: ffffffffffff4111

Architecture defines a 64-bit virtual address. Implementations can support
less. Currently supported are 48- and 57-bit virtual addresses. Bits 63
diff --git a/arch/Kconfig b/arch/Kconfig
index 75dd23acf133..bc5186fcdf59 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -401,6 +401,13 @@ config SECCOMP_FILTER

See Documentation/prctl/seccomp_filter.txt for details.

+config HAVE_ARCH_STACKLEAK
+ bool
+ help
+ An architecture should select this if it has the code which
+ fills the used part of the kernel stack with the STACKLEAK_POISON
+ value before returning from system calls.
+
config HAVE_GCC_PLUGINS
bool
help
@@ -535,6 +542,26 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE
in structures. This reduces the performance hit of RANDSTRUCT
at the cost of weakened randomization.

+config GCC_PLUGIN_STACKLEAK
+ bool "Erase the kernel stack before returning from syscalls"
+ depends on GCC_PLUGINS
+ depends on HAVE_ARCH_STACKLEAK
+ help
+ This option makes the kernel erase the kernel stack before
+ returning from system calls. That reduces the information which
+ kernel stack leak bugs can reveal and blocks some uninitialized
+ stack variable attacks. This option also blocks kernel stack depth
+ overflow caused by alloca(), aka Stack Clash attack.
+
+ The tradeoff is the performance impact: on a single CPU system kernel
+ compilation sees a 1% slowdown, other systems and workloads may vary
+ and you are advised to test this feature on your expected workload
+ before deploying it.
+
+ This plugin was ported from grsecurity/PaX. More information at:
+ * https://grsecurity.net/
+ * https://pax.grsecurity.net/
+
config HAVE_CC_STACKPROTECTOR
bool
help
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index c07f492b871a..74b8f5c1d805 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -121,6 +121,7 @@ config X86
select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_THREAD_STRUCT_WHITELIST
+ select HAVE_ARCH_STACKLEAK
select HAVE_ARCH_TRACEHOOK
select HAVE_ARCH_TRANSPARENT_HUGEPAGE
select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64
diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h
index 352e70cd33e8..20d0885b00fb 100644
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -329,8 +329,22 @@ For 32-bit we have the following conventions - kernel is built with

#endif

+.macro STACKLEAK_ERASE_NOCLOBBER
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+ PUSH_AND_CLEAR_REGS
+ call stackleak_erase
+ POP_REGS
+#endif
+.endm
+
#endif /* CONFIG_X86_64 */

+.macro STACKLEAK_ERASE
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+ call stackleak_erase
+#endif
+.endm
+
/*
* This does 'call enter_from_user_mode' unless we can avoid it based on
* kernel config or using the static jump infrastructure.
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index bef8e2b202a8..71e0323e235a 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -46,6 +46,8 @@
#include <asm/frame.h>
#include <asm/nospec-branch.h>

+#include "calling.h"
+
.section .entry.text, "ax"

/*
@@ -298,6 +300,7 @@ ENTRY(ret_from_fork)
/* When we fork, we trace the syscall return in the child, too. */
movl %esp, %eax
call syscall_return_slowpath
+ STACKLEAK_ERASE
jmp restore_all

/* kernel thread */
@@ -458,6 +461,8 @@ ENTRY(entry_SYSENTER_32)
ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \
"jmp .Lsyscall_32_done", X86_FEATURE_XENPV

+ STACKLEAK_ERASE
+
/* Opportunistic SYSEXIT */
TRACE_IRQS_ON /* User mode traces as IRQs on. */
movl PT_EIP(%esp), %edx /* pt_regs->ip */
@@ -544,6 +549,8 @@ ENTRY(entry_INT80_32)
call do_int80_syscall_32
.Lsyscall_32_done:

+ STACKLEAK_ERASE
+
restore_all:
TRACE_IRQS_IRET
.Lrestore_all_notrace:
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 3166b9674429..e0b29f2bb267 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -329,6 +329,8 @@ syscall_return_via_sysret:
* We are on the trampoline stack. All regs except RDI are live.
* We can do future final exit work right here.
*/
+ STACKLEAK_ERASE_NOCLOBBER
+
SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi

popq %rdi
@@ -687,6 +689,7 @@ GLOBAL(swapgs_restore_regs_and_return_to_usermode)
* We are on the trampoline stack. All regs except RDI are live.
* We can do future final exit work right here.
*/
+ STACKLEAK_ERASE_NOCLOBBER

SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi

diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index 7d0df78db727..8eaf8952c408 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -261,6 +261,11 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe)

/* Opportunistic SYSRET */
sysret32_from_system_call:
+ /*
+ * We are not going to return to userspace from the trampoline
+ * stack. So let's erase the thread stack right now.
+ */
+ STACKLEAK_ERASE
TRACE_IRQS_ON /* User mode traces as IRQs on. */
movq RBX(%rsp), %rbx /* pt_regs->rbx */
movq RBP(%rsp), %rbp /* pt_regs->rbp */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ca3f3eae8980..050906fc255c 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1168,6 +1168,10 @@ struct task_struct {
void *security;
#endif

+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+ unsigned long lowest_stack;
+#endif
+
/*
* New fields for task_struct should be added above here, so that
* they are included in the randomized portion of task_struct.
diff --git a/include/linux/stackleak.h b/include/linux/stackleak.h
new file mode 100644
index 000000000000..d2560159e68b
--- /dev/null
+++ b/include/linux/stackleak.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_STACKLEAK_H
+#define _LINUX_STACKLEAK_H
+
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+
+/*
+ * Check that the poison value points to the unused hole in the
+ * virtual memory map for your platform.
+ */
+#define STACKLEAK_POISON -0xBEEF
+
+#define STACKLEAK_SEARCH_DEPTH 128
+
+static inline void stackleak_task_init(struct task_struct *t)
+{
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+ t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long);
+#endif
+}
+
+#endif
diff --git a/kernel/Makefile b/kernel/Makefile
index f85ae5dfa474..a530f77a0ab7 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -114,6 +114,10 @@ obj-$(CONFIG_TORTURE_TEST) += torture.o

obj-$(CONFIG_HAS_IOMEM) += memremap.o

+obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
+KASAN_SANITIZE_stackleak.o := n
+KCOV_INSTRUMENT_stackleak.o := n
+
$(obj)/configs.o: $(obj)/config_data.h

targets += config_data.gz
diff --git a/kernel/fork.c b/kernel/fork.c
index a5d21c42acfc..85956e9710af 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -91,6 +91,7 @@
#include <linux/kcov.h>
#include <linux/livepatch.h>
#include <linux/thread_info.h>
+#include <linux/stackleak.h>

#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -1804,6 +1805,8 @@ static __latent_entropy struct task_struct *copy_process(
if (retval)
goto bad_fork_cleanup_io;

+ stackleak_task_init(p);
+
if (pid != &init_struct_pid) {
pid = alloc_pid(p->nsproxy->pid_ns_for_children);
if (IS_ERR(pid)) {
diff --git a/kernel/stackleak.c b/kernel/stackleak.c
new file mode 100644
index 000000000000..ba2abdf3a569
--- /dev/null
+++ b/kernel/stackleak.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This code fills the used part of the kernel stack with a poison value
+ * before returning to userspace. It's part of the STACKLEAK feature
+ * ported from grsecurity/PaX.
+ *
+ * Author: Alexander Popov <alex.popov@linux.com>
+ *
+ * STACKLEAK reduces the information which kernel stack leak bugs can
+ * reveal and blocks some uninitialized stack variable attacks. Moreover,
+ * STACKLEAK blocks stack depth overflow caused by alloca(), aka Stack Clash
+ * attack.
+ */
+
+#include <linux/stackleak.h>
+
+asmlinkage void stackleak_erase(void)
+{
+ /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
+ unsigned long kstack_ptr = current->lowest_stack;
+ unsigned long boundary = kstack_ptr & ~(THREAD_SIZE - 1);
+ unsigned int poison_count = 0;
+ const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
+
+ /* Search for the poison value in the kernel stack */
+ while (kstack_ptr > boundary && poison_count <= depth) {
+ if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)
+ poison_count++;
+ else
+ poison_count = 0;
+
+ kstack_ptr -= sizeof(unsigned long);
+ }
+
+ /*
+ * One 'long int' at the bottom of the thread stack is reserved and
+ * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK=y).
+ */
+ if (kstack_ptr == boundary)
+ kstack_ptr += sizeof(unsigned long);
+
+ /*
+ * Now write the poison value to the kernel stack. Start from
+ * 'kstack_ptr' and move up till the new 'boundary'. We assume that
+ * the stack pointer doesn't change when we write poison.
+ */
+ if (on_thread_stack())
+ boundary = current_stack_pointer;
+ else
+ boundary = current_top_of_stack();
+
+ BUG_ON(boundary - kstack_ptr >= THREAD_SIZE);
+
+ while (kstack_ptr < boundary) {
+ *(unsigned long *)kstack_ptr = STACKLEAK_POISON;
+ kstack_ptr += sizeof(unsigned long);
+ }
+
+ /* Reset the 'lowest_stack' value for the next syscall */
+ current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64;
+}
--
2.16.1

Large diffs are not rendered by default.

@@ -0,0 +1,227 @@
From fb27f8ee56cd90b120928c1d72f2d72b9822ad3a Mon Sep 17 00:00:00 2001
From: Alexander Popov <alex.popov@linux.com>
Date: Wed, 11 Jul 2018 23:36:38 +0300
Subject: [PATCH 4/6] lkdtm: Add a test for STACKLEAK

Introduce lkdtm tests for the STACKLEAK feature.

First, all of them check that the current task stack is properly erased
(filled with STACKLEAK_POISON).

STACKLEAK_DEEP_RECURSION tests that exhausting the current task stack
with deep recursion is detected by CONFIG_VMAP_STACK (which is implied
by CONFIG_GCC_PLUGIN_STACKLEAK).

STACKLEAK_BIG_ALLOCA and STACKLEAK_RECURSION_WITH_ALLOCA test that
alloca() calls which overflow the kernel stack hit BUG()/panic() in
stackleak_check_alloca().

Signed-off-by: Alexander Popov <alex.popov@linux.com>
Signed-off-by: Tycho Andersen <tycho@tycho.ws>
Message-Id: <1531341400-12077-5-git-send-email-alex.popov@linux.com>
Signed-off-by: Steve Arnold <nerdboy@gentoo.org>
---
drivers/misc/lkdtm/Makefile | 2 +
drivers/misc/lkdtm/core.c | 3 +
drivers/misc/lkdtm/lkdtm.h | 5 ++
drivers/misc/lkdtm/stackleak.c | 146 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 156 insertions(+)
create mode 100644 drivers/misc/lkdtm/stackleak.c

diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile
index 3370a4138e94..951c984de61a 100644
--- a/drivers/misc/lkdtm/Makefile
+++ b/drivers/misc/lkdtm/Makefile
@@ -8,7 +8,9 @@ lkdtm-$(CONFIG_LKDTM) += perms.o
lkdtm-$(CONFIG_LKDTM) += refcount.o
lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o
lkdtm-$(CONFIG_LKDTM) += usercopy.o
+lkdtm-$(CONFIG_LKDTM) += stackleak.o

+KASAN_SANITIZE_stackleak.o := n
KCOV_INSTRUMENT_rodata.o := n

OBJCOPYFLAGS :=
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
index 2154d1bfd18b..9d0324a2e7e7 100644
--- a/drivers/misc/lkdtm/core.c
+++ b/drivers/misc/lkdtm/core.c
@@ -183,6 +183,9 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(USERCOPY_STACK_FRAME_FROM),
CRASHTYPE(USERCOPY_STACK_BEYOND),
CRASHTYPE(USERCOPY_KERNEL),
+ CRASHTYPE(STACKLEAK_BIG_ALLOCA),
+ CRASHTYPE(STACKLEAK_DEEP_RECURSION),
+ CRASHTYPE(STACKLEAK_RECURSION_WITH_ALLOCA),
};


diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index 9e513dcfd809..865a6c371bfa 100644
--- a/drivers/misc/lkdtm/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
@@ -83,4 +83,9 @@ void lkdtm_USERCOPY_STACK_FRAME_FROM(void);
void lkdtm_USERCOPY_STACK_BEYOND(void);
void lkdtm_USERCOPY_KERNEL(void);

+/* lkdtm_stackleak.c */
+void lkdtm_STACKLEAK_BIG_ALLOCA(void);
+void lkdtm_STACKLEAK_DEEP_RECURSION(void);
+void lkdtm_STACKLEAK_RECURSION_WITH_ALLOCA(void);
+
#endif
diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/stackleak.c
new file mode 100644
index 000000000000..5aecdec71623
--- /dev/null
+++ b/drivers/misc/lkdtm/stackleak.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This code tests several aspects of the STACKLEAK feature:
+ * - the current task stack is properly erased (filled with STACKLEAK_POISON);
+ * - exhausting the current task stack with deep recursion is detected by
+ * CONFIG_VMAP_STACK (which is implied by CONFIG_GCC_PLUGIN_STACKLEAK);
+ * - alloca() calls which overflow the kernel stack hit BUG()/panic() in
+ * stackleak_check_alloca().
+ *
+ * Authors:
+ * Alexander Popov <alex.popov@linux.com>
+ * Tycho Andersen <tycho@tycho.ws>
+ */
+
+#include "lkdtm.h"
+#include <linux/stackleak.h>
+
+static noinline bool stack_is_erased(void)
+{
+ unsigned long *sp, left, found, i;
+ const unsigned long check_depth =
+ STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
+
+ /*
+ * For the details about the alignment of the poison values, see
+ * the comment in stackleak_track_stack().
+ */
+ sp = PTR_ALIGN(&i, sizeof(unsigned long));
+
+ left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long);
+ sp--;
+
+ /*
+ * One 'long int' at the bottom of the thread stack is reserved
+ * and not poisoned.
+ */
+ if (left > 1)
+ left--;
+ else
+ return false;
+
+ pr_info("checking unused part of the thread stack (%lu bytes)...\n",
+ left * sizeof(unsigned long));
+
+ /*
+ * Search for 'check_depth' poison values in a row (just like
+ * stackleak_erase() does).
+ */
+ for (i = 0, found = 0; i < left && found <= check_depth; i++) {
+ if (*(sp - i) == STACKLEAK_POISON)
+ found++;
+ else
+ found = 0;
+ }
+
+ if (found <= check_depth) {
+ pr_err("FAIL: thread stack is not erased (checked %lu bytes)\n",
+ i * sizeof(unsigned long));
+ return false;
+ }
+
+ pr_info("first %lu bytes are unpoisoned\n",
+ (i - found) * sizeof(unsigned long));
+
+ /* The rest of thread stack should be erased */
+ for (; i < left; i++) {
+ if (*(sp - i) != STACKLEAK_POISON) {
+ pr_err("FAIL: thread stack is NOT properly erased\n");
+ return false;
+ }
+ }
+
+ pr_info("the rest of the thread stack is properly erased\n");
+ return true;
+}
+
+static noinline void do_alloca(unsigned long size)
+{
+ char buf[size];
+
+ /* So this doesn't get inlined or optimized out */
+ snprintf(buf, size, "testing alloca...\n");
+}
+
+void lkdtm_STACKLEAK_BIG_ALLOCA(void)
+{
+ if (!stack_is_erased())
+ return;
+
+ pr_info("try a small alloca() of 16 bytes...\n");
+ do_alloca(16);
+ pr_info("small alloca() is successful\n");
+
+ pr_info("try alloca() over the thread stack boundary...\n");
+ do_alloca(THREAD_SIZE);
+ pr_err("FAIL: alloca() over the thread stack boundary is NOT detected\n");
+}
+
+static noinline unsigned long recursion(unsigned long prev_sp, bool with_alloca)
+{
+ char buf[400];
+ unsigned long sp = (unsigned long)&sp;
+
+ snprintf(buf, sizeof(buf), "testing deep recursion...\n");
+
+ if (with_alloca)
+ do_alloca(400);
+
+ if (prev_sp < sp + THREAD_SIZE)
+ sp = recursion(prev_sp, with_alloca);
+
+ return sp;
+}
+
+void lkdtm_STACKLEAK_DEEP_RECURSION(void)
+{
+ unsigned long sp = (unsigned long)&sp;
+
+ if (!stack_is_erased())
+ return;
+
+ /*
+ * Overflow the thread stack using deep recursion. It should hit the
+ * guard page provided by CONFIG_VMAP_STACK (which is implied by
+ * CONFIG_GCC_PLUGIN_STACKLEAK).
+ */
+ pr_info("try to overflow the thread stack using deep recursion...\n");
+ pr_err("FAIL: stack depth overflow (%lu bytes) is not detected\n",
+ sp - recursion(sp, 0));
+}
+
+void lkdtm_STACKLEAK_RECURSION_WITH_ALLOCA(void)
+{
+ unsigned long sp = (unsigned long)&sp;
+
+ if (!stack_is_erased())
+ return;
+
+ /*
+ * Overflow the thread stack using deep recursion with alloca.
+ * It should hit BUG()/panic() in stackleak_check_alloca().
+ */
+ pr_info("try to overflow the thread stack using recursion & alloca\n");
+ recursion(sp, 1);
+ pr_err("FAIL: stack depth overflow is not detected\n");
+}
--
2.16.1

@@ -0,0 +1,124 @@
From 639ebb03fa61e899c21ed2684e40725612458127 Mon Sep 17 00:00:00 2001
From: Steve Arnold <nerdboy@gentoo.org>
Date: Fri, 3 Aug 2018 14:39:08 -0700
Subject: [PATCH 5/6] fs/proc: Show STACKLEAK metrics in the /proc file system

Introduce CONFIG_STACKLEAK_METRICS providing STACKLEAK information about
tasks via the /proc file system. In particular, /proc/<pid>/stack_depth
shows the maximum kernel stack consumption for the current and previous
syscalls. Although this information is not precise, it can be useful for
estimating the STACKLEAK performance impact for your workloads.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
Signed-off-by: Steve Arnold <nerdboy@gentoo.org>
---
arch/Kconfig | 12 ++++++++++++
fs/proc/base.c | 18 ++++++++++++++++++
include/linux/sched.h | 1 +
include/linux/stackleak.h | 3 +++
kernel/stackleak.c | 4 ++++
5 files changed, 38 insertions(+)

diff --git a/arch/Kconfig b/arch/Kconfig
index ac0811ef6d70..3e3bdb433e0c 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -576,6 +576,18 @@ config STACKLEAK_TRACK_MIN_SIZE
a stack frame size greater than or equal to this parameter.
If unsure, leave the default value 100.

+config STACKLEAK_METRICS
+ bool "Show STACKLEAK metrics in the /proc file system"
+ depends on GCC_PLUGIN_STACKLEAK
+ depends on PROC_FS
+ help
+ If this is set, STACKLEAK metrics for every task are available in
+ the /proc file system. In particular, /proc/<pid>/stack_depth
+ shows the maximum kernel stack consumption for the current and
+ previous syscalls. Although this information is not precise, it
+ can be useful for estimating the STACKLEAK performance impact for
+ your workloads.
+
config HAVE_CC_STACKPROTECTOR
bool
help
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 1a76d751cf3c..5a97edf20c0f 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2924,6 +2924,21 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns,
}
#endif /* CONFIG_LIVEPATCH */

+#ifdef CONFIG_STACKLEAK_METRICS
+static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *task)
+{
+ unsigned long prev_depth = THREAD_SIZE -
+ (task->prev_lowest_stack & (THREAD_SIZE - 1));
+ unsigned long depth = THREAD_SIZE -
+ (task->lowest_stack & (THREAD_SIZE - 1));
+
+ seq_printf(m, "previous stack depth: %lu\nstack depth: %lu\n",
+ prev_depth, depth);
+ return 0;
+}
+#endif /* CONFIG_STACKLEAK_METRICS */
+
/*
* Thread groups
*/
@@ -3025,6 +3040,9 @@ static const struct pid_entry tgid_base_stuff[] = {
#ifdef CONFIG_LIVEPATCH
ONE("patch_state", S_IRUSR, proc_pid_patch_state),
#endif
+#ifdef CONFIG_STACKLEAK_METRICS
+ ONE("stack_depth", S_IRUGO, proc_stack_depth),
+#endif
};

static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 050906fc255c..3456d7cc32aa 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1170,6 +1170,7 @@ struct task_struct {

#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
unsigned long lowest_stack;
+ unsigned long prev_lowest_stack;
#endif

/*
diff --git a/include/linux/stackleak.h b/include/linux/stackleak.h
index d2560159e68b..b62f1330c9ff 100644
--- a/include/linux/stackleak.h
+++ b/include/linux/stackleak.h
@@ -17,6 +17,9 @@ static inline void stackleak_task_init(struct task_struct *t)
{
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long);
+# ifdef CONFIG_STACKLEAK_METRICS
+ t->prev_lowest_stack = t->lowest_stack;
+# endif
#endif
}

diff --git a/kernel/stackleak.c b/kernel/stackleak.c
index 259b6fbefa5f..90f73187eac3 100644
--- a/kernel/stackleak.c
+++ b/kernel/stackleak.c
@@ -39,6 +39,10 @@ asmlinkage void stackleak_erase(void)
if (kstack_ptr == boundary)
kstack_ptr += sizeof(unsigned long);

+#ifdef CONFIG_STACKLEAK_METRICS
+ current->prev_lowest_stack = kstack_ptr;
+#endif
+
/*
* Now write the poison value to the kernel stack. Start from
* 'kstack_ptr' and move up till the new 'boundary'. We assume that
--
2.16.1

@@ -0,0 +1,60 @@
From ad9873f821956da5ef5d9fe5f2560e29e84f0527 Mon Sep 17 00:00:00 2001
From: Alexander Popov <alex.popov@linux.com>
Date: Wed, 11 Jul 2018 23:36:40 +0300
Subject: [PATCH 6/6] doc: self-protection: Add information about STACKLEAK
feature

Add information about STACKLEAK feature to "Stack depth overflow" and
"Memory poisoning" sections of self-protection.rst.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
Message-Id: <1531341400-12077-7-git-send-email-alex.popov@linux.com>
Signed-off-by: Steve Arnold <nerdboy@gentoo.org>
---
Documentation/security/self-protection.rst | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/Documentation/security/self-protection.rst b/Documentation/security/self-protection.rst
index 0f53826c78b9..b685f18041a0 100644
--- a/Documentation/security/self-protection.rst
+++ b/Documentation/security/self-protection.rst
@@ -165,10 +165,15 @@ Stack depth overflow
A less well understood attack is using a bug that triggers the
kernel to consume stack memory with deep function calls or large stack
allocations. With this attack it is possible to write beyond the end of
-the kernel's preallocated stack space and into sensitive structures. Two
-important changes need to be made for better protections: moving the
-sensitive thread_info structure elsewhere, and adding a faulting memory
-hole at the bottom of the stack to catch these overflows.
+the kernel's preallocated stack space and into sensitive structures.
+The combination of the following measures gives better protection:
+
+* moving the sensitive thread_info structure off the stack
+ (``CONFIG_THREAD_INFO_IN_TASK``);
+* adding a faulting memory hole at the bottom of the stack to catch
+ these overflows (``CONFIG_VMAP_STACK``);
+* runtime checking that alloca() calls don't overstep the stack boundary
+ (``CONFIG_GCC_PLUGIN_STACKLEAK``).

Heap memory integrity
---------------------
@@ -302,11 +307,11 @@ sure structure holes are cleared.
Memory poisoning
----------------

-When releasing memory, it is best to poison the contents (clear stack on
-syscall return, wipe heap memory on a free), to avoid reuse attacks that
-rely on the old contents of memory. This frustrates many uninitialized
-variable attacks, stack content exposures, heap content exposures, and
-use-after-free attacks.
+When releasing memory, it is best to poison the contents, to avoid reuse
+attacks that rely on the old contents of memory. E.g., clear stack on a
+syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a
+free. This frustrates many uninitialized variable attacks, stack content
+exposures, heap content exposures, and use-after-free attacks.

Destination tracking
--------------------
--
2.16.1

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,63 @@
# Copyright 1999-2018 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

EAPI="6"
UNIPATCH_STRICTORDER="1"
ETYPE="sources"
K_WANT_GENPATCHES="base extras experimental"
K_GENPATCHES_VER="17"

SPLASH_PATCH="linux-4.15-bootsplash-patches-and-Makefile-fix.patch"
SPL_PATCH_URI="mirror://gentoo/${SPLASH_PATCH}.gz"

inherit kernel-2
detect_version
detect_arch

KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86"
HOMEPAGE="https://dev.gentoo.org/~mpagano/genpatches
https://github.com/philmmanjaro/linux-bootsplash"
IUSE="experimental"

DESCRIPTION="Full sources including the Gentoo patchset for the ${KV_MAJOR}.${KV_MINOR} kernel tree"
SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI} ${SPL_PATCH_URI}"

K_EXTRAELOG="This is the bleeding-edge mainline gentoo-sources kernel
with the linux bootsplash patches on top (see tools/bootsplash). You
need to enable CONFIG_BOOTSPLASH and build the splash file using the
script in the above directory (a sample tux bootsplash is provided).
Then install the splash file as shown and add the cmdline parameter."

src_unpack() {
# need to unpack manually due to patch reqs below
unpack ${SPLASH_PATCH}.gz

kernel-2_src_unpack
}

src_prepare() {
ebegin "Applying kernel bootsplash patches"
EPATCH_OPTS="-F3"
epatch "${WORKDIR}"/${SPLASH_PATCH} || die "splash patch failed!"
cp "${FILESDIR}"/*.gif "${S}"/tools/bootsplash/
epatch "${FILESDIR}"/4.15.14/*
eend $? || return

kernel-2_src_prepare
}

pkg_postinst() {
kernel-2_pkg_postinst
einfo ""
einfo "To configure the bootsplash, copy the file and add cmdline:"
einfo ""
einfo " Splash-file under /lib/firmware/mypath/myfile"
einfo " Cmdline: bootsplash.bootfile=mypath/myfile"
einfo ""
einfo "For more info on this patchset, and how to report problems, see:"
einfo "${HOMEPAGE}"
}

pkg_postrm() {
kernel-2_pkg_postrm
}
@@ -0,0 +1,78 @@
# Copyright 1999-2018 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

EAPI="6"

ETYPE="sources"
UNIPATCH_STRICTORDER="1"
K_WANT_GENPATCHES="base extras experimental"
K_GENPATCHES_VER="12"

inherit eutils kernel-2
detect_version
detect_arch

K_BRANCH_ID="${KV_MAJOR}.${KV_MINOR}"
SPLASH_PATCH="linux-${K_BRANCH_ID}-bootsplash-patches-for-kernel-space-fbc.patch"
SPLASH_URI="mirror://gentoo/${SPLASH_PATCH}.gz"
LOGO_PATCH="linux-4.14-bootsplash-add-gentoo-logo-build-script.patch"

KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86"
HOMEPAGE="https://dev.gentoo.org/~mpagano/genpatches"
IUSE="experimental"

DESCRIPTION="Full sources including the Gentoo patchset for the ${KV_MAJOR}.${KV_MINOR} kernel tree"
SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI} ${SPLASH_URI}"

RDEPEND=""
DEPEND="${RDEPEND}
>=dev-vcs/git-1.8.2.1"

K_EXTRAELOG="This is the bleeding-edge mainline gentoo-sources kernel
with the linux bootsplash patches on top (see tools/bootsplash). You
need to enable CONFIG_BOOTSPLASH and build the splash file using the
script in the above directory (a sample tux bootsplash is provided).
Then install the splash file as shown and add the cmdline parameter."

src_unpack() {
# need to unpack manually and depend on git due to patch reqs below
unpack ${SPLASH_PATCH}.gz

kernel-2_src_unpack
}

src_prepare() {
# We can't use unipatch or epatch here due to the git binary
# diffs that always cause dry-run errors (even with --force).

ebegin "Applying kernel bootsplash, protectable memory, and stackleak patches"
EPATCH_OPTS="-F3 -b"
epatch "${WORKDIR}"/${SPLASH_PATCH} || die "splash patch failed!"
epatch "${FILESDIR}"/${LOGO_PATCH} || die "logo patch failed!"
cp "${FILESDIR}"/*.gif "${S}"/tools/bootsplash/

epatch "${FILESDIR}"/${K_BRANCH_ID}/stack/*
epatch "${FILESDIR}"/${K_BRANCH_ID}/pmem/*
eend $? || return

default

# clean up workdir so we don't install patch cruft
rm -f "${WORKDIR}"/*bootsplash-patches-for-kernel-space-fbc*
}

pkg_postinst() {
kernel-2_pkg_postinst
einfo ""
einfo "To configure the bootsplash, copy the file and add cmdline:"
einfo ""
einfo " Splash-file under /lib/firmware/mypath/myfile"
einfo " Cmdline: bootsplash.bootfile=mypath/myfile"
einfo ""
einfo "For more info on this patchset, and how to report problems, see:"
einfo " https://github.com/philmmanjaro/linux-bootsplash"
}

pkg_postrm() {
kernel-2_pkg_postrm
}