Merge a6b450573b ("Merge tag 'execve-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux") into android-mainline
Steps on the way to 5.19-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: Ic005ca1e2d3733b5ec1db9f34c1c77d44c3be4ff
This commit is contained in:
@@ -211,6 +211,7 @@ r200_reg_safe.h
|
||||
r300_reg_safe.h
|
||||
r420_reg_safe.h
|
||||
r600_reg_safe.h
|
||||
randstruct.seed
|
||||
randomize_layout_hash.h
|
||||
randomize_layout_seed.h
|
||||
recordmcount
|
||||
|
||||
@@ -99,10 +99,10 @@ unreproducible parts can be treated as sources:
|
||||
Structure randomisation
|
||||
-----------------------
|
||||
|
||||
If you enable ``CONFIG_GCC_PLUGIN_RANDSTRUCT``, you will need to
|
||||
pre-generate the random seed in
|
||||
``scripts/gcc-plugins/randomize_layout_seed.h`` so the same value
|
||||
is used in rebuilds.
|
||||
If you enable ``CONFIG_RANDSTRUCT``, you will need to pre-generate
|
||||
the random seed in ``scripts/basic/randstruct.seed`` so the same
|
||||
value is used by each build. See ``scripts/gen-randstruct-seed.sh``
|
||||
for details.
|
||||
|
||||
Debug info conflicts
|
||||
--------------------
|
||||
|
||||
@@ -271,6 +271,16 @@ notifying process it will be replaced. The supervisor can also add an FD, and
|
||||
respond atomically by using the ``SECCOMP_ADDFD_FLAG_SEND`` flag and the return
|
||||
value will be the injected file descriptor number.
|
||||
|
||||
The notifying process can be preempted, resulting in the notification being
|
||||
aborted. This can be problematic when trying to take actions on behalf of the
|
||||
notifying process that are long-running and typically retryable (mounting a
|
||||
filesytem). Alternatively, at filter installation time, the
|
||||
``SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV`` flag can be set. This flag makes it
|
||||
such that when a user notification is received by the supervisor, the notifying
|
||||
process will ignore non-fatal signals until the response is sent. Signals that
|
||||
are sent prior to the notification being received by userspace are handled
|
||||
normally.
|
||||
|
||||
It is worth noting that ``struct seccomp_data`` contains the values of register
|
||||
arguments to the syscall, but does not contain pointers to memory. The task's
|
||||
memory is accessible to suitably privileged traces via ``ptrace()`` or
|
||||
|
||||
@@ -1037,6 +1037,7 @@ include-$(CONFIG_KASAN) += scripts/Makefile.kasan
|
||||
include-$(CONFIG_KCSAN) += scripts/Makefile.kcsan
|
||||
include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan
|
||||
include-$(CONFIG_KCOV) += scripts/Makefile.kcov
|
||||
include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct
|
||||
include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins
|
||||
|
||||
include $(addprefix $(srctree)/, $(include-y))
|
||||
|
||||
+1
-4
@@ -732,10 +732,7 @@ config ARCH_SUPPORTS_CFI_CLANG
|
||||
config CFI_CLANG
|
||||
bool "Use Clang's Control Flow Integrity (CFI)"
|
||||
depends on LTO_CLANG && ARCH_SUPPORTS_CFI_CLANG
|
||||
# Clang >= 12:
|
||||
# - https://bugs.llvm.org/show_bug.cgi?id=46258
|
||||
# - https://bugs.llvm.org/show_bug.cgi?id=47479
|
||||
depends on CLANG_VERSION >= 120000
|
||||
depends on CLANG_VERSION >= 140000
|
||||
select KALLSYMS
|
||||
help
|
||||
This option enables Clang’s forward-edge Control Flow Integrity
|
||||
|
||||
@@ -30,7 +30,6 @@ CONFIG_ARM_APPENDED_DTB=y
|
||||
# CONFIG_BLK_DEV_BSG is not set
|
||||
CONFIG_BINFMT_FLAT=y
|
||||
CONFIG_BINFMT_ZFLAT=y
|
||||
CONFIG_BINFMT_SHARED_FLAT=y
|
||||
# CONFIG_COREDUMP is not set
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
|
||||
@@ -23,7 +23,6 @@ CONFIG_PREEMPT_VOLUNTARY=y
|
||||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_BINFMT_FLAT=y
|
||||
CONFIG_BINFMT_SHARED_FLAT=y
|
||||
# CONFIG_COREDUMP is not set
|
||||
# CONFIG_SUSPEND is not set
|
||||
CONFIG_NET=y
|
||||
|
||||
@@ -28,7 +28,6 @@ CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_XIP_KERNEL=y
|
||||
CONFIG_XIP_PHYS_ADDR=0x08008000
|
||||
CONFIG_BINFMT_FLAT=y
|
||||
CONFIG_BINFMT_SHARED_FLAT=y
|
||||
# CONFIG_COREDUMP is not set
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
|
||||
@@ -18,7 +18,6 @@ CONFIG_XIP_KERNEL=y
|
||||
CONFIG_XIP_PHYS_ADDR=0x0f000080
|
||||
CONFIG_BINFMT_FLAT=y
|
||||
CONFIG_BINFMT_ZFLAT=y
|
||||
CONFIG_BINFMT_SHARED_FLAT=y
|
||||
# CONFIG_SUSPEND is not set
|
||||
# CONFIG_UEVENT_HELPER is not set
|
||||
# CONFIG_STANDALONE is not set
|
||||
|
||||
@@ -28,7 +28,7 @@ CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
|
||||
CFLAGS_REMOVE_vdso.o = -pg
|
||||
|
||||
# Force -O2 to avoid libgcc dependencies
|
||||
CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(GCC_PLUGINS_CFLAGS)
|
||||
CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS)
|
||||
ifeq ($(c-gettimeofday-y),)
|
||||
CFLAGS_vgettimeofday.o = -O2
|
||||
else
|
||||
|
||||
@@ -23,20 +23,4 @@
|
||||
#define __builtin_return_address(val) \
|
||||
(void *)(ptrauth_clear_pac((unsigned long)__builtin_return_address(val)))
|
||||
|
||||
#ifdef CONFIG_CFI_CLANG
|
||||
/*
|
||||
* With CONFIG_CFI_CLANG, the compiler replaces function address
|
||||
* references with the address of the function's CFI jump table
|
||||
* entry. The function_nocfi macro always returns the address of the
|
||||
* actual function instead.
|
||||
*/
|
||||
#define function_nocfi(x) ({ \
|
||||
void *addr; \
|
||||
asm("adrp %0, " __stringify(x) "\n\t" \
|
||||
"add %0, %0, :lo12:" __stringify(x) \
|
||||
: "=r" (addr)); \
|
||||
addr; \
|
||||
})
|
||||
#endif
|
||||
|
||||
#endif /* __ASM_COMPILER_H */
|
||||
|
||||
@@ -408,12 +408,10 @@ long get_tagged_addr_ctrl(struct task_struct *task);
|
||||
* of header definitions for the use of task_stack_page.
|
||||
*/
|
||||
|
||||
#define current_top_of_stack() \
|
||||
({ \
|
||||
struct stack_info _info; \
|
||||
BUG_ON(!on_accessible_stack(current, current_stack_pointer, 1, &_info)); \
|
||||
_info.high; \
|
||||
})
|
||||
/*
|
||||
* The top of the current task's task stack
|
||||
*/
|
||||
#define current_top_of_stack() ((unsigned long)current->stack + THREAD_SIZE)
|
||||
#define on_thread_stack() (on_task_stack(current, current_stack_pointer, 1, NULL))
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
@@ -596,7 +596,7 @@ SYM_CODE_START_LOCAL(ret_to_user)
|
||||
ldr x19, [tsk, #TSK_TI_FLAGS] // re-check for single-step
|
||||
enable_step_tsk x19, x2
|
||||
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
|
||||
bl stackleak_erase
|
||||
bl stackleak_erase_on_task_stack
|
||||
#endif
|
||||
kernel_exit 0
|
||||
SYM_CODE_END(ret_to_user)
|
||||
|
||||
@@ -32,7 +32,8 @@ ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
|
||||
# -Wmissing-prototypes and -Wmissing-declarations are removed from
|
||||
# the CFLAGS of vgettimeofday.c to make possible to build the
|
||||
# kernel with CONFIG_WERROR enabled.
|
||||
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) $(GCC_PLUGINS_CFLAGS) \
|
||||
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \
|
||||
$(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) \
|
||||
$(CC_FLAGS_LTO) -Wmissing-prototypes -Wmissing-declarations
|
||||
KASAN_SANITIZE := n
|
||||
KCSAN_SANITIZE := n
|
||||
|
||||
+1
-1
@@ -468,7 +468,7 @@ config CC_HAVE_STACKPROTECTOR_TLS
|
||||
|
||||
config STACKPROTECTOR_PER_TASK
|
||||
def_bool y
|
||||
depends on !GCC_PLUGIN_RANDSTRUCT
|
||||
depends on !RANDSTRUCT
|
||||
depends on STACKPROTECTOR && CC_HAVE_STACKPROTECTOR_TLS
|
||||
|
||||
config PHYS_RAM_BASE_FIXED
|
||||
|
||||
@@ -25,7 +25,6 @@ CONFIG_CMDLINE_OVERWRITE=y
|
||||
CONFIG_CMDLINE="console=ttySC0,115200 earlyprintk=serial ignore_loglevel"
|
||||
CONFIG_BINFMT_FLAT=y
|
||||
CONFIG_BINFMT_ZFLAT=y
|
||||
CONFIG_BINFMT_SHARED_FLAT=y
|
||||
CONFIG_PM=y
|
||||
CONFIG_CPU_IDLE=y
|
||||
# CONFIG_STANDALONE is not set
|
||||
|
||||
@@ -30,7 +30,6 @@ CONFIG_CMDLINE_OVERWRITE=y
|
||||
CONFIG_CMDLINE="console=ttySC0,115200 earlyprintk=serial ignore_loglevel"
|
||||
CONFIG_BINFMT_FLAT=y
|
||||
CONFIG_BINFMT_ZFLAT=y
|
||||
CONFIG_BINFMT_SHARED_FLAT=y
|
||||
CONFIG_PM=y
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_NET=y
|
||||
|
||||
@@ -40,7 +40,6 @@ CONFIG_CMDLINE_OVERWRITE=y
|
||||
CONFIG_CMDLINE="console=ttySC3,115200 ignore_loglevel earlyprintk=serial"
|
||||
CONFIG_BINFMT_FLAT=y
|
||||
CONFIG_BINFMT_ZFLAT=y
|
||||
CONFIG_BINFMT_SHARED_FLAT=y
|
||||
CONFIG_BINFMT_MISC=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
|
||||
@@ -58,7 +58,7 @@ CFL := $(PROFILING) -mcmodel=medlow -fPIC -O2 -fasynchronous-unwind-tables -m64
|
||||
|
||||
SPARC_REG_CFLAGS = -ffixed-g4 -ffixed-g5 -fcall-used-g5 -fcall-used-g7
|
||||
|
||||
$(vobjs): KBUILD_CFLAGS := $(filter-out $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
|
||||
$(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
|
||||
|
||||
#
|
||||
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
|
||||
@@ -88,6 +88,7 @@ $(obj)/vdso32.so.dbg: asflags-$(CONFIG_SPARC64) += -m32
|
||||
KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
|
||||
KBUILD_CFLAGS_32 := $(filter-out -mcmodel=medlow,$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 += -m32 -msoft-float -fpic
|
||||
|
||||
@@ -91,7 +91,7 @@ ifneq ($(RETPOLINE_VDSO_CFLAGS),)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(vobjs): KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
|
||||
$(vobjs): KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO) $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
|
||||
|
||||
#
|
||||
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
|
||||
@@ -148,6 +148,7 @@ KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
|
||||
KBUILD_CFLAGS_32 := $(filter-out -mcmodel=kernel,$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out -mfentry,$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS_32))
|
||||
KBUILD_CFLAGS_32 := $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS_32))
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/paravirt.h>
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/pgtable_areas.h>
|
||||
|
||||
/* declarations for highmem.c */
|
||||
extern unsigned long highstart_pfn, highend_pfn;
|
||||
|
||||
+1
-1
@@ -540,7 +540,7 @@ static inline bool pti_kernel_image_global_ok(void)
|
||||
* cases where RANDSTRUCT is in use to help keep the layout a
|
||||
* secret.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_GCC_PLUGIN_RANDSTRUCT))
|
||||
if (IS_ENABLED(CONFIG_RANDSTRUCT))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
+100
-47
@@ -11,72 +11,125 @@
|
||||
#include "lkdtm.h"
|
||||
#include <linux/stackleak.h>
|
||||
|
||||
void lkdtm_STACKLEAK_ERASING(void)
|
||||
#if defined(CONFIG_GCC_PLUGIN_STACKLEAK)
|
||||
/*
|
||||
* Check that stackleak tracks the lowest stack pointer and erases the stack
|
||||
* below this as expected.
|
||||
*
|
||||
* To prevent the lowest stack pointer changing during the test, IRQs are
|
||||
* masked and instrumentation of this function is disabled. We assume that the
|
||||
* compiler will create a fixed-size stack frame for this function.
|
||||
*
|
||||
* Any non-inlined function may make further use of the stack, altering the
|
||||
* lowest stack pointer and/or clobbering poison values. To avoid spurious
|
||||
* failures we must avoid printing until the end of the test or have already
|
||||
* encountered a failure condition.
|
||||
*/
|
||||
static void noinstr check_stackleak_irqoff(void)
|
||||
{
|
||||
unsigned long *sp, left, found, i;
|
||||
const unsigned long check_depth =
|
||||
STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
|
||||
const unsigned long task_stack_base = (unsigned long)task_stack_page(current);
|
||||
const unsigned long task_stack_low = stackleak_task_low_bound(current);
|
||||
const unsigned long task_stack_high = stackleak_task_high_bound(current);
|
||||
const unsigned long current_sp = current_stack_pointer;
|
||||
const unsigned long lowest_sp = current->lowest_stack;
|
||||
unsigned long untracked_high;
|
||||
unsigned long poison_high, poison_low;
|
||||
bool test_failed = false;
|
||||
|
||||
/*
|
||||
* For the details about the alignment of the poison values, see
|
||||
* the comment in stackleak_track_stack().
|
||||
* Check that the current and lowest recorded stack pointer values fall
|
||||
* within the expected task stack boundaries. These tests should never
|
||||
* fail unless the boundaries are incorrect or we're clobbering the
|
||||
* STACK_END_MAGIC, and in either casee something is seriously wrong.
|
||||
*/
|
||||
sp = PTR_ALIGN(&i, sizeof(unsigned long));
|
||||
|
||||
left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long);
|
||||
sp--;
|
||||
if (current_sp < task_stack_low || current_sp >= task_stack_high) {
|
||||
pr_err("FAIL: current_stack_pointer (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n",
|
||||
current_sp, task_stack_low, task_stack_high - 1);
|
||||
test_failed = true;
|
||||
goto out;
|
||||
}
|
||||
if (lowest_sp < task_stack_low || lowest_sp >= task_stack_high) {
|
||||
pr_err("FAIL: current->lowest_stack (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n",
|
||||
lowest_sp, task_stack_low, task_stack_high - 1);
|
||||
test_failed = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* One 'long int' at the bottom of the thread stack is reserved
|
||||
* and not poisoned.
|
||||
* Depending on what has run prior to this test, the lowest recorded
|
||||
* stack pointer could be above or below the current stack pointer.
|
||||
* Start from the lowest of the two.
|
||||
*
|
||||
* Poison values are naturally-aligned unsigned longs. As the current
|
||||
* stack pointer might not be sufficiently aligned, we must align
|
||||
* downwards to find the lowest known stack pointer value. This is the
|
||||
* high boundary for a portion of the stack which may have been used
|
||||
* without being tracked, and has to be scanned for poison.
|
||||
*/
|
||||
if (left > 1) {
|
||||
left--;
|
||||
} else {
|
||||
pr_err("FAIL: not enough stack space for the test\n");
|
||||
test_failed = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_info("checking unused part of the thread stack (%lu bytes)...\n",
|
||||
left * sizeof(unsigned long));
|
||||
untracked_high = min(current_sp, lowest_sp);
|
||||
untracked_high = ALIGN_DOWN(untracked_high, sizeof(unsigned long));
|
||||
|
||||
/*
|
||||
* Search for 'check_depth' poison values in a row (just like
|
||||
* stackleak_erase() does).
|
||||
* Find the top of the poison in the same way as the erasing code.
|
||||
*/
|
||||
for (i = 0, found = 0; i < left && found <= check_depth; i++) {
|
||||
if (*(sp - i) == STACKLEAK_POISON)
|
||||
found++;
|
||||
else
|
||||
found = 0;
|
||||
}
|
||||
poison_high = stackleak_find_top_of_poison(task_stack_low, untracked_high);
|
||||
|
||||
if (found <= check_depth) {
|
||||
pr_err("FAIL: the erased part is not found (checked %lu bytes)\n",
|
||||
i * sizeof(unsigned long));
|
||||
/*
|
||||
* Check whether the poisoned portion of the stack (if any) consists
|
||||
* entirely of poison. This verifies the entries that
|
||||
* stackleak_find_top_of_poison() should have checked.
|
||||
*/
|
||||
poison_low = poison_high;
|
||||
while (poison_low > task_stack_low) {
|
||||
poison_low -= sizeof(unsigned long);
|
||||
|
||||
if (*(unsigned long *)poison_low == STACKLEAK_POISON)
|
||||
continue;
|
||||
|
||||
pr_err("FAIL: non-poison value %lu bytes below poison boundary: 0x%lx\n",
|
||||
poison_high - poison_low, *(unsigned long *)poison_low);
|
||||
test_failed = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_info("the erased part begins after %lu not poisoned bytes\n",
|
||||
(i - found) * sizeof(unsigned long));
|
||||
pr_info("stackleak stack usage:\n"
|
||||
" high offset: %lu bytes\n"
|
||||
" current: %lu bytes\n"
|
||||
" lowest: %lu bytes\n"
|
||||
" tracked: %lu bytes\n"
|
||||
" untracked: %lu bytes\n"
|
||||
" poisoned: %lu bytes\n"
|
||||
" low offset: %lu bytes\n",
|
||||
task_stack_base + THREAD_SIZE - task_stack_high,
|
||||
task_stack_high - current_sp,
|
||||
task_stack_high - lowest_sp,
|
||||
task_stack_high - untracked_high,
|
||||
untracked_high - poison_high,
|
||||
poison_high - task_stack_low,
|
||||
task_stack_low - task_stack_base);
|
||||
|
||||
/* The rest of thread stack should be erased */
|
||||
for (; i < left; i++) {
|
||||
if (*(sp - i) != STACKLEAK_POISON) {
|
||||
pr_err("FAIL: bad value number %lu in the erased part: 0x%lx\n",
|
||||
i, *(sp - i));
|
||||
test_failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
out:
|
||||
if (test_failed) {
|
||||
pr_err("FAIL: the thread stack is NOT properly erased!\n");
|
||||
pr_expected_config(CONFIG_GCC_PLUGIN_STACKLEAK);
|
||||
} else {
|
||||
pr_info("OK: the rest of the thread stack is properly erased\n");
|
||||
}
|
||||
}
|
||||
|
||||
void lkdtm_STACKLEAK_ERASING(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
check_stackleak_irqoff();
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#else /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */
|
||||
void lkdtm_STACKLEAK_ERASING(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_HAVE_ARCH_STACKLEAK)) {
|
||||
pr_err("XFAIL: stackleak is not enabled (CONFIG_GCC_PLUGIN_STACKLEAK=n)\n");
|
||||
} else {
|
||||
pr_err("XFAIL: stackleak is not supported on this arch (HAVE_ARCH_STACKLEAK=n)\n");
|
||||
}
|
||||
}
|
||||
#endif /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */
|
||||
|
||||
@@ -35,6 +35,25 @@
|
||||
|
||||
#include "niu.h"
|
||||
|
||||
/* This driver wants to store a link to a "next page" within the
|
||||
* page struct itself by overloading the content of the "mapping"
|
||||
* member. This is not expected by the page API, but does currently
|
||||
* work. However, the randstruct plugin gets very bothered by this
|
||||
* case because "mapping" (struct address_space) is randomized, so
|
||||
* casts to/from it trigger warnings. Hide this by way of a union,
|
||||
* to create a typed alias of "mapping", since that's how it is
|
||||
* actually being used here.
|
||||
*/
|
||||
union niu_page {
|
||||
struct page page;
|
||||
struct {
|
||||
unsigned long __flags; /* unused alias of "flags" */
|
||||
struct list_head __lru; /* unused alias of "lru" */
|
||||
struct page *next; /* alias of "mapping" */
|
||||
};
|
||||
};
|
||||
#define niu_next_page(p) container_of(p, union niu_page, page)->next
|
||||
|
||||
#define DRV_MODULE_NAME "niu"
|
||||
#define DRV_MODULE_VERSION "1.1"
|
||||
#define DRV_MODULE_RELDATE "Apr 22, 2010"
|
||||
@@ -3283,7 +3302,7 @@ static struct page *niu_find_rxpage(struct rx_ring_info *rp, u64 addr,
|
||||
|
||||
addr &= PAGE_MASK;
|
||||
pp = &rp->rxhash[h];
|
||||
for (; (p = *pp) != NULL; pp = (struct page **) &p->mapping) {
|
||||
for (; (p = *pp) != NULL; pp = &niu_next_page(p)) {
|
||||
if (p->index == addr) {
|
||||
*link = pp;
|
||||
goto found;
|
||||
@@ -3300,7 +3319,7 @@ static void niu_hash_page(struct rx_ring_info *rp, struct page *page, u64 base)
|
||||
unsigned int h = niu_hash_rxaddr(rp, base);
|
||||
|
||||
page->index = base;
|
||||
page->mapping = (struct address_space *) rp->rxhash[h];
|
||||
niu_next_page(page) = rp->rxhash[h];
|
||||
rp->rxhash[h] = page;
|
||||
}
|
||||
|
||||
@@ -3382,11 +3401,11 @@ static int niu_rx_pkt_ignore(struct niu *np, struct rx_ring_info *rp)
|
||||
rcr_size = rp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >>
|
||||
RCR_ENTRY_PKTBUFSZ_SHIFT];
|
||||
if ((page->index + PAGE_SIZE) - rcr_size == addr) {
|
||||
*link = (struct page *) page->mapping;
|
||||
*link = niu_next_page(page);
|
||||
np->ops->unmap_page(np->device, page->index,
|
||||
PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
page->index = 0;
|
||||
page->mapping = NULL;
|
||||
niu_next_page(page) = NULL;
|
||||
__free_page(page);
|
||||
rp->rbr_refill_pending++;
|
||||
}
|
||||
@@ -3451,11 +3470,11 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
|
||||
|
||||
niu_rx_skb_append(skb, page, off, append_size, rcr_size);
|
||||
if ((page->index + rp->rbr_block_size) - rcr_size == addr) {
|
||||
*link = (struct page *) page->mapping;
|
||||
*link = niu_next_page(page);
|
||||
np->ops->unmap_page(np->device, page->index,
|
||||
PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
page->index = 0;
|
||||
page->mapping = NULL;
|
||||
niu_next_page(page) = NULL;
|
||||
rp->rbr_refill_pending++;
|
||||
} else
|
||||
get_page(page);
|
||||
@@ -3518,13 +3537,13 @@ static void niu_rbr_free(struct niu *np, struct rx_ring_info *rp)
|
||||
|
||||
page = rp->rxhash[i];
|
||||
while (page) {
|
||||
struct page *next = (struct page *) page->mapping;
|
||||
struct page *next = niu_next_page(page);
|
||||
u64 base = page->index;
|
||||
|
||||
np->ops->unmap_page(np->device, base, PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
page->index = 0;
|
||||
page->mapping = NULL;
|
||||
niu_next_page(page) = NULL;
|
||||
|
||||
__free_page(page);
|
||||
|
||||
@@ -6440,8 +6459,7 @@ static void niu_reset_buffers(struct niu *np)
|
||||
|
||||
page = rp->rxhash[j];
|
||||
while (page) {
|
||||
struct page *next =
|
||||
(struct page *) page->mapping;
|
||||
struct page *next = niu_next_page(page);
|
||||
u64 base = page->index;
|
||||
base = base >> RBR_DESCR_ADDR_SHIFT;
|
||||
rp->rbr[k++] = cpu_to_le32(base);
|
||||
@@ -10176,6 +10194,9 @@ static int __init niu_init(void)
|
||||
|
||||
BUILD_BUG_ON(PAGE_SIZE < 4 * 1024);
|
||||
|
||||
BUILD_BUG_ON(offsetof(struct page, mapping) !=
|
||||
offsetof(union niu_page, next));
|
||||
|
||||
niu_debug = netif_msg_init(debug, NIU_MSG_DEFAULT);
|
||||
|
||||
#ifdef CONFIG_SPARC64
|
||||
|
||||
@@ -142,12 +142,6 @@ config BINFMT_ZFLAT
|
||||
help
|
||||
Support FLAT format compressed binaries
|
||||
|
||||
config BINFMT_SHARED_FLAT
|
||||
bool "Enable shared FLAT support"
|
||||
depends on BINFMT_FLAT
|
||||
help
|
||||
Support FLAT shared libraries
|
||||
|
||||
config HAVE_AOUT
|
||||
def_bool n
|
||||
|
||||
|
||||
+66
-173
@@ -37,7 +37,6 @@
|
||||
#include <linux/flat.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/coredump.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
@@ -69,11 +68,7 @@
|
||||
#define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */
|
||||
#define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */
|
||||
|
||||
#ifdef CONFIG_BINFMT_SHARED_FLAT
|
||||
#define MAX_SHARED_LIBS (4)
|
||||
#else
|
||||
#define MAX_SHARED_LIBS (1)
|
||||
#endif
|
||||
#define MAX_SHARED_LIBS (1)
|
||||
|
||||
#ifdef CONFIG_BINFMT_FLAT_NO_DATA_START_OFFSET
|
||||
#define DATA_START_OFFSET_WORDS (0)
|
||||
@@ -93,38 +88,13 @@ struct lib_info {
|
||||
} lib_list[MAX_SHARED_LIBS];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BINFMT_SHARED_FLAT
|
||||
static int load_flat_shared_library(int id, struct lib_info *p);
|
||||
#endif
|
||||
|
||||
static int load_flat_binary(struct linux_binprm *);
|
||||
#ifdef CONFIG_COREDUMP
|
||||
static int flat_core_dump(struct coredump_params *cprm);
|
||||
#endif
|
||||
|
||||
static struct linux_binfmt flat_format = {
|
||||
.module = THIS_MODULE,
|
||||
.load_binary = load_flat_binary,
|
||||
#ifdef CONFIG_COREDUMP
|
||||
.core_dump = flat_core_dump,
|
||||
.min_coredump = PAGE_SIZE
|
||||
#endif
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
/*
|
||||
* Routine writes a core dump image in the current directory.
|
||||
* Currently only a stub-function.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_COREDUMP
|
||||
static int flat_core_dump(struct coredump_params *cprm)
|
||||
{
|
||||
pr_warn("Process %s:%d received signr %d and should have core dumped\n",
|
||||
current->comm, current->pid, cprm->siginfo->si_signo);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/*
|
||||
@@ -329,51 +299,18 @@ out_free:
|
||||
/****************************************************************************/
|
||||
|
||||
static unsigned long
|
||||
calc_reloc(unsigned long r, struct lib_info *p, int curid, int internalp)
|
||||
calc_reloc(unsigned long r, struct lib_info *p)
|
||||
{
|
||||
unsigned long addr;
|
||||
int id;
|
||||
unsigned long start_brk;
|
||||
unsigned long start_data;
|
||||
unsigned long text_len;
|
||||
unsigned long start_code;
|
||||
|
||||
#ifdef CONFIG_BINFMT_SHARED_FLAT
|
||||
if (r == 0)
|
||||
id = curid; /* Relocs of 0 are always self referring */
|
||||
else {
|
||||
id = (r >> 24) & 0xff; /* Find ID for this reloc */
|
||||
r &= 0x00ffffff; /* Trim ID off here */
|
||||
}
|
||||
if (id >= MAX_SHARED_LIBS) {
|
||||
pr_err("reference 0x%lx to shared library %d", r, id);
|
||||
goto failed;
|
||||
}
|
||||
if (curid != id) {
|
||||
if (internalp) {
|
||||
pr_err("reloc address 0x%lx not in same module "
|
||||
"(%d != %d)", r, curid, id);
|
||||
goto failed;
|
||||
} else if (!p->lib_list[id].loaded &&
|
||||
load_flat_shared_library(id, p) < 0) {
|
||||
pr_err("failed to load library %d", id);
|
||||
goto failed;
|
||||
}
|
||||
/* Check versioning information (i.e. time stamps) */
|
||||
if (p->lib_list[id].build_date && p->lib_list[curid].build_date &&
|
||||
p->lib_list[curid].build_date < p->lib_list[id].build_date) {
|
||||
pr_err("library %d is younger than %d", id, curid);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
#else
|
||||
id = 0;
|
||||
#endif
|
||||
|
||||
start_brk = p->lib_list[id].start_brk;
|
||||
start_data = p->lib_list[id].start_data;
|
||||
start_code = p->lib_list[id].start_code;
|
||||
text_len = p->lib_list[id].text_len;
|
||||
start_brk = p->lib_list[0].start_brk;
|
||||
start_data = p->lib_list[0].start_data;
|
||||
start_code = p->lib_list[0].start_code;
|
||||
text_len = p->lib_list[0].text_len;
|
||||
|
||||
if (r > start_brk - start_data + text_len) {
|
||||
pr_err("reloc outside program 0x%lx (0 - 0x%lx/0x%lx)",
|
||||
@@ -440,8 +377,32 @@ static void old_reloc(unsigned long rl)
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static inline u32 __user *skip_got_header(u32 __user *rp)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_RISCV)) {
|
||||
/*
|
||||
* RISC-V has a 16 byte GOT PLT header for elf64-riscv
|
||||
* and 8 byte GOT PLT header for elf32-riscv.
|
||||
* Skip the whole GOT PLT header, since it is reserved
|
||||
* for the dynamic linker (ld.so).
|
||||
*/
|
||||
u32 rp_val0, rp_val1;
|
||||
|
||||
if (get_user(rp_val0, rp))
|
||||
return rp;
|
||||
if (get_user(rp_val1, rp + 1))
|
||||
return rp;
|
||||
|
||||
if (rp_val0 == 0xffffffff && rp_val1 == 0xffffffff)
|
||||
rp += 4;
|
||||
else if (rp_val0 == 0xffffffff)
|
||||
rp += 2;
|
||||
}
|
||||
return rp;
|
||||
}
|
||||
|
||||
static int load_flat_file(struct linux_binprm *bprm,
|
||||
struct lib_info *libinfo, int id, unsigned long *extra_stack)
|
||||
struct lib_info *libinfo, unsigned long *extra_stack)
|
||||
{
|
||||
struct flat_hdr *hdr;
|
||||
unsigned long textpos, datapos, realdatastart;
|
||||
@@ -493,14 +454,6 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Don't allow old format executables to use shared libraries */
|
||||
if (rev == OLD_FLAT_VERSION && id != 0) {
|
||||
pr_err("shared libraries are not available before rev 0x%lx\n",
|
||||
FLAT_VERSION);
|
||||
ret = -ENOEXEC;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* fix up the flags for the older format, there were all kinds
|
||||
* of endian hacks, this only works for the simple cases
|
||||
@@ -551,15 +504,13 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||
}
|
||||
|
||||
/* Flush all traces of the currently running executable */
|
||||
if (id == 0) {
|
||||
ret = begin_new_exec(bprm);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = begin_new_exec(bprm);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* OK, This is the point of no return */
|
||||
set_personality(PER_LINUX_32BIT);
|
||||
setup_new_exec(bprm);
|
||||
}
|
||||
/* OK, This is the point of no return */
|
||||
set_personality(PER_LINUX_32BIT);
|
||||
setup_new_exec(bprm);
|
||||
|
||||
/*
|
||||
* calculate the extra space we need to map in
|
||||
@@ -739,42 +690,40 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||
text_len -= sizeof(struct flat_hdr); /* the real code len */
|
||||
|
||||
/* The main program needs a little extra setup in the task structure */
|
||||
if (id == 0) {
|
||||
current->mm->start_code = start_code;
|
||||
current->mm->end_code = end_code;
|
||||
current->mm->start_data = datapos;
|
||||
current->mm->end_data = datapos + data_len;
|
||||
/*
|
||||
* set up the brk stuff, uses any slack left in data/bss/stack
|
||||
* allocation. We put the brk after the bss (between the bss
|
||||
* and stack) like other platforms.
|
||||
* Userspace code relies on the stack pointer starting out at
|
||||
* an address right at the end of a page.
|
||||
*/
|
||||
current->mm->start_brk = datapos + data_len + bss_len;
|
||||
current->mm->brk = (current->mm->start_brk + 3) & ~3;
|
||||
current->mm->start_code = start_code;
|
||||
current->mm->end_code = end_code;
|
||||
current->mm->start_data = datapos;
|
||||
current->mm->end_data = datapos + data_len;
|
||||
/*
|
||||
* set up the brk stuff, uses any slack left in data/bss/stack
|
||||
* allocation. We put the brk after the bss (between the bss
|
||||
* and stack) like other platforms.
|
||||
* Userspace code relies on the stack pointer starting out at
|
||||
* an address right at the end of a page.
|
||||
*/
|
||||
current->mm->start_brk = datapos + data_len + bss_len;
|
||||
current->mm->brk = (current->mm->start_brk + 3) & ~3;
|
||||
#ifndef CONFIG_MMU
|
||||
current->mm->context.end_brk = memp + memp_size - stack_len;
|
||||
current->mm->context.end_brk = memp + memp_size - stack_len;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (flags & FLAT_FLAG_KTRACE) {
|
||||
pr_info("Mapping is %lx, Entry point is %x, data_start is %x\n",
|
||||
textpos, 0x00ffffff&ntohl(hdr->entry), ntohl(hdr->data_start));
|
||||
pr_info("%s %s: TEXT=%lx-%lx DATA=%lx-%lx BSS=%lx-%lx\n",
|
||||
id ? "Lib" : "Load", bprm->filename,
|
||||
"Load", bprm->filename,
|
||||
start_code, end_code, datapos, datapos + data_len,
|
||||
datapos + data_len, (datapos + data_len + bss_len + 3) & ~3);
|
||||
}
|
||||
|
||||
/* Store the current module values into the global library structure */
|
||||
libinfo->lib_list[id].start_code = start_code;
|
||||
libinfo->lib_list[id].start_data = datapos;
|
||||
libinfo->lib_list[id].start_brk = datapos + data_len + bss_len;
|
||||
libinfo->lib_list[id].text_len = text_len;
|
||||
libinfo->lib_list[id].loaded = 1;
|
||||
libinfo->lib_list[id].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos;
|
||||
libinfo->lib_list[id].build_date = ntohl(hdr->build_date);
|
||||
libinfo->lib_list[0].start_code = start_code;
|
||||
libinfo->lib_list[0].start_data = datapos;
|
||||
libinfo->lib_list[0].start_brk = datapos + data_len + bss_len;
|
||||
libinfo->lib_list[0].text_len = text_len;
|
||||
libinfo->lib_list[0].loaded = 1;
|
||||
libinfo->lib_list[0].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos;
|
||||
libinfo->lib_list[0].build_date = ntohl(hdr->build_date);
|
||||
|
||||
/*
|
||||
* We just load the allocations into some temporary memory to
|
||||
@@ -789,14 +738,15 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||
* image.
|
||||
*/
|
||||
if (flags & FLAT_FLAG_GOTPIC) {
|
||||
for (rp = (u32 __user *)datapos; ; rp++) {
|
||||
rp = skip_got_header((u32 __user *) datapos);
|
||||
for (; ; rp++) {
|
||||
u32 addr, rp_val;
|
||||
if (get_user(rp_val, rp))
|
||||
return -EFAULT;
|
||||
if (rp_val == 0xffffffff)
|
||||
break;
|
||||
if (rp_val) {
|
||||
addr = calc_reloc(rp_val, libinfo, id, 0);
|
||||
addr = calc_reloc(rp_val, libinfo);
|
||||
if (addr == RELOC_FAILED) {
|
||||
ret = -ENOEXEC;
|
||||
goto err;
|
||||
@@ -832,7 +782,7 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||
return -EFAULT;
|
||||
relval = ntohl(tmp);
|
||||
addr = flat_get_relocate_addr(relval);
|
||||
rp = (u32 __user *)calc_reloc(addr, libinfo, id, 1);
|
||||
rp = (u32 __user *)calc_reloc(addr, libinfo);
|
||||
if (rp == (u32 __user *)RELOC_FAILED) {
|
||||
ret = -ENOEXEC;
|
||||
goto err;
|
||||
@@ -855,7 +805,7 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||
*/
|
||||
addr = ntohl((__force __be32)addr);
|
||||
}
|
||||
addr = calc_reloc(addr, libinfo, id, 0);
|
||||
addr = calc_reloc(addr, libinfo);
|
||||
if (addr == RELOC_FAILED) {
|
||||
ret = -ENOEXEC;
|
||||
goto err;
|
||||
@@ -883,7 +833,7 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||
/* zero the BSS, BRK and stack areas */
|
||||
if (clear_user((void __user *)(datapos + data_len), bss_len +
|
||||
(memp + memp_size - stack_len - /* end brk */
|
||||
libinfo->lib_list[id].start_brk) + /* start brk */
|
||||
libinfo->lib_list[0].start_brk) + /* start brk */
|
||||
stack_len))
|
||||
return -EFAULT;
|
||||
|
||||
@@ -893,49 +843,6 @@ err:
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
#ifdef CONFIG_BINFMT_SHARED_FLAT
|
||||
|
||||
/*
|
||||
* Load a shared library into memory. The library gets its own data
|
||||
* segment (including bss) but not argv/argc/environ.
|
||||
*/
|
||||
|
||||
static int load_flat_shared_library(int id, struct lib_info *libs)
|
||||
{
|
||||
/*
|
||||
* This is a fake bprm struct; only the members "buf", "file" and
|
||||
* "filename" are actually used.
|
||||
*/
|
||||
struct linux_binprm bprm;
|
||||
int res;
|
||||
char buf[16];
|
||||
loff_t pos = 0;
|
||||
|
||||
memset(&bprm, 0, sizeof(bprm));
|
||||
|
||||
/* Create the file name */
|
||||
sprintf(buf, "/lib/lib%d.so", id);
|
||||
|
||||
/* Open the file up */
|
||||
bprm.filename = buf;
|
||||
bprm.file = open_exec(bprm.filename);
|
||||
res = PTR_ERR(bprm.file);
|
||||
if (IS_ERR(bprm.file))
|
||||
return res;
|
||||
|
||||
res = kernel_read(bprm.file, bprm.buf, BINPRM_BUF_SIZE, &pos);
|
||||
|
||||
if (res >= 0)
|
||||
res = load_flat_file(&bprm, libs, id, NULL);
|
||||
|
||||
allow_write_access(bprm.file);
|
||||
fput(bprm.file);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BINFMT_SHARED_FLAT */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
@@ -968,7 +875,7 @@ static int load_flat_binary(struct linux_binprm *bprm)
|
||||
stack_len += (bprm->envc + 1) * sizeof(char *); /* the envp array */
|
||||
stack_len = ALIGN(stack_len, FLAT_STACK_ALIGN);
|
||||
|
||||
res = load_flat_file(bprm, &libinfo, 0, &stack_len);
|
||||
res = load_flat_file(bprm, &libinfo, &stack_len);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
@@ -1013,20 +920,6 @@ static int load_flat_binary(struct linux_binprm *bprm)
|
||||
*/
|
||||
start_addr = libinfo.lib_list[0].entry;
|
||||
|
||||
#ifdef CONFIG_BINFMT_SHARED_FLAT
|
||||
for (i = MAX_SHARED_LIBS-1; i > 0; i--) {
|
||||
if (libinfo.lib_list[i].loaded) {
|
||||
/* Push previos first to call address */
|
||||
unsigned long __user *sp;
|
||||
current->mm->start_stack -= sizeof(unsigned long);
|
||||
sp = (unsigned long __user *)current->mm->start_stack;
|
||||
if (put_user(start_addr, sp))
|
||||
return -EFAULT;
|
||||
start_addr = libinfo.lib_list[i].entry;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FLAT_PLAT_INIT
|
||||
FLAT_PLAT_INIT(regs);
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -202,7 +202,7 @@ static int enable_verity(struct file *filp,
|
||||
const struct fsverity_operations *vops = inode->i_sb->s_vop;
|
||||
struct merkle_tree_params params = { };
|
||||
struct fsverity_descriptor *desc;
|
||||
size_t desc_size = sizeof(*desc) + arg->sig_size;
|
||||
size_t desc_size = struct_size(desc, signature, arg->sig_size);
|
||||
struct fsverity_info *vi;
|
||||
int err;
|
||||
|
||||
@@ -281,7 +281,7 @@ static int enable_verity(struct file *filp,
|
||||
* from disk. This is simpler, and it serves as an extra check that the
|
||||
* metadata we're writing is valid before actually enabling verity.
|
||||
*/
|
||||
vi = fsverity_create_info(inode, desc, desc_size);
|
||||
vi = fsverity_create_info(inode, desc);
|
||||
if (IS_ERR(vi)) {
|
||||
err = PTR_ERR(vi);
|
||||
goto rollback;
|
||||
|
||||
@@ -122,16 +122,14 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
|
||||
const u8 *salt, size_t salt_size);
|
||||
|
||||
struct fsverity_info *fsverity_create_info(const struct inode *inode,
|
||||
struct fsverity_descriptor *desc,
|
||||
size_t desc_size);
|
||||
struct fsverity_descriptor *desc);
|
||||
|
||||
void fsverity_set_info(struct inode *inode, struct fsverity_info *vi);
|
||||
|
||||
void fsverity_free_info(struct fsverity_info *vi);
|
||||
|
||||
int fsverity_get_descriptor(struct inode *inode,
|
||||
struct fsverity_descriptor **desc_ret,
|
||||
size_t *desc_size_ret);
|
||||
struct fsverity_descriptor **desc_ret);
|
||||
|
||||
int __init fsverity_init_info_cache(void);
|
||||
void __init fsverity_exit_info_cache(void);
|
||||
|
||||
+4
-8
@@ -147,8 +147,7 @@ static int compute_file_digest(struct fsverity_hash_alg *hash_alg,
|
||||
* fsverity_descriptor must have already undergone basic validation.
|
||||
*/
|
||||
struct fsverity_info *fsverity_create_info(const struct inode *inode,
|
||||
struct fsverity_descriptor *desc,
|
||||
size_t desc_size)
|
||||
struct fsverity_descriptor *desc)
|
||||
{
|
||||
struct fsverity_info *vi;
|
||||
int err;
|
||||
@@ -264,8 +263,7 @@ static bool validate_fsverity_descriptor(struct inode *inode,
|
||||
* the filesystem, and do basic validation of it.
|
||||
*/
|
||||
int fsverity_get_descriptor(struct inode *inode,
|
||||
struct fsverity_descriptor **desc_ret,
|
||||
size_t *desc_size_ret)
|
||||
struct fsverity_descriptor **desc_ret)
|
||||
{
|
||||
int res;
|
||||
struct fsverity_descriptor *desc;
|
||||
@@ -297,7 +295,6 @@ int fsverity_get_descriptor(struct inode *inode,
|
||||
}
|
||||
|
||||
*desc_ret = desc;
|
||||
*desc_size_ret = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -306,17 +303,16 @@ static int ensure_verity_info(struct inode *inode)
|
||||
{
|
||||
struct fsverity_info *vi = fsverity_get_info(inode);
|
||||
struct fsverity_descriptor *desc;
|
||||
size_t desc_size;
|
||||
int err;
|
||||
|
||||
if (vi)
|
||||
return 0;
|
||||
|
||||
err = fsverity_get_descriptor(inode, &desc, &desc_size);
|
||||
err = fsverity_get_descriptor(inode, &desc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
vi = fsverity_create_info(inode, desc, desc_size);
|
||||
vi = fsverity_create_info(inode, desc);
|
||||
if (IS_ERR(vi)) {
|
||||
err = PTR_ERR(vi);
|
||||
goto out_free_desc;
|
||||
|
||||
@@ -101,7 +101,7 @@ static int fsverity_read_descriptor(struct inode *inode,
|
||||
size_t desc_size;
|
||||
int res;
|
||||
|
||||
res = fsverity_get_descriptor(inode, &desc, &desc_size);
|
||||
res = fsverity_get_descriptor(inode, &desc);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
@@ -119,10 +119,9 @@ static int fsverity_read_signature(struct inode *inode,
|
||||
void __user *buf, u64 offset, int length)
|
||||
{
|
||||
struct fsverity_descriptor *desc;
|
||||
size_t desc_size;
|
||||
int res;
|
||||
|
||||
res = fsverity_get_descriptor(inode, &desc, &desc_size);
|
||||
res = fsverity_get_descriptor(inode, &desc);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
|
||||
@@ -69,6 +69,16 @@
|
||||
#define __nocfi __attribute__((__no_sanitize__("cfi")))
|
||||
#define __cficanonical __attribute__((__cfi_canonical_jump_table__))
|
||||
|
||||
#if defined(CONFIG_CFI_CLANG)
|
||||
/*
|
||||
* With CONFIG_CFI_CLANG, the compiler replaces function address
|
||||
* references with the address of the function's CFI jump table
|
||||
* entry. The function_nocfi macro always returns the address of the
|
||||
* actual function instead.
|
||||
*/
|
||||
#define function_nocfi(x) __builtin_function_start(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Turn individual warnings and errors on and off locally, depending
|
||||
* on version.
|
||||
|
||||
@@ -66,14 +66,6 @@
|
||||
__builtin_unreachable(); \
|
||||
} while (0)
|
||||
|
||||
#if defined(RANDSTRUCT_PLUGIN) && !defined(__CHECKER__)
|
||||
#define __randomize_layout __attribute__((randomize_layout))
|
||||
#define __no_randomize_layout __attribute__((no_randomize_layout))
|
||||
/* This anon struct can add padding, so only enable it under randstruct. */
|
||||
#define randomized_struct_fields_start struct {
|
||||
#define randomized_struct_fields_end } __randomize_layout;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GCC 'asm goto' miscompiles certain code sequences:
|
||||
*
|
||||
|
||||
@@ -242,15 +242,15 @@ struct ftrace_likely_data {
|
||||
# define __latent_entropy
|
||||
#endif
|
||||
|
||||
#ifndef __randomize_layout
|
||||
#if defined(RANDSTRUCT) && !defined(__CHECKER__)
|
||||
# define __randomize_layout __designated_init __attribute__((randomize_layout))
|
||||
# define __no_randomize_layout __attribute__((no_randomize_layout))
|
||||
/* This anon struct can add padding, so only enable it under randstruct. */
|
||||
# define randomized_struct_fields_start struct {
|
||||
# define randomized_struct_fields_end } __randomize_layout;
|
||||
#else
|
||||
# define __randomize_layout __designated_init
|
||||
#endif
|
||||
|
||||
#ifndef __no_randomize_layout
|
||||
# define __no_randomize_layout
|
||||
#endif
|
||||
|
||||
#ifndef randomized_struct_fields_start
|
||||
# define randomized_struct_fields_start
|
||||
# define randomized_struct_fields_end
|
||||
#endif
|
||||
|
||||
@@ -149,6 +149,11 @@ static inline void totalhigh_pages_add(long count)
|
||||
atomic_long_add(count, &_totalhigh_pages);
|
||||
}
|
||||
|
||||
static inline bool is_kmap_addr(const void *x)
|
||||
{
|
||||
unsigned long addr = (unsigned long)x;
|
||||
return addr >= PKMAP_ADDR(0) && addr < PKMAP_ADDR(LAST_PKMAP);
|
||||
}
|
||||
#else /* CONFIG_HIGHMEM */
|
||||
|
||||
static inline struct page *kmap_to_page(void *addr)
|
||||
@@ -234,6 +239,11 @@ static inline void __kunmap_atomic(void *addr)
|
||||
static inline unsigned int nr_free_highpages(void) { return 0; }
|
||||
static inline unsigned long totalhigh_pages(void) { return 0UL; }
|
||||
|
||||
static inline bool is_kmap_addr(const void *x)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HIGHMEM */
|
||||
|
||||
/*
|
||||
|
||||
@@ -295,7 +295,7 @@ extern void netfs_stats_show(struct seq_file *);
|
||||
*/
|
||||
static inline struct netfs_i_context *netfs_i_context(struct inode *inode)
|
||||
{
|
||||
return (struct netfs_i_context *)(inode + 1);
|
||||
return (void *)inode + sizeof(*inode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,7 +307,7 @@ static inline struct netfs_i_context *netfs_i_context(struct inode *inode)
|
||||
*/
|
||||
static inline struct inode *netfs_inode(struct netfs_i_context *ctx)
|
||||
{
|
||||
return ((struct inode *)ctx) - 1;
|
||||
return (void *)ctx - sizeof(struct inode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,10 +40,14 @@ DECLARE_PER_CPU(u32, kstack_offset);
|
||||
*/
|
||||
#define KSTACK_OFFSET_MAX(x) ((x) & 0x3FF)
|
||||
|
||||
/*
|
||||
* These macros must be used during syscall entry when interrupts and
|
||||
/**
|
||||
* add_random_kstack_offset - Increase stack utilization by previously
|
||||
* chosen random offset
|
||||
*
|
||||
* This should be used in the syscall entry path when interrupts and
|
||||
* preempt are disabled, and after user registers have been stored to
|
||||
* the stack.
|
||||
* the stack. For testing the resulting entropy, please see:
|
||||
* tools/testing/selftests/lkdtm/stack-entropy.sh
|
||||
*/
|
||||
#define add_random_kstack_offset() do { \
|
||||
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
|
||||
@@ -55,6 +59,23 @@ DECLARE_PER_CPU(u32, kstack_offset);
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* choose_random_kstack_offset - Choose the random offset for the next
|
||||
* add_random_kstack_offset()
|
||||
*
|
||||
* This should only be used during syscall exit when interrupts and
|
||||
* preempt are disabled. This position in the syscall flow is done to
|
||||
* frustrate attacks from userspace attempting to learn the next offset:
|
||||
* - Maximize the timing uncertainty visible from userspace: if the
|
||||
* offset is chosen at syscall entry, userspace has much more control
|
||||
* over the timing between choosing offsets. "How long will we be in
|
||||
* kernel mode?" tends to be more difficult to predict than "how long
|
||||
* will we be in user mode?"
|
||||
* - Reduce the lifetime of the new offset sitting in memory during
|
||||
* kernel mode execution. Exposure of "thread-local" memory content
|
||||
* (e.g. current, percpu, etc) tends to be easier than arbitrary
|
||||
* location memory exposure.
|
||||
*/
|
||||
#define choose_random_kstack_offset(rand) do { \
|
||||
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
|
||||
&randomize_kstack_offset)) { \
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
SECCOMP_FILTER_FLAG_LOG | \
|
||||
SECCOMP_FILTER_FLAG_SPEC_ALLOW | \
|
||||
SECCOMP_FILTER_FLAG_NEW_LISTENER | \
|
||||
SECCOMP_FILTER_FLAG_TSYNC_ESRCH)
|
||||
SECCOMP_FILTER_FLAG_TSYNC_ESRCH | \
|
||||
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV)
|
||||
|
||||
/* sizeof() the first published struct seccomp_notif_addfd */
|
||||
#define SECCOMP_NOTIFY_ADDFD_SIZE_VER0 24
|
||||
|
||||
@@ -15,9 +15,62 @@
|
||||
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
/*
|
||||
* The lowest address on tsk's stack which we can plausibly erase.
|
||||
*/
|
||||
static __always_inline unsigned long
|
||||
stackleak_task_low_bound(const struct task_struct *tsk)
|
||||
{
|
||||
/*
|
||||
* The lowest unsigned long on the task stack contains STACK_END_MAGIC,
|
||||
* which we must not corrupt.
|
||||
*/
|
||||
return (unsigned long)end_of_stack(tsk) + sizeof(unsigned long);
|
||||
}
|
||||
|
||||
/*
|
||||
* The address immediately after the highest address on tsk's stack which we
|
||||
* can plausibly erase.
|
||||
*/
|
||||
static __always_inline unsigned long
|
||||
stackleak_task_high_bound(const struct task_struct *tsk)
|
||||
{
|
||||
/*
|
||||
* The task's pt_regs lives at the top of the task stack and will be
|
||||
* overwritten by exception entry, so there's no need to erase them.
|
||||
*/
|
||||
return (unsigned long)task_pt_regs(tsk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the address immediately above the poisoned region of the stack, where
|
||||
* that region falls between 'low' (inclusive) and 'high' (exclusive).
|
||||
*/
|
||||
static __always_inline unsigned long
|
||||
stackleak_find_top_of_poison(const unsigned long low, const unsigned long high)
|
||||
{
|
||||
const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
|
||||
unsigned int poison_count = 0;
|
||||
unsigned long poison_high = high;
|
||||
unsigned long sp = high;
|
||||
|
||||
while (sp > low && poison_count < depth) {
|
||||
sp -= sizeof(unsigned long);
|
||||
|
||||
if (*(unsigned long *)sp == STACKLEAK_POISON) {
|
||||
poison_count++;
|
||||
} else {
|
||||
poison_count = 0;
|
||||
poison_high = sp;
|
||||
}
|
||||
}
|
||||
|
||||
return poison_high;
|
||||
}
|
||||
|
||||
static inline void stackleak_task_init(struct task_struct *t)
|
||||
{
|
||||
t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long);
|
||||
t->lowest_stack = stackleak_task_low_bound(t);
|
||||
# ifdef CONFIG_STACKLEAK_METRICS
|
||||
t->prev_lowest_stack = t->lowest_stack;
|
||||
# endif
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
#else
|
||||
#define MODULE_VERMAGIC_MODVERSIONS ""
|
||||
#endif
|
||||
#ifdef RANDSTRUCT_PLUGIN
|
||||
#include <generated/randomize_layout_hash.h>
|
||||
#define MODULE_RANDSTRUCT_PLUGIN "RANDSTRUCT_PLUGIN_" RANDSTRUCT_HASHED_SEED
|
||||
#ifdef RANDSTRUCT
|
||||
#include <generated/randstruct_hash.h>
|
||||
#define MODULE_RANDSTRUCT "RANDSTRUCT_" RANDSTRUCT_HASHED_SEED
|
||||
#else
|
||||
#define MODULE_RANDSTRUCT_PLUGIN
|
||||
#define MODULE_RANDSTRUCT
|
||||
#endif
|
||||
|
||||
#define VERMAGIC_STRING \
|
||||
@@ -44,6 +44,6 @@
|
||||
MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT \
|
||||
MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS \
|
||||
MODULE_ARCH_VERMAGIC \
|
||||
MODULE_RANDSTRUCT_PLUGIN
|
||||
MODULE_RANDSTRUCT
|
||||
|
||||
#endif /* _LINUX_VERMAGIC_H */
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2)
|
||||
#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
|
||||
#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4)
|
||||
/* Received notifications wait in killable state (only respond to fatal signals) */
|
||||
#define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5)
|
||||
|
||||
/*
|
||||
* All BPF programs must return a 32-bit value.
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ unsigned int __read_mostly sysctl_oops_all_cpu_backtrace;
|
||||
|
||||
int panic_on_oops = CONFIG_PANIC_ON_OOPS_VALUE;
|
||||
static unsigned long tainted_mask =
|
||||
IS_ENABLED(CONFIG_GCC_PLUGIN_RANDSTRUCT) ? (1 << TAINT_RANDSTRUCT) : 0;
|
||||
IS_ENABLED(CONFIG_RANDSTRUCT) ? (1 << TAINT_RANDSTRUCT) : 0;
|
||||
static int pause_on_oops;
|
||||
static int pause_on_oops_flag;
|
||||
static DEFINE_SPINLOCK(pause_on_oops_lock);
|
||||
|
||||
+41
-3
@@ -200,6 +200,8 @@ static inline void seccomp_cache_prepare(struct seccomp_filter *sfilter)
|
||||
* the filter can be freed.
|
||||
* @cache: cache of arch/syscall mappings to actions
|
||||
* @log: true if all actions except for SECCOMP_RET_ALLOW should be logged
|
||||
* @wait_killable_recv: Put notifying process in killable state once the
|
||||
* notification is received by the userspace listener.
|
||||
* @prev: points to a previously installed, or inherited, filter
|
||||
* @prog: the BPF program to evaluate
|
||||
* @notif: the struct that holds all notification related information
|
||||
@@ -220,6 +222,7 @@ struct seccomp_filter {
|
||||
refcount_t refs;
|
||||
refcount_t users;
|
||||
bool log;
|
||||
bool wait_killable_recv;
|
||||
struct action_cache cache;
|
||||
struct seccomp_filter *prev;
|
||||
struct bpf_prog *prog;
|
||||
@@ -893,6 +896,10 @@ static long seccomp_attach_filter(unsigned int flags,
|
||||
if (flags & SECCOMP_FILTER_FLAG_LOG)
|
||||
filter->log = true;
|
||||
|
||||
/* Set wait killable flag, if present. */
|
||||
if (flags & SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV)
|
||||
filter->wait_killable_recv = true;
|
||||
|
||||
/*
|
||||
* If there is an existing filter, make it the prev and don't drop its
|
||||
* task reference.
|
||||
@@ -1080,6 +1087,12 @@ static void seccomp_handle_addfd(struct seccomp_kaddfd *addfd, struct seccomp_kn
|
||||
complete(&addfd->completion);
|
||||
}
|
||||
|
||||
static bool should_sleep_killable(struct seccomp_filter *match,
|
||||
struct seccomp_knotif *n)
|
||||
{
|
||||
return match->wait_killable_recv && n->state == SECCOMP_NOTIFY_SENT;
|
||||
}
|
||||
|
||||
static int seccomp_do_user_notification(int this_syscall,
|
||||
struct seccomp_filter *match,
|
||||
const struct seccomp_data *sd)
|
||||
@@ -1100,7 +1113,7 @@ static int seccomp_do_user_notification(int this_syscall,
|
||||
n.data = sd;
|
||||
n.id = seccomp_next_notify_id(match);
|
||||
init_completion(&n.ready);
|
||||
list_add(&n.list, &match->notif->notifications);
|
||||
list_add_tail(&n.list, &match->notif->notifications);
|
||||
INIT_LIST_HEAD(&n.addfd);
|
||||
|
||||
up(&match->notif->request);
|
||||
@@ -1110,11 +1123,25 @@ static int seccomp_do_user_notification(int this_syscall,
|
||||
* This is where we wait for a reply from userspace.
|
||||
*/
|
||||
do {
|
||||
bool wait_killable = should_sleep_killable(match, &n);
|
||||
|
||||
mutex_unlock(&match->notify_lock);
|
||||
err = wait_for_completion_interruptible(&n.ready);
|
||||
if (wait_killable)
|
||||
err = wait_for_completion_killable(&n.ready);
|
||||
else
|
||||
err = wait_for_completion_interruptible(&n.ready);
|
||||
mutex_lock(&match->notify_lock);
|
||||
if (err != 0)
|
||||
|
||||
if (err != 0) {
|
||||
/*
|
||||
* Check to see if the notifcation got picked up and
|
||||
* whether we should switch to wait killable.
|
||||
*/
|
||||
if (!wait_killable && should_sleep_killable(match, &n))
|
||||
continue;
|
||||
|
||||
goto interrupted;
|
||||
}
|
||||
|
||||
addfd = list_first_entry_or_null(&n.addfd,
|
||||
struct seccomp_kaddfd, list);
|
||||
@@ -1484,6 +1511,9 @@ out:
|
||||
mutex_lock(&filter->notify_lock);
|
||||
knotif = find_notification(filter, unotif.id);
|
||||
if (knotif) {
|
||||
/* Reset the process to make sure it's not stuck */
|
||||
if (should_sleep_killable(filter, knotif))
|
||||
complete(&knotif->ready);
|
||||
knotif->state = SECCOMP_NOTIFY_INIT;
|
||||
up(&filter->notif->request);
|
||||
}
|
||||
@@ -1829,6 +1859,14 @@ static long seccomp_set_mode_filter(unsigned int flags,
|
||||
((flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH) == 0))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The SECCOMP_FILTER_FLAG_WAIT_KILLABLE_SENT flag doesn't make sense
|
||||
* without the SECCOMP_FILTER_FLAG_NEW_LISTENER flag.
|
||||
*/
|
||||
if ((flags & SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV) &&
|
||||
((flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) == 0))
|
||||
return -EINVAL;
|
||||
|
||||
/* Prepare the new filter before holding any locks. */
|
||||
prepared = seccomp_prepare_user_filter(filter);
|
||||
if (IS_ERR(prepared))
|
||||
|
||||
+63
-42
@@ -70,59 +70,81 @@ late_initcall(stackleak_sysctls_init);
|
||||
#define skip_erasing() false
|
||||
#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
|
||||
|
||||
asmlinkage void noinstr stackleak_erase(void)
|
||||
static __always_inline void __stackleak_erase(bool on_task_stack)
|
||||
{
|
||||
/* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
|
||||
unsigned long kstack_ptr = current->lowest_stack;
|
||||
unsigned long boundary = (unsigned long)end_of_stack(current);
|
||||
unsigned int poison_count = 0;
|
||||
const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
|
||||
const unsigned long task_stack_low = stackleak_task_low_bound(current);
|
||||
const unsigned long task_stack_high = stackleak_task_high_bound(current);
|
||||
unsigned long erase_low, erase_high;
|
||||
|
||||
if (skip_erasing())
|
||||
return;
|
||||
|
||||
/* Check that 'lowest_stack' value is sane */
|
||||
if (unlikely(kstack_ptr - boundary >= THREAD_SIZE))
|
||||
kstack_ptr = boundary;
|
||||
|
||||
/* 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);
|
||||
erase_low = stackleak_find_top_of_poison(task_stack_low,
|
||||
current->lowest_stack);
|
||||
|
||||
#ifdef CONFIG_STACKLEAK_METRICS
|
||||
current->prev_lowest_stack = kstack_ptr;
|
||||
current->prev_lowest_stack = erase_low;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Write poison to the task's stack between 'erase_low' and
|
||||
* 'erase_high'.
|
||||
*
|
||||
* If we're running on a different stack (e.g. an entry trampoline
|
||||
* stack) we can erase everything below the pt_regs at the top of the
|
||||
* task stack.
|
||||
*
|
||||
* If we're running on the task stack itself, we must not clobber any
|
||||
* stack used by this function and its caller. We assume that this
|
||||
* function has a fixed-size stack frame, and the current stack pointer
|
||||
* doesn't change while we write poison.
|
||||
*/
|
||||
if (on_thread_stack())
|
||||
boundary = current_stack_pointer;
|
||||
if (on_task_stack)
|
||||
erase_high = current_stack_pointer;
|
||||
else
|
||||
boundary = current_top_of_stack();
|
||||
erase_high = task_stack_high;
|
||||
|
||||
while (kstack_ptr < boundary) {
|
||||
*(unsigned long *)kstack_ptr = STACKLEAK_POISON;
|
||||
kstack_ptr += sizeof(unsigned long);
|
||||
while (erase_low < erase_high) {
|
||||
*(unsigned long *)erase_low = STACKLEAK_POISON;
|
||||
erase_low += sizeof(unsigned long);
|
||||
}
|
||||
|
||||
/* Reset the 'lowest_stack' value for the next syscall */
|
||||
current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64;
|
||||
current->lowest_stack = task_stack_high;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase and poison the portion of the task stack used since the last erase.
|
||||
* Can be called from the task stack or an entry stack when the task stack is
|
||||
* no longer in use.
|
||||
*/
|
||||
asmlinkage void noinstr stackleak_erase(void)
|
||||
{
|
||||
if (skip_erasing())
|
||||
return;
|
||||
|
||||
__stackleak_erase(on_thread_stack());
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase and poison the portion of the task stack used since the last erase.
|
||||
* Can only be called from the task stack.
|
||||
*/
|
||||
asmlinkage void noinstr stackleak_erase_on_task_stack(void)
|
||||
{
|
||||
if (skip_erasing())
|
||||
return;
|
||||
|
||||
__stackleak_erase(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase and poison the portion of the task stack used since the last erase.
|
||||
* Can only be called from a stack other than the task stack.
|
||||
*/
|
||||
asmlinkage void noinstr stackleak_erase_off_task_stack(void)
|
||||
{
|
||||
if (skip_erasing())
|
||||
return;
|
||||
|
||||
__stackleak_erase(false);
|
||||
}
|
||||
|
||||
void __used __no_caller_saved_registers noinstr stackleak_track_stack(void)
|
||||
@@ -139,8 +161,7 @@ void __used __no_caller_saved_registers noinstr stackleak_track_stack(void)
|
||||
/* 'lowest_stack' should be aligned on the register width boundary */
|
||||
sp = ALIGN(sp, sizeof(unsigned long));
|
||||
if (sp < current->lowest_stack &&
|
||||
sp >= (unsigned long)task_stack_page(current) +
|
||||
sizeof(unsigned long)) {
|
||||
sp >= stackleak_task_low_bound(current)) {
|
||||
current->lowest_stack = sp;
|
||||
}
|
||||
}
|
||||
|
||||
+30
-73
@@ -17,6 +17,7 @@
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -157,91 +158,47 @@ static inline void check_bogus_address(const unsigned long ptr, unsigned long n,
|
||||
usercopy_abort("null address", NULL, to_user, ptr, n);
|
||||
}
|
||||
|
||||
/* Checks for allocs that are marked in some way as spanning multiple pages. */
|
||||
static inline void check_page_span(const void *ptr, unsigned long n,
|
||||
struct page *page, bool to_user)
|
||||
{
|
||||
#ifdef CONFIG_HARDENED_USERCOPY_PAGESPAN
|
||||
const void *end = ptr + n - 1;
|
||||
struct page *endpage;
|
||||
bool is_reserved, is_cma;
|
||||
|
||||
/*
|
||||
* Sometimes the kernel data regions are not marked Reserved (see
|
||||
* check below). And sometimes [_sdata,_edata) does not cover
|
||||
* rodata and/or bss, so check each range explicitly.
|
||||
*/
|
||||
|
||||
/* Allow reads of kernel rodata region (if not marked as Reserved). */
|
||||
if (ptr >= (const void *)__start_rodata &&
|
||||
end <= (const void *)__end_rodata) {
|
||||
if (!to_user)
|
||||
usercopy_abort("rodata", NULL, to_user, 0, n);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allow kernel data region (if not marked as Reserved). */
|
||||
if (ptr >= (const void *)_sdata && end <= (const void *)_edata)
|
||||
return;
|
||||
|
||||
/* Allow kernel bss region (if not marked as Reserved). */
|
||||
if (ptr >= (const void *)__bss_start &&
|
||||
end <= (const void *)__bss_stop)
|
||||
return;
|
||||
|
||||
/* Is the object wholly within one base page? */
|
||||
if (likely(((unsigned long)ptr & (unsigned long)PAGE_MASK) ==
|
||||
((unsigned long)end & (unsigned long)PAGE_MASK)))
|
||||
return;
|
||||
|
||||
/* Allow if fully inside the same compound (__GFP_COMP) page. */
|
||||
endpage = virt_to_head_page(end);
|
||||
if (likely(endpage == page))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Reject if range is entirely either Reserved (i.e. special or
|
||||
* device memory), or CMA. Otherwise, reject since the object spans
|
||||
* several independently allocated pages.
|
||||
*/
|
||||
is_reserved = PageReserved(page);
|
||||
is_cma = is_migrate_cma_page(page);
|
||||
if (!is_reserved && !is_cma)
|
||||
usercopy_abort("spans multiple pages", NULL, to_user, 0, n);
|
||||
|
||||
for (ptr += PAGE_SIZE; ptr <= end; ptr += PAGE_SIZE) {
|
||||
page = virt_to_head_page(ptr);
|
||||
if (is_reserved && !PageReserved(page))
|
||||
usercopy_abort("spans Reserved and non-Reserved pages",
|
||||
NULL, to_user, 0, n);
|
||||
if (is_cma && !is_migrate_cma_page(page))
|
||||
usercopy_abort("spans CMA and non-CMA pages", NULL,
|
||||
to_user, 0, n);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void check_heap_object(const void *ptr, unsigned long n,
|
||||
bool to_user)
|
||||
{
|
||||
struct folio *folio;
|
||||
|
||||
if (is_kmap_addr(ptr)) {
|
||||
unsigned long page_end = (unsigned long)ptr | (PAGE_SIZE - 1);
|
||||
|
||||
if ((unsigned long)ptr + n - 1 > page_end)
|
||||
usercopy_abort("kmap", NULL, to_user,
|
||||
offset_in_page(ptr), n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_vmalloc_addr(ptr)) {
|
||||
struct vm_struct *area = find_vm_area(ptr);
|
||||
unsigned long offset;
|
||||
|
||||
if (!area) {
|
||||
usercopy_abort("vmalloc", "no area", to_user, 0, n);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = ptr - area->addr;
|
||||
if (offset + n > get_vm_area_size(area))
|
||||
usercopy_abort("vmalloc", NULL, to_user, offset, n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!virt_addr_valid(ptr))
|
||||
return;
|
||||
|
||||
/*
|
||||
* When CONFIG_HIGHMEM=y, kmap_to_page() will give either the
|
||||
* highmem page or fallback to virt_to_page(). The following
|
||||
* is effectively a highmem-aware virt_to_slab().
|
||||
*/
|
||||
folio = page_folio(kmap_to_page((void *)ptr));
|
||||
folio = virt_to_folio(ptr);
|
||||
|
||||
if (folio_test_slab(folio)) {
|
||||
/* Check slab allocator for flags and size. */
|
||||
__check_heap_object(ptr, n, folio_slab(folio), to_user);
|
||||
} else {
|
||||
/* Verify object does not incorrectly span multiple pages. */
|
||||
check_page_span(ptr, n, folio_page(folio, 0), to_user);
|
||||
} else if (folio_test_large(folio)) {
|
||||
unsigned long offset = ptr - folio_address(folio);
|
||||
if (offset + n > folio_size(folio))
|
||||
usercopy_abort("page alloc", NULL, to_user, offset, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -1808,11 +1808,9 @@ static int maybe_init_creds(struct scm_cookie *scm,
|
||||
static bool unix_skb_scm_eq(struct sk_buff *skb,
|
||||
struct scm_cookie *scm)
|
||||
{
|
||||
const struct unix_skb_parms *u = &UNIXCB(skb);
|
||||
|
||||
return u->pid == scm->pid &&
|
||||
uid_eq(u->uid, scm->creds.uid) &&
|
||||
gid_eq(u->gid, scm->creds.gid) &&
|
||||
return UNIXCB(skb).pid == scm->pid &&
|
||||
uid_eq(UNIXCB(skb).uid, scm->creds.uid) &&
|
||||
gid_eq(UNIXCB(skb).gid, scm->creds.gid) &&
|
||||
unix_secdata_eq(scm, skb);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
|
||||
endif
|
||||
export DISABLE_LATENT_ENTROPY_PLUGIN
|
||||
|
||||
gcc-plugin-$(CONFIG_GCC_PLUGIN_SANCOV) += sancov_plugin.so
|
||||
|
||||
gcc-plugin-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += structleak_plugin.so
|
||||
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_VERBOSE) \
|
||||
+= -fplugin-arg-structleak_plugin-verbose
|
||||
@@ -24,12 +22,6 @@ export DISABLE_STRUCTLEAK_PLUGIN
|
||||
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) \
|
||||
+= -DSTRUCTLEAK_PLUGIN
|
||||
|
||||
gcc-plugin-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) += randomize_layout_plugin.so
|
||||
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \
|
||||
+= -DRANDSTRUCT_PLUGIN
|
||||
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) \
|
||||
+= -fplugin-arg-randomize_layout_plugin-performance-mode
|
||||
|
||||
gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so
|
||||
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
|
||||
+= -DSTACKLEAK_PLUGIN
|
||||
@@ -53,13 +45,19 @@ export DISABLE_ARM_SSP_PER_TASK_PLUGIN
|
||||
# All the plugin CFLAGS are collected here in case a build target needs to
|
||||
# filter them out of the KBUILD_CFLAGS.
|
||||
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
|
||||
# The sancov_plugin.so is included via CFLAGS_KCOV, so it is removed here.
|
||||
GCC_PLUGINS_CFLAGS := $(filter-out %/sancov_plugin.so, $(GCC_PLUGINS_CFLAGS))
|
||||
export GCC_PLUGINS_CFLAGS
|
||||
|
||||
# Add the flags to the build!
|
||||
KBUILD_CFLAGS += $(GCC_PLUGINS_CFLAGS)
|
||||
|
||||
# All enabled GCC plugins are collected here for building below.
|
||||
GCC_PLUGIN := $(gcc-plugin-y)
|
||||
# Some plugins are enabled outside of this Makefile, but they still need to
|
||||
# be included in GCC_PLUGIN so they can get built.
|
||||
gcc-plugin-external-$(CONFIG_GCC_PLUGIN_SANCOV) \
|
||||
+= sancov_plugin.so
|
||||
gcc-plugin-external-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \
|
||||
+= randomize_layout_plugin.so
|
||||
|
||||
# All enabled GCC plugins are collected here for building in
|
||||
# scripts/gcc-scripts/Makefile.
|
||||
GCC_PLUGIN := $(gcc-plugin-y) $(gcc-plugin-external-y)
|
||||
export GCC_PLUGIN
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
randstruct-cflags-y += -DRANDSTRUCT
|
||||
|
||||
ifdef CONFIG_GCC_PLUGIN_RANDSTRUCT
|
||||
randstruct-cflags-y \
|
||||
+= -fplugin=$(objtree)/scripts/gcc-plugins/randomize_layout_plugin.so
|
||||
randstruct-cflags-$(CONFIG_RANDSTRUCT_PERFORMANCE) \
|
||||
+= -fplugin-arg-randomize_layout_plugin-performance-mode
|
||||
else
|
||||
randstruct-cflags-y \
|
||||
+= -frandomize-layout-seed-file=$(objtree)/scripts/basic/randstruct.seed
|
||||
endif
|
||||
|
||||
export RANDSTRUCT_CFLAGS := $(randstruct-cflags-y)
|
||||
|
||||
KBUILD_CFLAGS += $(RANDSTRUCT_CFLAGS)
|
||||
@@ -1,2 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
/fixdep
|
||||
/randstruct.seed
|
||||
|
||||
@@ -3,3 +3,14 @@
|
||||
# fixdep: used to generate dependency information during build process
|
||||
|
||||
hostprogs-always-y += fixdep
|
||||
|
||||
# randstruct: the seed is needed before building the gcc-plugin or
|
||||
# before running a Clang kernel build.
|
||||
gen-randstruct-seed := $(srctree)/scripts/gen-randstruct-seed.sh
|
||||
quiet_cmd_create_randstruct_seed = GENSEED $@
|
||||
cmd_create_randstruct_seed = \
|
||||
$(CONFIG_SHELL) $(gen-randstruct-seed) \
|
||||
$@ $(objtree)/include/generated/randstruct_hash.h
|
||||
$(obj)/randstruct.seed: $(gen-randstruct-seed) FORCE
|
||||
$(call if_changed,create_randstruct_seed)
|
||||
always-$(CONFIG_RANDSTRUCT) += randstruct.seed
|
||||
|
||||
@@ -46,44 +46,6 @@ config GCC_PLUGIN_LATENT_ENTROPY
|
||||
* https://grsecurity.net/
|
||||
* https://pax.grsecurity.net/
|
||||
|
||||
config GCC_PLUGIN_RANDSTRUCT
|
||||
bool "Randomize layout of sensitive kernel structures"
|
||||
select MODVERSIONS if MODULES
|
||||
help
|
||||
If you say Y here, the layouts of structures that are entirely
|
||||
function pointers (and have not been manually annotated with
|
||||
__no_randomize_layout), or structures that have been explicitly
|
||||
marked with __randomize_layout, will be randomized at compile-time.
|
||||
This can introduce the requirement of an additional information
|
||||
exposure vulnerability for exploits targeting these structure
|
||||
types.
|
||||
|
||||
Enabling this feature will introduce some performance impact,
|
||||
slightly increase memory usage, and prevent the use of forensic
|
||||
tools like Volatility against the system (unless the kernel
|
||||
source tree isn't cleaned after kernel installation).
|
||||
|
||||
The seed used for compilation is located at
|
||||
scripts/gcc-plugins/randomize_layout_seed.h. It remains after
|
||||
a make clean to allow for external modules to be compiled with
|
||||
the existing seed and will be removed by a make mrproper or
|
||||
make distclean.
|
||||
|
||||
This plugin was ported from grsecurity/PaX. More information at:
|
||||
* https://grsecurity.net/
|
||||
* https://pax.grsecurity.net/
|
||||
|
||||
config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE
|
||||
bool "Use cacheline-aware structure randomization"
|
||||
depends on GCC_PLUGIN_RANDSTRUCT
|
||||
depends on !COMPILE_TEST # do not reduce test coverage
|
||||
help
|
||||
If you say Y here, the RANDSTRUCT randomization will make a
|
||||
best effort at restricting randomization to cacheline-sized
|
||||
groups of elements. It will further not randomize bitfields
|
||||
in structures. This reduces the performance hit of RANDSTRUCT
|
||||
at the cost of weakened randomization.
|
||||
|
||||
config GCC_PLUGIN_ARM_SSP_PER_TASK
|
||||
bool
|
||||
depends on GCC_PLUGINS && ARM
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
$(obj)/randomize_layout_plugin.so: $(objtree)/$(obj)/randomize_layout_seed.h
|
||||
quiet_cmd_create_randomize_layout_seed = GENSEED $@
|
||||
$(obj)/randomize_layout_plugin.so: $(obj)/randomize_layout_seed.h
|
||||
quiet_cmd_create_randomize_layout_seed = SEEDHDR $@
|
||||
cmd_create_randomize_layout_seed = \
|
||||
$(CONFIG_SHELL) $(srctree)/$(src)/gen-random-seed.sh $@ $(objtree)/include/generated/randomize_layout_hash.h
|
||||
$(objtree)/$(obj)/randomize_layout_seed.h: FORCE
|
||||
SEED=$$(cat $(filter-out FORCE,$^) </dev/null); \
|
||||
echo '/*' > $@; \
|
||||
echo ' * This file is automatically generated. Keep it private.' >> $@; \
|
||||
echo ' * Exposing this value will expose the layout of randomized structures.' >> $@; \
|
||||
echo ' */' >> $@; \
|
||||
echo "const char *randstruct_seed = \"$$SEED\";" >> $@
|
||||
$(obj)/randomize_layout_seed.h: $(objtree)/scripts/basic/randstruct.seed FORCE
|
||||
$(call if_changed,create_randomize_layout_seed)
|
||||
targets += randomize_layout_seed.h randomize_layout_hash.h
|
||||
targets += randomize_layout_seed.h
|
||||
|
||||
# Build rules for plugins
|
||||
#
|
||||
@@ -23,10 +28,11 @@ GCC_PLUGINS_DIR = $(shell $(CC) -print-file-name=plugin)
|
||||
|
||||
plugin_cxxflags = -Wp,-MMD,$(depfile) $(KBUILD_HOSTCXXFLAGS) -fPIC \
|
||||
-include $(srctree)/include/linux/compiler-version.h \
|
||||
-I $(GCC_PLUGINS_DIR)/include -I $(obj) -std=gnu++11 \
|
||||
-fno-rtti -fno-exceptions -fasynchronous-unwind-tables \
|
||||
-ggdb -Wno-narrowing -Wno-unused-variable \
|
||||
-Wno-format-diag
|
||||
-include $(objtree)/include/generated/utsrelease.h \
|
||||
-I $(GCC_PLUGINS_DIR)/include -I $(obj) -std=gnu++11 \
|
||||
-fno-rtti -fno-exceptions -fasynchronous-unwind-tables \
|
||||
-ggdb -Wno-narrowing -Wno-unused-variable \
|
||||
-Wno-format-diag
|
||||
|
||||
plugin_ldflags = -shared
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
if [ ! -f "$1" ]; then
|
||||
SEED=`od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n'`
|
||||
echo "const char *randstruct_seed = \"$SEED\";" > "$1"
|
||||
HASH=`echo -n "$SEED" | sha256sum | cut -d" " -f1 | tr -d ' \n'`
|
||||
echo "#define RANDSTRUCT_HASHED_SEED \"$HASH\"" > "$2"
|
||||
fi
|
||||
@@ -82,7 +82,7 @@ __visible int plugin_is_GPL_compatible;
|
||||
static GTY(()) tree latent_entropy_decl;
|
||||
|
||||
static struct plugin_info latent_entropy_plugin_info = {
|
||||
.version = "201606141920vanilla",
|
||||
.version = UTS_RELEASE,
|
||||
.help = "disable\tturn off latent entropy instrumentation\n",
|
||||
};
|
||||
|
||||
|
||||
@@ -34,29 +34,11 @@ __visible int plugin_is_GPL_compatible;
|
||||
static int performance_mode;
|
||||
|
||||
static struct plugin_info randomize_layout_plugin_info = {
|
||||
.version = "201402201816vanilla",
|
||||
.version = UTS_RELEASE,
|
||||
.help = "disable\t\t\tdo not activate plugin\n"
|
||||
"performance-mode\tenable cacheline-aware layout randomization\n"
|
||||
};
|
||||
|
||||
struct whitelist_entry {
|
||||
const char *pathname;
|
||||
const char *lhs;
|
||||
const char *rhs;
|
||||
};
|
||||
|
||||
static const struct whitelist_entry whitelist[] = {
|
||||
/* NIU overloads mapping with page struct */
|
||||
{ "drivers/net/ethernet/sun/niu.c", "page", "address_space" },
|
||||
/* unix_skb_parms via UNIXCB() buffer */
|
||||
{ "net/unix/af_unix.c", "unix_skb_parms", "char" },
|
||||
/* big_key payload.data struct splashing */
|
||||
{ "security/keys/big_key.c", "path", "void *" },
|
||||
/* walk struct security_hook_heads as an array of struct hlist_head */
|
||||
{ "security/security.c", "hlist_head", "security_hook_heads" },
|
||||
{ }
|
||||
};
|
||||
|
||||
/* from old Linux dcache.h */
|
||||
static inline unsigned long
|
||||
partial_name_hash(unsigned long c, unsigned long prevhash)
|
||||
@@ -742,60 +724,6 @@ static void handle_local_var_initializers(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool type_name_eq(gimple stmt, const_tree type_tree, const char *wanted_name)
|
||||
{
|
||||
const char *type_name;
|
||||
|
||||
if (type_tree == NULL_TREE)
|
||||
return false;
|
||||
|
||||
switch (TREE_CODE(type_tree)) {
|
||||
case RECORD_TYPE:
|
||||
type_name = TYPE_NAME_POINTER(type_tree);
|
||||
break;
|
||||
case INTEGER_TYPE:
|
||||
if (TYPE_PRECISION(type_tree) == CHAR_TYPE_SIZE)
|
||||
type_name = "char";
|
||||
else {
|
||||
INFORM(gimple_location(stmt), "found non-char INTEGER_TYPE cast comparison: %qT\n", type_tree);
|
||||
debug_tree(type_tree);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case POINTER_TYPE:
|
||||
if (TREE_CODE(TREE_TYPE(type_tree)) == VOID_TYPE) {
|
||||
type_name = "void *";
|
||||
break;
|
||||
} else {
|
||||
INFORM(gimple_location(stmt), "found non-void POINTER_TYPE cast comparison %qT\n", type_tree);
|
||||
debug_tree(type_tree);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
INFORM(gimple_location(stmt), "unhandled cast comparison: %qT\n", type_tree);
|
||||
debug_tree(type_tree);
|
||||
return false;
|
||||
}
|
||||
|
||||
return strcmp(type_name, wanted_name) == 0;
|
||||
}
|
||||
|
||||
static bool whitelisted_cast(gimple stmt, const_tree lhs_tree, const_tree rhs_tree)
|
||||
{
|
||||
const struct whitelist_entry *entry;
|
||||
expanded_location xloc = expand_location(gimple_location(stmt));
|
||||
|
||||
for (entry = whitelist; entry->pathname; entry++) {
|
||||
if (!strstr(xloc.file, entry->pathname))
|
||||
continue;
|
||||
|
||||
if (type_name_eq(stmt, lhs_tree, entry->lhs) && type_name_eq(stmt, rhs_tree, entry->rhs))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* iterate over all statements to find "bad" casts:
|
||||
* those where the address of the start of a structure is cast
|
||||
@@ -872,10 +800,7 @@ static unsigned int find_bad_casts_execute(void)
|
||||
#ifndef __DEBUG_PLUGIN
|
||||
if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_lhs_type)))
|
||||
#endif
|
||||
{
|
||||
if (!whitelisted_cast(stmt, ptr_lhs_type, ptr_rhs_type))
|
||||
MISMATCH(gimple_location(stmt), "rhs", ptr_lhs_type, ptr_rhs_type);
|
||||
}
|
||||
MISMATCH(gimple_location(stmt), "rhs", ptr_lhs_type, ptr_rhs_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -898,10 +823,7 @@ static unsigned int find_bad_casts_execute(void)
|
||||
#ifndef __DEBUG_PLUGIN
|
||||
if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(op0_type)))
|
||||
#endif
|
||||
{
|
||||
if (!whitelisted_cast(stmt, ptr_lhs_type, op0_type))
|
||||
MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type);
|
||||
}
|
||||
MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type);
|
||||
} else {
|
||||
const_tree ssa_name_var = SSA_NAME_VAR(rhs1);
|
||||
/* skip bogus type casts introduced by container_of */
|
||||
@@ -911,10 +833,7 @@ static unsigned int find_bad_casts_execute(void)
|
||||
#ifndef __DEBUG_PLUGIN
|
||||
if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_rhs_type)))
|
||||
#endif
|
||||
{
|
||||
if (!whitelisted_cast(stmt, ptr_lhs_type, ptr_rhs_type))
|
||||
MISMATCH(gimple_location(stmt), "ssa", ptr_lhs_type, ptr_rhs_type);
|
||||
}
|
||||
MISMATCH(gimple_location(stmt), "ssa", ptr_lhs_type, ptr_rhs_type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ __visible int plugin_is_GPL_compatible;
|
||||
tree sancov_fndecl;
|
||||
|
||||
static struct plugin_info sancov_plugin_info = {
|
||||
.version = "20160402",
|
||||
.version = UTS_RELEASE,
|
||||
.help = "sancov plugin\n",
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ static bool verbose = false;
|
||||
static GTY(()) tree track_function_decl;
|
||||
|
||||
static struct plugin_info stackleak_plugin_info = {
|
||||
.version = "201707101337",
|
||||
.version = UTS_RELEASE,
|
||||
.help = "track-min-size=nn\ttrack stack for functions with a stack frame size >= nn bytes\n"
|
||||
"arch=target_arch\tspecify target build arch\n"
|
||||
"disable\t\tdo not activate the plugin\n"
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
__visible int plugin_is_GPL_compatible;
|
||||
|
||||
static struct plugin_info structleak_plugin_info = {
|
||||
.version = "20190125vanilla",
|
||||
.version = UTS_RELEASE,
|
||||
.help = "disable\tdo not activate plugin\n"
|
||||
"byref\tinit structs passed by reference\n"
|
||||
"byref-all\tinit anything passed by reference\n"
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
SEED=$(od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n')
|
||||
echo "$SEED" > "$1"
|
||||
HASH=$(echo -n "$SEED" | sha256sum | cut -d" " -f1)
|
||||
echo "#define RANDSTRUCT_HASHED_SEED \"$HASH\"" > "$2"
|
||||
+1
-12
@@ -160,20 +160,9 @@ config HARDENED_USERCOPY
|
||||
copy_from_user() functions) by rejecting memory ranges that
|
||||
are larger than the specified heap object, span multiple
|
||||
separately allocated pages, are not on the process stack,
|
||||
or are part of the kernel text. This kills entire classes
|
||||
or are part of the kernel text. This prevents entire classes
|
||||
of heap overflow exploits and similar kernel memory exposures.
|
||||
|
||||
config HARDENED_USERCOPY_PAGESPAN
|
||||
bool "Refuse to copy allocations that span multiple pages"
|
||||
depends on HARDENED_USERCOPY
|
||||
depends on BROKEN
|
||||
help
|
||||
When a multi-page allocation is done without __GFP_COMP,
|
||||
hardened usercopy will reject attempts to copy it. There are,
|
||||
however, several cases of this in the kernel that have not all
|
||||
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
|
||||
|
||||
@@ -266,4 +266,77 @@ config ZERO_CALL_USED_REGS
|
||||
|
||||
endmenu
|
||||
|
||||
config CC_HAS_RANDSTRUCT
|
||||
def_bool $(cc-option,-frandomize-layout-seed-file=/dev/null)
|
||||
|
||||
choice
|
||||
prompt "Randomize layout of sensitive kernel structures"
|
||||
default RANDSTRUCT_FULL if COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT)
|
||||
default RANDSTRUCT_NONE
|
||||
help
|
||||
If you enable this, the layouts of structures that are entirely
|
||||
function pointers (and have not been manually annotated with
|
||||
__no_randomize_layout), or structures that have been explicitly
|
||||
marked with __randomize_layout, will be randomized at compile-time.
|
||||
This can introduce the requirement of an additional information
|
||||
exposure vulnerability for exploits targeting these structure
|
||||
types.
|
||||
|
||||
Enabling this feature will introduce some performance impact,
|
||||
slightly increase memory usage, and prevent the use of forensic
|
||||
tools like Volatility against the system (unless the kernel
|
||||
source tree isn't cleaned after kernel installation).
|
||||
|
||||
The seed used for compilation is in scripts/basic/randomize.seed.
|
||||
It remains after a "make clean" to allow for external modules to
|
||||
be compiled with the existing seed and will be removed by a
|
||||
"make mrproper" or "make distclean". This file should not be made
|
||||
public, or the structure layout can be determined.
|
||||
|
||||
config RANDSTRUCT_NONE
|
||||
bool "Disable structure layout randomization"
|
||||
help
|
||||
Build normally: no structure layout randomization.
|
||||
|
||||
config RANDSTRUCT_FULL
|
||||
bool "Fully randomize structure layout"
|
||||
depends on CC_HAS_RANDSTRUCT || GCC_PLUGINS
|
||||
select MODVERSIONS if MODULES
|
||||
help
|
||||
Fully randomize the member layout of sensitive
|
||||
structures as much as possible, which may have both a
|
||||
memory size and performance impact.
|
||||
|
||||
One difference between the Clang and GCC plugin
|
||||
implementations is the handling of bitfields. The GCC
|
||||
plugin treats them as fully separate variables,
|
||||
introducing sometimes significant padding. Clang tries
|
||||
to keep adjacent bitfields together, but with their bit
|
||||
ordering randomized.
|
||||
|
||||
config RANDSTRUCT_PERFORMANCE
|
||||
bool "Limit randomization of structure layout to cache-lines"
|
||||
depends on GCC_PLUGINS
|
||||
select MODVERSIONS if MODULES
|
||||
help
|
||||
Randomization of sensitive kernel structures will make a
|
||||
best effort at restricting randomization to cacheline-sized
|
||||
groups of members. It will further not randomize bitfields
|
||||
in structures. This reduces the performance hit of RANDSTRUCT
|
||||
at the cost of weakened randomization.
|
||||
endchoice
|
||||
|
||||
config RANDSTRUCT
|
||||
def_bool !RANDSTRUCT_NONE
|
||||
|
||||
config GCC_PLUGIN_RANDSTRUCT
|
||||
def_bool GCC_PLUGINS && RANDSTRUCT
|
||||
help
|
||||
Use GCC plugin to randomize structure layout.
|
||||
|
||||
This plugin was ported from grsecurity/PaX. More
|
||||
information at:
|
||||
* https://grsecurity.net/
|
||||
* https://pax.grsecurity.net/
|
||||
|
||||
endmenu
|
||||
|
||||
+36
-37
@@ -20,12 +20,13 @@
|
||||
/*
|
||||
* Layout of key payload words.
|
||||
*/
|
||||
enum {
|
||||
big_key_data,
|
||||
big_key_path,
|
||||
big_key_path_2nd_part,
|
||||
big_key_len,
|
||||
struct big_key_payload {
|
||||
u8 *data;
|
||||
struct path path;
|
||||
size_t length;
|
||||
};
|
||||
#define to_big_key_payload(payload) \
|
||||
(struct big_key_payload *)((payload).data)
|
||||
|
||||
/*
|
||||
* If the data is under this limit, there's no point creating a shm file to
|
||||
@@ -55,7 +56,7 @@ struct key_type key_type_big_key = {
|
||||
*/
|
||||
int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct path *path = (struct path *)&prep->payload.data[big_key_path];
|
||||
struct big_key_payload *payload = to_big_key_payload(prep->payload);
|
||||
struct file *file;
|
||||
u8 *buf, *enckey;
|
||||
ssize_t written;
|
||||
@@ -63,13 +64,15 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
|
||||
int ret;
|
||||
|
||||
BUILD_BUG_ON(sizeof(*payload) != sizeof(prep->payload.data));
|
||||
|
||||
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set an arbitrary quota */
|
||||
prep->quotalen = 16;
|
||||
|
||||
prep->payload.data[big_key_len] = (void *)(unsigned long)datalen;
|
||||
payload->length = datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
/* Create a shmem file to store the data in. This will permit the data
|
||||
@@ -117,9 +120,9 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
/* Pin the mount and dentry to the key so that we can open it again
|
||||
* later
|
||||
*/
|
||||
prep->payload.data[big_key_data] = enckey;
|
||||
*path = file->f_path;
|
||||
path_get(path);
|
||||
payload->data = enckey;
|
||||
payload->path = file->f_path;
|
||||
path_get(&payload->path);
|
||||
fput(file);
|
||||
kvfree_sensitive(buf, enclen);
|
||||
} else {
|
||||
@@ -129,7 +132,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
prep->payload.data[big_key_data] = data;
|
||||
payload->data = data;
|
||||
memcpy(data, prep->data, prep->datalen);
|
||||
}
|
||||
return 0;
|
||||
@@ -148,12 +151,11 @@ error:
|
||||
*/
|
||||
void big_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&prep->payload.data[big_key_path];
|
||||
struct big_key_payload *payload = to_big_key_payload(prep->payload);
|
||||
|
||||
path_put(path);
|
||||
}
|
||||
kfree_sensitive(prep->payload.data[big_key_data]);
|
||||
if (prep->datalen > BIG_KEY_FILE_THRESHOLD)
|
||||
path_put(&payload->path);
|
||||
kfree_sensitive(payload->data);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -162,13 +164,12 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
*/
|
||||
void big_key_revoke(struct key *key)
|
||||
{
|
||||
struct path *path = (struct path *)&key->payload.data[big_key_path];
|
||||
struct big_key_payload *payload = to_big_key_payload(key->payload);
|
||||
|
||||
/* clear the quota */
|
||||
key_payload_reserve(key, 0);
|
||||
if (key_is_positive(key) &&
|
||||
(size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
|
||||
vfs_truncate(path, 0);
|
||||
if (key_is_positive(key) && payload->length > BIG_KEY_FILE_THRESHOLD)
|
||||
vfs_truncate(&payload->path, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -176,17 +177,15 @@ void big_key_revoke(struct key *key)
|
||||
*/
|
||||
void big_key_destroy(struct key *key)
|
||||
{
|
||||
size_t datalen = (size_t)key->payload.data[big_key_len];
|
||||
struct big_key_payload *payload = to_big_key_payload(key->payload);
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data[big_key_path];
|
||||
|
||||
path_put(path);
|
||||
path->mnt = NULL;
|
||||
path->dentry = NULL;
|
||||
if (payload->length > BIG_KEY_FILE_THRESHOLD) {
|
||||
path_put(&payload->path);
|
||||
payload->path.mnt = NULL;
|
||||
payload->path.dentry = NULL;
|
||||
}
|
||||
kfree_sensitive(key->payload.data[big_key_data]);
|
||||
key->payload.data[big_key_data] = NULL;
|
||||
kfree_sensitive(payload->data);
|
||||
payload->data = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -211,14 +210,14 @@ int big_key_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
*/
|
||||
void big_key_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
size_t datalen = (size_t)key->payload.data[big_key_len];
|
||||
struct big_key_payload *payload = to_big_key_payload(key->payload);
|
||||
|
||||
seq_puts(m, key->description);
|
||||
|
||||
if (key_is_positive(key))
|
||||
seq_printf(m, ": %zu [%s]",
|
||||
datalen,
|
||||
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
|
||||
payload->length,
|
||||
payload->length > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -227,16 +226,16 @@ void big_key_describe(const struct key *key, struct seq_file *m)
|
||||
*/
|
||||
long big_key_read(const struct key *key, char *buffer, size_t buflen)
|
||||
{
|
||||
size_t datalen = (size_t)key->payload.data[big_key_len];
|
||||
struct big_key_payload *payload = to_big_key_payload(key->payload);
|
||||
size_t datalen = payload->length;
|
||||
long ret;
|
||||
|
||||
if (!buffer || buflen < datalen)
|
||||
return datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data[big_key_path];
|
||||
struct file *file;
|
||||
u8 *buf, *enckey = (u8 *)key->payload.data[big_key_data];
|
||||
u8 *buf, *enckey = payload->data;
|
||||
size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
|
||||
loff_t pos = 0;
|
||||
|
||||
@@ -244,7 +243,7 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
file = dentry_open(path, O_RDONLY, current_cred());
|
||||
file = dentry_open(&payload->path, O_RDONLY, current_cred());
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto error;
|
||||
@@ -274,7 +273,7 @@ error:
|
||||
kvfree_sensitive(buf, enclen);
|
||||
} else {
|
||||
ret = datalen;
|
||||
memcpy(buffer, key->payload.data[big_key_data], datalen);
|
||||
memcpy(buffer, payload->data, datalen);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -78,11 +78,8 @@ static void check_pinning_enforcement(struct super_block *mnt_sb)
|
||||
* device, allow sysctl to change modes for testing.
|
||||
*/
|
||||
if (mnt_sb->s_bdev) {
|
||||
char bdev[BDEVNAME_SIZE];
|
||||
|
||||
ro = bdev_read_only(mnt_sb->s_bdev);
|
||||
bdevname(mnt_sb->s_bdev, bdev);
|
||||
pr_info("%s (%u:%u): %s\n", bdev,
|
||||
pr_info("%pg (%u:%u): %s\n", mnt_sb->s_bdev,
|
||||
MAJOR(mnt_sb->s_bdev->bd_dev),
|
||||
MINOR(mnt_sb->s_bdev->bd_dev),
|
||||
ro ? "read-only" : "writable");
|
||||
|
||||
+4
-5
@@ -367,13 +367,12 @@ static void __init ordered_lsm_init(void)
|
||||
|
||||
int __init early_security_init(void)
|
||||
{
|
||||
int i;
|
||||
struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
|
||||
struct lsm_info *lsm;
|
||||
|
||||
for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);
|
||||
i++)
|
||||
INIT_HLIST_HEAD(&list[i]);
|
||||
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
|
||||
INIT_HLIST_HEAD(&security_hook_heads.NAME);
|
||||
#include "linux/lsm_hook_defs.h"
|
||||
#undef LSM_HOOK
|
||||
|
||||
for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
|
||||
if (!lsm->enabled)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
CFLAGS += -Wl,-no-as-needed -Wall -isystem ../../../../usr/include/
|
||||
LDFLAGS += -lpthread
|
||||
LDLIBS += -lcap
|
||||
|
||||
TEST_GEN_PROGS := seccomp_bpf seccomp_benchmark
|
||||
include ../lib.mk
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/kcmp.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/capability.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
@@ -59,6 +60,8 @@
|
||||
#define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
||||
|
||||
#ifndef PR_SET_PTRACER
|
||||
# define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
@@ -268,6 +271,10 @@ struct seccomp_notif_addfd_big {
|
||||
#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4)
|
||||
#endif
|
||||
|
||||
#ifndef SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV
|
||||
#define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5)
|
||||
#endif
|
||||
|
||||
#ifndef seccomp
|
||||
int seccomp(unsigned int op, unsigned int flags, void *args)
|
||||
{
|
||||
@@ -3742,7 +3749,10 @@ TEST(user_notification_fault_recv)
|
||||
struct seccomp_notif req = {};
|
||||
struct seccomp_notif_resp resp = {};
|
||||
|
||||
ASSERT_EQ(unshare(CLONE_NEWUSER), 0);
|
||||
ASSERT_EQ(unshare(CLONE_NEWUSER), 0) {
|
||||
if (errno == EINVAL)
|
||||
SKIP(return, "kernel missing CLONE_NEWUSER support");
|
||||
}
|
||||
|
||||
listener = user_notif_syscall(__NR_getppid,
|
||||
SECCOMP_FILTER_FLAG_NEW_LISTENER);
|
||||
@@ -4231,6 +4241,421 @@ TEST(user_notification_addfd_rlimit)
|
||||
close(memfd);
|
||||
}
|
||||
|
||||
/* Make sure PTRACE_O_SUSPEND_SECCOMP requires CAP_SYS_ADMIN. */
|
||||
FIXTURE(O_SUSPEND_SECCOMP) {
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(O_SUSPEND_SECCOMP)
|
||||
{
|
||||
ERRNO_FILTER(block_read, E2BIG);
|
||||
cap_value_t cap_list[] = { CAP_SYS_ADMIN };
|
||||
cap_t caps;
|
||||
|
||||
self->pid = 0;
|
||||
|
||||
/* make sure we don't have CAP_SYS_ADMIN */
|
||||
caps = cap_get_proc();
|
||||
ASSERT_NE(NULL, caps);
|
||||
ASSERT_EQ(0, cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_CLEAR));
|
||||
ASSERT_EQ(0, cap_set_proc(caps));
|
||||
cap_free(caps);
|
||||
|
||||
ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
|
||||
ASSERT_EQ(0, prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_block_read));
|
||||
|
||||
self->pid = fork();
|
||||
ASSERT_GE(self->pid, 0);
|
||||
|
||||
if (self->pid == 0) {
|
||||
while (1)
|
||||
pause();
|
||||
_exit(127);
|
||||
}
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(O_SUSPEND_SECCOMP)
|
||||
{
|
||||
if (self->pid)
|
||||
kill(self->pid, SIGKILL);
|
||||
}
|
||||
|
||||
TEST_F(O_SUSPEND_SECCOMP, setoptions)
|
||||
{
|
||||
int wstatus;
|
||||
|
||||
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, self->pid, NULL, 0));
|
||||
ASSERT_EQ(self->pid, wait(&wstatus));
|
||||
ASSERT_EQ(-1, ptrace(PTRACE_SETOPTIONS, self->pid, NULL, PTRACE_O_SUSPEND_SECCOMP));
|
||||
if (errno == EINVAL)
|
||||
SKIP(return, "Kernel does not support PTRACE_O_SUSPEND_SECCOMP (missing CONFIG_CHECKPOINT_RESTORE?)");
|
||||
ASSERT_EQ(EPERM, errno);
|
||||
}
|
||||
|
||||
TEST_F(O_SUSPEND_SECCOMP, seize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ptrace(PTRACE_SEIZE, self->pid, NULL, PTRACE_O_SUSPEND_SECCOMP);
|
||||
ASSERT_EQ(-1, ret);
|
||||
if (errno == EINVAL)
|
||||
SKIP(return, "Kernel does not support PTRACE_O_SUSPEND_SECCOMP (missing CONFIG_CHECKPOINT_RESTORE?)");
|
||||
ASSERT_EQ(EPERM, errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_nth - Get the nth, space separated entry in a file.
|
||||
*
|
||||
* Returns the length of the read field.
|
||||
* Throws error if field is zero-lengthed.
|
||||
*/
|
||||
static ssize_t get_nth(struct __test_metadata *_metadata, const char *path,
|
||||
const unsigned int position, char **entry)
|
||||
{
|
||||
char *line = NULL;
|
||||
unsigned int i;
|
||||
ssize_t nread;
|
||||
size_t len = 0;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(path, "r");
|
||||
ASSERT_NE(f, NULL) {
|
||||
TH_LOG("Could not open %s: %s", path, strerror(errno));
|
||||
}
|
||||
|
||||
for (i = 0; i < position; i++) {
|
||||
nread = getdelim(&line, &len, ' ', f);
|
||||
ASSERT_GE(nread, 0) {
|
||||
TH_LOG("Failed to read %d entry in file %s", i, path);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
ASSERT_GT(nread, 0) {
|
||||
TH_LOG("Entry in file %s had zero length", path);
|
||||
}
|
||||
|
||||
*entry = line;
|
||||
return nread - 1;
|
||||
}
|
||||
|
||||
/* For a given PID, get the task state (D, R, etc...) */
|
||||
static char get_proc_stat(struct __test_metadata *_metadata, pid_t pid)
|
||||
{
|
||||
char proc_path[100] = {0};
|
||||
char status;
|
||||
char *line;
|
||||
|
||||
snprintf(proc_path, sizeof(proc_path), "/proc/%d/stat", pid);
|
||||
ASSERT_EQ(get_nth(_metadata, proc_path, 3, &line), 1);
|
||||
|
||||
status = *line;
|
||||
free(line);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
TEST(user_notification_fifo)
|
||||
{
|
||||
struct seccomp_notif_resp resp = {};
|
||||
struct seccomp_notif req = {};
|
||||
int i, status, listener;
|
||||
pid_t pid, pids[3];
|
||||
__u64 baseid;
|
||||
long ret;
|
||||
/* 100 ms */
|
||||
struct timespec delay = { .tv_nsec = 100000000 };
|
||||
|
||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
ASSERT_EQ(0, ret) {
|
||||
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
|
||||
}
|
||||
|
||||
/* Setup a listener */
|
||||
listener = user_notif_syscall(__NR_getppid,
|
||||
SECCOMP_FILTER_FLAG_NEW_LISTENER);
|
||||
ASSERT_GE(listener, 0);
|
||||
|
||||
pid = fork();
|
||||
ASSERT_GE(pid, 0);
|
||||
|
||||
if (pid == 0) {
|
||||
ret = syscall(__NR_getppid);
|
||||
exit(ret != USER_NOTIF_MAGIC);
|
||||
}
|
||||
|
||||
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0);
|
||||
baseid = req.id + 1;
|
||||
|
||||
resp.id = req.id;
|
||||
resp.error = 0;
|
||||
resp.val = USER_NOTIF_MAGIC;
|
||||
|
||||
/* check that we make sure flags == 0 */
|
||||
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0);
|
||||
|
||||
EXPECT_EQ(waitpid(pid, &status, 0), pid);
|
||||
EXPECT_EQ(true, WIFEXITED(status));
|
||||
EXPECT_EQ(0, WEXITSTATUS(status));
|
||||
|
||||
/* Start children, and generate notifications */
|
||||
for (i = 0; i < ARRAY_SIZE(pids); i++) {
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
ret = syscall(__NR_getppid);
|
||||
exit(ret != USER_NOTIF_MAGIC);
|
||||
}
|
||||
pids[i] = pid;
|
||||
}
|
||||
|
||||
/* This spins until all of the children are sleeping */
|
||||
restart_wait:
|
||||
for (i = 0; i < ARRAY_SIZE(pids); i++) {
|
||||
if (get_proc_stat(_metadata, pids[i]) != 'S') {
|
||||
nanosleep(&delay, NULL);
|
||||
goto restart_wait;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the notifications in order (and respond) */
|
||||
for (i = 0; i < ARRAY_SIZE(pids); i++) {
|
||||
memset(&req, 0, sizeof(req));
|
||||
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0);
|
||||
EXPECT_EQ(req.id, baseid + i);
|
||||
resp.id = req.id;
|
||||
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0);
|
||||
}
|
||||
|
||||
/* Make sure notifications were received */
|
||||
for (i = 0; i < ARRAY_SIZE(pids); i++) {
|
||||
EXPECT_EQ(waitpid(pids[i], &status, 0), pids[i]);
|
||||
EXPECT_EQ(true, WIFEXITED(status));
|
||||
EXPECT_EQ(0, WEXITSTATUS(status));
|
||||
}
|
||||
}
|
||||
|
||||
/* get_proc_syscall - Get the syscall in progress for a given pid
|
||||
*
|
||||
* Returns the current syscall number for a given process
|
||||
* Returns -1 if not in syscall (running or blocked)
|
||||
*/
|
||||
static long get_proc_syscall(struct __test_metadata *_metadata, int pid)
|
||||
{
|
||||
char proc_path[100] = {0};
|
||||
long ret = -1;
|
||||
ssize_t nread;
|
||||
char *line;
|
||||
|
||||
snprintf(proc_path, sizeof(proc_path), "/proc/%d/syscall", pid);
|
||||
nread = get_nth(_metadata, proc_path, 1, &line);
|
||||
ASSERT_GT(nread, 0);
|
||||
|
||||
if (!strncmp("running", line, MIN(7, nread)))
|
||||
ret = strtol(line, NULL, 16);
|
||||
|
||||
free(line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Ensure non-fatal signals prior to receive are unmodified */
|
||||
TEST(user_notification_wait_killable_pre_notification)
|
||||
{
|
||||
struct sigaction new_action = {
|
||||
.sa_handler = signal_handler,
|
||||
};
|
||||
int listener, status, sk_pair[2];
|
||||
pid_t pid;
|
||||
long ret;
|
||||
char c;
|
||||
/* 100 ms */
|
||||
struct timespec delay = { .tv_nsec = 100000000 };
|
||||
|
||||
ASSERT_EQ(sigemptyset(&new_action.sa_mask), 0);
|
||||
|
||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
ASSERT_EQ(0, ret)
|
||||
{
|
||||
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
|
||||
}
|
||||
|
||||
ASSERT_EQ(socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair), 0);
|
||||
|
||||
listener = user_notif_syscall(
|
||||
__NR_getppid, SECCOMP_FILTER_FLAG_NEW_LISTENER |
|
||||
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV);
|
||||
ASSERT_GE(listener, 0);
|
||||
|
||||
/*
|
||||
* Check that we can kill the process with SIGUSR1 prior to receiving
|
||||
* the notification. SIGUSR1 is wired up to a custom signal handler,
|
||||
* and make sure it gets called.
|
||||
*/
|
||||
pid = fork();
|
||||
ASSERT_GE(pid, 0);
|
||||
|
||||
if (pid == 0) {
|
||||
close(sk_pair[0]);
|
||||
handled = sk_pair[1];
|
||||
|
||||
/* Setup the non-fatal sigaction without SA_RESTART */
|
||||
if (sigaction(SIGUSR1, &new_action, NULL)) {
|
||||
perror("sigaction");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = syscall(__NR_getppid);
|
||||
/* Make sure we got a return from a signal interruption */
|
||||
exit(ret != -1 || errno != EINTR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we've gotten to the seccomp user notification wait
|
||||
* from getppid prior to sending any signals
|
||||
*/
|
||||
while (get_proc_syscall(_metadata, pid) != __NR_getppid &&
|
||||
get_proc_stat(_metadata, pid) != 'S')
|
||||
nanosleep(&delay, NULL);
|
||||
|
||||
/* Send non-fatal kill signal */
|
||||
EXPECT_EQ(kill(pid, SIGUSR1), 0);
|
||||
|
||||
/* wait for process to exit (exit checks for EINTR) */
|
||||
EXPECT_EQ(waitpid(pid, &status, 0), pid);
|
||||
EXPECT_EQ(true, WIFEXITED(status));
|
||||
EXPECT_EQ(0, WEXITSTATUS(status));
|
||||
|
||||
EXPECT_EQ(read(sk_pair[0], &c, 1), 1);
|
||||
}
|
||||
|
||||
/* Ensure non-fatal signals after receive are blocked */
|
||||
TEST(user_notification_wait_killable)
|
||||
{
|
||||
struct sigaction new_action = {
|
||||
.sa_handler = signal_handler,
|
||||
};
|
||||
struct seccomp_notif_resp resp = {};
|
||||
struct seccomp_notif req = {};
|
||||
int listener, status, sk_pair[2];
|
||||
pid_t pid;
|
||||
long ret;
|
||||
char c;
|
||||
/* 100 ms */
|
||||
struct timespec delay = { .tv_nsec = 100000000 };
|
||||
|
||||
ASSERT_EQ(sigemptyset(&new_action.sa_mask), 0);
|
||||
|
||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
ASSERT_EQ(0, ret)
|
||||
{
|
||||
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
|
||||
}
|
||||
|
||||
ASSERT_EQ(socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair), 0);
|
||||
|
||||
listener = user_notif_syscall(
|
||||
__NR_getppid, SECCOMP_FILTER_FLAG_NEW_LISTENER |
|
||||
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV);
|
||||
ASSERT_GE(listener, 0);
|
||||
|
||||
pid = fork();
|
||||
ASSERT_GE(pid, 0);
|
||||
|
||||
if (pid == 0) {
|
||||
close(sk_pair[0]);
|
||||
handled = sk_pair[1];
|
||||
|
||||
/* Setup the sigaction without SA_RESTART */
|
||||
if (sigaction(SIGUSR1, &new_action, NULL)) {
|
||||
perror("sigaction");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Make sure that the syscall is completed (no EINTR) */
|
||||
ret = syscall(__NR_getppid);
|
||||
exit(ret != USER_NOTIF_MAGIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the notification, to make move the notifying process into a
|
||||
* non-preemptible (TASK_KILLABLE) state.
|
||||
*/
|
||||
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0);
|
||||
/* Send non-fatal kill signal */
|
||||
EXPECT_EQ(kill(pid, SIGUSR1), 0);
|
||||
|
||||
/*
|
||||
* Make sure the task enters moves to TASK_KILLABLE by waiting for
|
||||
* D (Disk Sleep) state after receiving non-fatal signal.
|
||||
*/
|
||||
while (get_proc_stat(_metadata, pid) != 'D')
|
||||
nanosleep(&delay, NULL);
|
||||
|
||||
resp.id = req.id;
|
||||
resp.val = USER_NOTIF_MAGIC;
|
||||
/* Make sure the notification is found and able to be replied to */
|
||||
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0);
|
||||
|
||||
/*
|
||||
* Make sure that the signal handler does get called once we're back in
|
||||
* userspace.
|
||||
*/
|
||||
EXPECT_EQ(read(sk_pair[0], &c, 1), 1);
|
||||
/* wait for process to exit (exit checks for USER_NOTIF_MAGIC) */
|
||||
EXPECT_EQ(waitpid(pid, &status, 0), pid);
|
||||
EXPECT_EQ(true, WIFEXITED(status));
|
||||
EXPECT_EQ(0, WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
/* Ensure fatal signals after receive are not blocked */
|
||||
TEST(user_notification_wait_killable_fatal)
|
||||
{
|
||||
struct seccomp_notif req = {};
|
||||
int listener, status;
|
||||
pid_t pid;
|
||||
long ret;
|
||||
/* 100 ms */
|
||||
struct timespec delay = { .tv_nsec = 100000000 };
|
||||
|
||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
ASSERT_EQ(0, ret)
|
||||
{
|
||||
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
|
||||
}
|
||||
|
||||
listener = user_notif_syscall(
|
||||
__NR_getppid, SECCOMP_FILTER_FLAG_NEW_LISTENER |
|
||||
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV);
|
||||
ASSERT_GE(listener, 0);
|
||||
|
||||
pid = fork();
|
||||
ASSERT_GE(pid, 0);
|
||||
|
||||
if (pid == 0) {
|
||||
/* This should never complete as it should get a SIGTERM */
|
||||
syscall(__NR_getppid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (get_proc_stat(_metadata, pid) != 'S')
|
||||
nanosleep(&delay, NULL);
|
||||
|
||||
/*
|
||||
* Get the notification, to make move the notifying process into a
|
||||
* non-preemptible (TASK_KILLABLE) state.
|
||||
*/
|
||||
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0);
|
||||
/* Kill the process with a fatal signal */
|
||||
EXPECT_EQ(kill(pid, SIGTERM), 0);
|
||||
|
||||
/*
|
||||
* Wait for the process to exit, and make sure the process terminated
|
||||
* due to the SIGTERM signal.
|
||||
*/
|
||||
EXPECT_EQ(waitpid(pid, &status, 0), pid);
|
||||
EXPECT_EQ(true, WIFSIGNALED(status));
|
||||
EXPECT_EQ(SIGTERM, WTERMSIG(status));
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - expand NNP testing
|
||||
|
||||
Reference in New Issue
Block a user