Merge tag 'hardening-v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull hardening updates from Kees Cook:
- Fix Sparse warnings with randomizd kstack (GONG, Ruiqi)
- Replace uintptr_t with unsigned long in usercopy (Jason A. Donenfeld)
- Fix Clang -Wforward warning in LKDTM (Justin Stitt)
- Fix comment to correctly refer to STRICT_DEVMEM (Lukas Bulwahn)
- Introduce dm-verity binding logic to LoadPin LSM (Matthias Kaehlcke)
- Clean up warnings and overflow and KASAN tests (Kees Cook)
* tag 'hardening-v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
dm: verity-loadpin: Drop use of dm_table_get_num_targets()
kasan: test: Silence GCC 12 warnings
drivers: lkdtm: fix clang -Wformat warning
x86: mm: refer to the intended config STRICT_DEVMEM in a comment
dm: verity-loadpin: Use CONFIG_SECURITY_LOADPIN_VERITY for conditional compilation
LoadPin: Enable loading from trusted dm-verity devices
dm: Add verity helpers for LoadPin
stack: Declare {randomize_,}kstack_offset to fix Sparse warnings
lib: overflow: Do not define 64-bit tests on 32-bit
MAINTAINERS: Add a general "kernel hardening" section
usercopy: use unsigned long instead of uintptr_t
This commit is contained in:
+17
-4
@@ -4998,7 +4998,7 @@ R: Nick Desaulniers <ndesaulniers@google.com>
|
|||||||
L: llvm@lists.linux.dev
|
L: llvm@lists.linux.dev
|
||||||
S: Supported
|
S: Supported
|
||||||
B: https://github.com/ClangBuiltLinux/linux/issues
|
B: https://github.com/ClangBuiltLinux/linux/issues
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/clang/features
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
|
||||||
F: include/linux/cfi.h
|
F: include/linux/cfi.h
|
||||||
F: kernel/cfi.c
|
F: kernel/cfi.c
|
||||||
|
|
||||||
@@ -7909,6 +7909,7 @@ FORTIFY_SOURCE
|
|||||||
M: Kees Cook <keescook@chromium.org>
|
M: Kees Cook <keescook@chromium.org>
|
||||||
L: linux-hardening@vger.kernel.org
|
L: linux-hardening@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
|
||||||
F: include/linux/fortify-string.h
|
F: include/linux/fortify-string.h
|
||||||
F: lib/test_fortify/*
|
F: lib/test_fortify/*
|
||||||
F: scripts/test_fortify.sh
|
F: scripts/test_fortify.sh
|
||||||
@@ -8351,6 +8352,7 @@ GCC PLUGINS
|
|||||||
M: Kees Cook <keescook@chromium.org>
|
M: Kees Cook <keescook@chromium.org>
|
||||||
L: linux-hardening@vger.kernel.org
|
L: linux-hardening@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
|
||||||
F: Documentation/kbuild/gcc-plugins.rst
|
F: Documentation/kbuild/gcc-plugins.rst
|
||||||
F: scripts/Makefile.gcc-plugins
|
F: scripts/Makefile.gcc-plugins
|
||||||
F: scripts/gcc-plugins/
|
F: scripts/gcc-plugins/
|
||||||
@@ -10878,6 +10880,17 @@ F: scripts/mk*
|
|||||||
F: scripts/mod/
|
F: scripts/mod/
|
||||||
F: scripts/package/
|
F: scripts/package/
|
||||||
|
|
||||||
|
KERNEL HARDENING (not covered by other areas)
|
||||||
|
M: Kees Cook <keescook@chromium.org>
|
||||||
|
L: linux-hardening@vger.kernel.org
|
||||||
|
S: Supported
|
||||||
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
|
||||||
|
F: include/linux/overflow.h
|
||||||
|
F: include/linux/randomize_kstack.h
|
||||||
|
F: mm/usercopy.c
|
||||||
|
K: \b(add|choose)_random_kstack_offset\b
|
||||||
|
K: \b__check_(object_size|heap_object)\b
|
||||||
|
|
||||||
KERNEL JANITORS
|
KERNEL JANITORS
|
||||||
L: kernel-janitors@vger.kernel.org
|
L: kernel-janitors@vger.kernel.org
|
||||||
S: Odd Fixes
|
S: Odd Fixes
|
||||||
@@ -11688,7 +11701,7 @@ F: drivers/media/usb/dvb-usb-v2/lmedm04*
|
|||||||
LOADPIN SECURITY MODULE
|
LOADPIN SECURITY MODULE
|
||||||
M: Kees Cook <keescook@chromium.org>
|
M: Kees Cook <keescook@chromium.org>
|
||||||
S: Supported
|
S: Supported
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
|
||||||
F: Documentation/admin-guide/LSM/LoadPin.rst
|
F: Documentation/admin-guide/LSM/LoadPin.rst
|
||||||
F: security/loadpin/
|
F: security/loadpin/
|
||||||
|
|
||||||
@@ -18026,7 +18039,7 @@ M: Kees Cook <keescook@chromium.org>
|
|||||||
R: Andy Lutomirski <luto@amacapital.net>
|
R: Andy Lutomirski <luto@amacapital.net>
|
||||||
R: Will Drewry <wad@chromium.org>
|
R: Will Drewry <wad@chromium.org>
|
||||||
S: Supported
|
S: Supported
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/seccomp
|
||||||
F: Documentation/userspace-api/seccomp_filter.rst
|
F: Documentation/userspace-api/seccomp_filter.rst
|
||||||
F: include/linux/seccomp.h
|
F: include/linux/seccomp.h
|
||||||
F: include/uapi/linux/seccomp.h
|
F: include/uapi/linux/seccomp.h
|
||||||
@@ -22174,7 +22187,7 @@ F: include/linux/yam.h
|
|||||||
YAMA SECURITY MODULE
|
YAMA SECURITY MODULE
|
||||||
M: Kees Cook <keescook@chromium.org>
|
M: Kees Cook <keescook@chromium.org>
|
||||||
S: Supported
|
S: Supported
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
|
||||||
F: Documentation/admin-guide/LSM/Yama.rst
|
F: Documentation/admin-guide/LSM/Yama.rst
|
||||||
F: security/yama/
|
F: security/yama/
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
|
|||||||
obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
|
obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
|
||||||
obj-$(CONFIG_DM_ZONED) += dm-zoned.o
|
obj-$(CONFIG_DM_ZONED) += dm-zoned.o
|
||||||
obj-$(CONFIG_DM_WRITECACHE) += dm-writecache.o
|
obj-$(CONFIG_DM_WRITECACHE) += dm-writecache.o
|
||||||
|
obj-$(CONFIG_SECURITY_LOADPIN_VERITY) += dm-verity-loadpin.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_DM_INIT),y)
|
ifeq ($(CONFIG_DM_INIT),y)
|
||||||
dm-mod-objs += dm-init.o
|
dm-mod-objs += dm-init.o
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/dm-verity-loadpin.h>
|
||||||
|
|
||||||
|
#include "dm.h"
|
||||||
|
#include "dm-core.h"
|
||||||
|
#include "dm-verity.h"
|
||||||
|
|
||||||
|
#define DM_MSG_PREFIX "verity-loadpin"
|
||||||
|
|
||||||
|
LIST_HEAD(dm_verity_loadpin_trusted_root_digests);
|
||||||
|
|
||||||
|
static bool is_trusted_verity_target(struct dm_target *ti)
|
||||||
|
{
|
||||||
|
u8 *root_digest;
|
||||||
|
unsigned int digest_size;
|
||||||
|
struct dm_verity_loadpin_trusted_root_digest *trd;
|
||||||
|
bool trusted = false;
|
||||||
|
|
||||||
|
if (!dm_is_verity_target(ti))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (dm_verity_get_root_digest(ti, &root_digest, &digest_size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
list_for_each_entry(trd, &dm_verity_loadpin_trusted_root_digests, node) {
|
||||||
|
if ((trd->len == digest_size) &&
|
||||||
|
!memcmp(trd->data, root_digest, digest_size)) {
|
||||||
|
trusted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(root_digest);
|
||||||
|
|
||||||
|
return trusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determines whether the file system of a superblock is located on
|
||||||
|
* a verity device that is trusted by LoadPin.
|
||||||
|
*/
|
||||||
|
bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev)
|
||||||
|
{
|
||||||
|
struct mapped_device *md;
|
||||||
|
struct dm_table *table;
|
||||||
|
struct dm_target *ti;
|
||||||
|
int srcu_idx;
|
||||||
|
bool trusted = false;
|
||||||
|
|
||||||
|
if (list_empty(&dm_verity_loadpin_trusted_root_digests))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
md = dm_get_md(bdev->bd_dev);
|
||||||
|
if (!md)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
table = dm_get_live_table(md, &srcu_idx);
|
||||||
|
|
||||||
|
if (table->num_targets != 1)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ti = dm_table_get_target(table, 0);
|
||||||
|
|
||||||
|
if (is_trusted_verity_target(ti))
|
||||||
|
trusted = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
dm_put_live_table(md, srcu_idx);
|
||||||
|
dm_put(md);
|
||||||
|
|
||||||
|
return trusted;
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
|
||||||
#define DM_MSG_PREFIX "verity"
|
#define DM_MSG_PREFIX "verity"
|
||||||
|
|
||||||
@@ -1309,10 +1310,40 @@ bad:
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether a DM target is a verity target.
|
||||||
|
*/
|
||||||
|
bool dm_is_verity_target(struct dm_target *ti)
|
||||||
|
{
|
||||||
|
return ti->type->module == THIS_MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the root digest of a verity target.
|
||||||
|
*
|
||||||
|
* Returns a copy of the root digest, the caller is responsible for
|
||||||
|
* freeing the memory of the digest.
|
||||||
|
*/
|
||||||
|
int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest, unsigned int *digest_size)
|
||||||
|
{
|
||||||
|
struct dm_verity *v = ti->private;
|
||||||
|
|
||||||
|
if (!dm_is_verity_target(ti))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*root_digest = kmemdup(v->root_digest, v->digest_size, GFP_KERNEL);
|
||||||
|
if (*root_digest == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*digest_size = v->digest_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct target_type verity_target = {
|
static struct target_type verity_target = {
|
||||||
.name = "verity",
|
.name = "verity",
|
||||||
.features = DM_TARGET_IMMUTABLE,
|
.features = DM_TARGET_IMMUTABLE,
|
||||||
.version = {1, 8, 0},
|
.version = {1, 8, 1},
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.ctr = verity_ctr,
|
.ctr = verity_ctr,
|
||||||
.dtr = verity_dtr,
|
.dtr = verity_dtr,
|
||||||
|
|||||||
@@ -129,4 +129,8 @@ extern int verity_hash(struct dm_verity *v, struct ahash_request *req,
|
|||||||
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
|
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
|
||||||
sector_t block, u8 *digest, bool *is_zero);
|
sector_t block, u8 *digest, bool *is_zero);
|
||||||
|
|
||||||
|
extern bool dm_is_verity_target(struct dm_target *ti);
|
||||||
|
extern int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest,
|
||||||
|
unsigned int *digest_size);
|
||||||
|
|
||||||
#endif /* DM_VERITY_H */
|
#endif /* DM_VERITY_H */
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct lkdtm_list {
|
|||||||
#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0)
|
#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0)
|
||||||
#define REC_STACK_SIZE (_AC(CONFIG_FRAME_WARN, UL) / 2)
|
#define REC_STACK_SIZE (_AC(CONFIG_FRAME_WARN, UL) / 2)
|
||||||
#else
|
#else
|
||||||
#define REC_STACK_SIZE (THREAD_SIZE / 8)
|
#define REC_STACK_SIZE (THREAD_SIZE / 8UL)
|
||||||
#endif
|
#endif
|
||||||
#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2)
|
#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
|
||||||
|
#ifndef __LINUX_DM_VERITY_LOADPIN_H
|
||||||
|
#define __LINUX_DM_VERITY_LOADPIN_H
|
||||||
|
|
||||||
|
#include <linux/list.h>
|
||||||
|
|
||||||
|
struct block_device;
|
||||||
|
|
||||||
|
extern struct list_head dm_verity_loadpin_trusted_root_digests;
|
||||||
|
|
||||||
|
struct dm_verity_loadpin_trusted_root_digest {
|
||||||
|
struct list_head node;
|
||||||
|
unsigned int len;
|
||||||
|
u8 data[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_SECURITY_LOADPIN_VERITY)
|
||||||
|
bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev);
|
||||||
|
#else
|
||||||
|
static inline bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __LINUX_DM_VERITY_LOADPIN_H */
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Google LLC
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UAPI_LINUX_LOOP_LOADPIN_H
|
||||||
|
#define _UAPI_LINUX_LOOP_LOADPIN_H
|
||||||
|
|
||||||
|
#define LOADPIN_IOC_MAGIC 'L'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS - Set up the root digests of verity devices
|
||||||
|
* that loadpin should trust.
|
||||||
|
*
|
||||||
|
* Takes a file descriptor from which to read the root digests of trusted verity devices. The file
|
||||||
|
* is expected to contain a list of digests in ASCII format, with one line per digest. The ioctl
|
||||||
|
* must be issued on the securityfs attribute 'loadpin/dm-verity' (which can be typically found
|
||||||
|
* under /sys/kernel/security/loadpin/dm-verity).
|
||||||
|
*/
|
||||||
|
#define LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS _IOW(LOADPIN_IOC_MAGIC, 0x00, unsigned int)
|
||||||
|
|
||||||
|
#endif /* _UAPI_LINUX_LOOP_LOADPIN_H */
|
||||||
@@ -99,6 +99,7 @@
|
|||||||
#include <linux/kcsan.h>
|
#include <linux/kcsan.h>
|
||||||
#include <linux/init_syscalls.h>
|
#include <linux/init_syscalls.h>
|
||||||
#include <linux/stackdepot.h>
|
#include <linux/stackdepot.h>
|
||||||
|
#include <linux/randomize_kstack.h>
|
||||||
#include <net/net_namespace.h>
|
#include <net/net_namespace.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ DEFINE_TEST_ARRAY(u32) = {
|
|||||||
{-4U, 5U, 1U, -9U, -20U, true, false, true},
|
{-4U, 5U, 1U, -9U, -20U, true, false, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if BITS_PER_LONG == 64
|
||||||
DEFINE_TEST_ARRAY(u64) = {
|
DEFINE_TEST_ARRAY(u64) = {
|
||||||
{0, 0, 0, 0, 0, false, false, false},
|
{0, 0, 0, 0, 0, false, false, false},
|
||||||
{1, 1, 2, 0, 1, false, false, false},
|
{1, 1, 2, 0, 1, false, false, false},
|
||||||
@@ -114,6 +115,7 @@ DEFINE_TEST_ARRAY(u64) = {
|
|||||||
false, true, false},
|
false, true, false},
|
||||||
{-15ULL, 10ULL, -5ULL, -25ULL, -150ULL, false, false, true},
|
{-15ULL, 10ULL, -5ULL, -25ULL, -150ULL, false, false, true},
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
DEFINE_TEST_ARRAY(s8) = {
|
DEFINE_TEST_ARRAY(s8) = {
|
||||||
{0, 0, 0, 0, 0, false, false, false},
|
{0, 0, 0, 0, 0, false, false, false},
|
||||||
@@ -188,6 +190,8 @@ DEFINE_TEST_ARRAY(s32) = {
|
|||||||
{S32_MIN, S32_MIN, 0, 0, 0, true, false, true},
|
{S32_MIN, S32_MIN, 0, 0, 0, true, false, true},
|
||||||
{S32_MAX, S32_MAX, -2, 0, 1, true, false, true},
|
{S32_MAX, S32_MAX, -2, 0, 1, true, false, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if BITS_PER_LONG == 64
|
||||||
DEFINE_TEST_ARRAY(s64) = {
|
DEFINE_TEST_ARRAY(s64) = {
|
||||||
{0, 0, 0, 0, 0, false, false, false},
|
{0, 0, 0, 0, 0, false, false, false},
|
||||||
|
|
||||||
@@ -216,6 +220,7 @@ DEFINE_TEST_ARRAY(s64) = {
|
|||||||
{-128, -1, -129, -127, 128, false, false, false},
|
{-128, -1, -129, -127, 128, false, false, false},
|
||||||
{0, -S64_MAX, -S64_MAX, S64_MAX, 0, false, false, false},
|
{0, -S64_MAX, -S64_MAX, S64_MAX, 0, false, false, false},
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#define check_one_op(t, fmt, op, sym, a, b, r, of) do { \
|
#define check_one_op(t, fmt, op, sym, a, b, r, of) do { \
|
||||||
t _r; \
|
t _r; \
|
||||||
@@ -650,6 +655,7 @@ static struct kunit_case overflow_test_cases[] = {
|
|||||||
KUNIT_CASE(s16_overflow_test),
|
KUNIT_CASE(s16_overflow_test),
|
||||||
KUNIT_CASE(u32_overflow_test),
|
KUNIT_CASE(u32_overflow_test),
|
||||||
KUNIT_CASE(s32_overflow_test),
|
KUNIT_CASE(s32_overflow_test),
|
||||||
|
/* Clang 13 and earlier generate unwanted libcalls on 32-bit. */
|
||||||
#if BITS_PER_LONG == 64
|
#if BITS_PER_LONG == 64
|
||||||
KUNIT_CASE(u64_overflow_test),
|
KUNIT_CASE(u64_overflow_test),
|
||||||
KUNIT_CASE(s64_overflow_test),
|
KUNIT_CASE(s64_overflow_test),
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ static void kmalloc_oob_right(struct kunit *test)
|
|||||||
ptr = kmalloc(size, GFP_KERNEL);
|
ptr = kmalloc(size, GFP_KERNEL);
|
||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr);
|
||||||
/*
|
/*
|
||||||
* An unaligned access past the requested kmalloc size.
|
* An unaligned access past the requested kmalloc size.
|
||||||
* Only generic KASAN can precisely detect these.
|
* Only generic KASAN can precisely detect these.
|
||||||
@@ -159,6 +160,7 @@ static void kmalloc_oob_left(struct kunit *test)
|
|||||||
ptr = kmalloc(size, GFP_KERNEL);
|
ptr = kmalloc(size, GFP_KERNEL);
|
||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr);
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, *ptr = *(ptr - 1));
|
KUNIT_EXPECT_KASAN_FAIL(test, *ptr = *(ptr - 1));
|
||||||
kfree(ptr);
|
kfree(ptr);
|
||||||
}
|
}
|
||||||
@@ -171,6 +173,7 @@ static void kmalloc_node_oob_right(struct kunit *test)
|
|||||||
ptr = kmalloc_node(size, GFP_KERNEL, 0);
|
ptr = kmalloc_node(size, GFP_KERNEL, 0);
|
||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr);
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, ptr[0] = ptr[size]);
|
KUNIT_EXPECT_KASAN_FAIL(test, ptr[0] = ptr[size]);
|
||||||
kfree(ptr);
|
kfree(ptr);
|
||||||
}
|
}
|
||||||
@@ -191,6 +194,7 @@ static void kmalloc_pagealloc_oob_right(struct kunit *test)
|
|||||||
ptr = kmalloc(size, GFP_KERNEL);
|
ptr = kmalloc(size, GFP_KERNEL);
|
||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr);
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, ptr[size + OOB_TAG_OFF] = 0);
|
KUNIT_EXPECT_KASAN_FAIL(test, ptr[size + OOB_TAG_OFF] = 0);
|
||||||
|
|
||||||
kfree(ptr);
|
kfree(ptr);
|
||||||
@@ -271,6 +275,7 @@ static void kmalloc_large_oob_right(struct kunit *test)
|
|||||||
ptr = kmalloc(size, GFP_KERNEL);
|
ptr = kmalloc(size, GFP_KERNEL);
|
||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr);
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, ptr[size] = 0);
|
KUNIT_EXPECT_KASAN_FAIL(test, ptr[size] = 0);
|
||||||
kfree(ptr);
|
kfree(ptr);
|
||||||
}
|
}
|
||||||
@@ -410,6 +415,8 @@ static void kmalloc_oob_16(struct kunit *test)
|
|||||||
ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL);
|
ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL);
|
||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr2);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr2);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr1);
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr2);
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, *ptr1 = *ptr2);
|
KUNIT_EXPECT_KASAN_FAIL(test, *ptr1 = *ptr2);
|
||||||
kfree(ptr1);
|
kfree(ptr1);
|
||||||
kfree(ptr2);
|
kfree(ptr2);
|
||||||
@@ -756,6 +763,8 @@ static void ksize_unpoisons_memory(struct kunit *test)
|
|||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||||
real_size = ksize(ptr);
|
real_size = ksize(ptr);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr);
|
||||||
|
|
||||||
/* This access shouldn't trigger a KASAN report. */
|
/* This access shouldn't trigger a KASAN report. */
|
||||||
ptr[size] = 'x';
|
ptr[size] = 'x';
|
||||||
|
|
||||||
@@ -778,6 +787,7 @@ static void ksize_uaf(struct kunit *test)
|
|||||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||||
kfree(ptr);
|
kfree(ptr);
|
||||||
|
|
||||||
|
OPTIMIZER_HIDE_VAR(ptr);
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, ksize(ptr));
|
KUNIT_EXPECT_KASAN_FAIL(test, ksize(ptr));
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
|
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
|
||||||
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size]);
|
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size]);
|
||||||
|
|||||||
+1
-1
@@ -161,7 +161,7 @@ static inline void check_bogus_address(const unsigned long ptr, unsigned long n,
|
|||||||
static inline void check_heap_object(const void *ptr, unsigned long n,
|
static inline void check_heap_object(const void *ptr, unsigned long n,
|
||||||
bool to_user)
|
bool to_user)
|
||||||
{
|
{
|
||||||
uintptr_t addr = (uintptr_t)ptr;
|
unsigned long addr = (unsigned long)ptr;
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
struct folio *folio;
|
struct folio *folio;
|
||||||
|
|
||||||
|
|||||||
@@ -18,3 +18,19 @@ config SECURITY_LOADPIN_ENFORCE
|
|||||||
If selected, LoadPin will enforce pinning at boot. If not
|
If selected, LoadPin will enforce pinning at boot. If not
|
||||||
selected, it can be enabled at boot with the kernel parameter
|
selected, it can be enabled at boot with the kernel parameter
|
||||||
"loadpin.enforce=1".
|
"loadpin.enforce=1".
|
||||||
|
|
||||||
|
config SECURITY_LOADPIN_VERITY
|
||||||
|
bool "Allow reading files from certain other filesystems that use dm-verity"
|
||||||
|
depends on SECURITY_LOADPIN && DM_VERITY=y && SECURITYFS
|
||||||
|
help
|
||||||
|
If selected LoadPin can allow reading files from filesystems
|
||||||
|
that use dm-verity. LoadPin maintains a list of verity root
|
||||||
|
digests it considers trusted. A verity backed filesystem is
|
||||||
|
considered trusted if its root digest is found in the list
|
||||||
|
of trusted digests.
|
||||||
|
|
||||||
|
The list of trusted verity can be populated through an ioctl
|
||||||
|
on the LoadPin securityfs entry 'dm-verity'. The ioctl
|
||||||
|
expects a file descriptor of a file with verity digests as
|
||||||
|
parameter. The file must be located on the pinned root and
|
||||||
|
contain a comma separated list of digests.
|
||||||
|
|||||||
+166
-1
@@ -18,6 +18,8 @@
|
|||||||
#include <linux/path.h>
|
#include <linux/path.h>
|
||||||
#include <linux/sched.h> /* current */
|
#include <linux/sched.h> /* current */
|
||||||
#include <linux/string_helpers.h>
|
#include <linux/string_helpers.h>
|
||||||
|
#include <linux/dm-verity-loadpin.h>
|
||||||
|
#include <uapi/linux/loadpin.h>
|
||||||
|
|
||||||
static void report_load(const char *origin, struct file *file, char *operation)
|
static void report_load(const char *origin, struct file *file, char *operation)
|
||||||
{
|
{
|
||||||
@@ -43,6 +45,9 @@ static char *exclude_read_files[READING_MAX_ID];
|
|||||||
static int ignore_read_file_id[READING_MAX_ID] __ro_after_init;
|
static int ignore_read_file_id[READING_MAX_ID] __ro_after_init;
|
||||||
static struct super_block *pinned_root;
|
static struct super_block *pinned_root;
|
||||||
static DEFINE_SPINLOCK(pinned_root_spinlock);
|
static DEFINE_SPINLOCK(pinned_root_spinlock);
|
||||||
|
#ifdef CONFIG_SECURITY_LOADPIN_VERITY
|
||||||
|
static bool deny_reading_verity_digests;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_SYSCTL
|
#ifdef CONFIG_SYSCTL
|
||||||
|
|
||||||
@@ -171,7 +176,8 @@ static int loadpin_read_file(struct file *file, enum kernel_read_file_id id,
|
|||||||
spin_unlock(&pinned_root_spinlock);
|
spin_unlock(&pinned_root_spinlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) {
|
if (IS_ERR_OR_NULL(pinned_root) ||
|
||||||
|
((load_root != pinned_root) && !dm_verity_loadpin_is_bdev_trusted(load_root->s_bdev))) {
|
||||||
if (unlikely(!enforce)) {
|
if (unlikely(!enforce)) {
|
||||||
report_load(origin, file, "pinning-ignored");
|
report_load(origin, file, "pinning-ignored");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -237,6 +243,7 @@ static int __init loadpin_init(void)
|
|||||||
enforce ? "" : "not ");
|
enforce ? "" : "not ");
|
||||||
parse_exclude();
|
parse_exclude();
|
||||||
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
|
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +252,164 @@ DEFINE_LSM(loadpin) = {
|
|||||||
.init = loadpin_init,
|
.init = loadpin_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURITY_LOADPIN_VERITY
|
||||||
|
|
||||||
|
enum loadpin_securityfs_interface_index {
|
||||||
|
LOADPIN_DM_VERITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int read_trusted_verity_root_digests(unsigned int fd)
|
||||||
|
{
|
||||||
|
struct fd f;
|
||||||
|
void *data;
|
||||||
|
int rc;
|
||||||
|
char *p, *d;
|
||||||
|
|
||||||
|
if (deny_reading_verity_digests)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
/* The list of trusted root digests can only be set up once */
|
||||||
|
if (!list_empty(&dm_verity_loadpin_trusted_root_digests))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
f = fdget(fd);
|
||||||
|
if (!f.file)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data = kzalloc(SZ_4K, GFP_KERNEL);
|
||||||
|
if (!data) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = kernel_read_file(f.file, 0, (void **)&data, SZ_4K - 1, NULL, READING_POLICY);
|
||||||
|
if (rc < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
p = data;
|
||||||
|
p[rc] = '\0';
|
||||||
|
p = strim(p);
|
||||||
|
|
||||||
|
p = strim(data);
|
||||||
|
while ((d = strsep(&p, "\n")) != NULL) {
|
||||||
|
int len = strlen(d);
|
||||||
|
struct dm_verity_loadpin_trusted_root_digest *trd;
|
||||||
|
|
||||||
|
if (len % 2) {
|
||||||
|
rc = -EPROTO;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
len /= 2;
|
||||||
|
|
||||||
|
trd = kzalloc(struct_size(trd, data, len), GFP_KERNEL);
|
||||||
|
if (!trd) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hex2bin(trd->data, d, len)) {
|
||||||
|
kfree(trd);
|
||||||
|
rc = -EPROTO;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
trd->len = len;
|
||||||
|
|
||||||
|
list_add_tail(&trd->node, &dm_verity_loadpin_trusted_root_digests);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_empty(&dm_verity_loadpin_trusted_root_digests)) {
|
||||||
|
rc = -EPROTO;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(data);
|
||||||
|
fdput(f);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
kfree(data);
|
||||||
|
|
||||||
|
/* any failure in loading/parsing invalidates the entire list */
|
||||||
|
{
|
||||||
|
struct dm_verity_loadpin_trusted_root_digest *trd, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(trd, tmp, &dm_verity_loadpin_trusted_root_digests, node) {
|
||||||
|
list_del(&trd->node);
|
||||||
|
kfree(trd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disallow further attempts after reading a corrupt/invalid file */
|
||||||
|
deny_reading_verity_digests = true;
|
||||||
|
|
||||||
|
fdput(f);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************** securityfs ********************************/
|
||||||
|
|
||||||
|
static long dm_verity_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *uarg = (void __user *)arg;
|
||||||
|
unsigned int fd;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS:
|
||||||
|
rc = copy_from_user(&fd, uarg, sizeof(fd));
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return read_trusted_verity_root_digests(fd);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations loadpin_dm_verity_ops = {
|
||||||
|
.unlocked_ioctl = dm_verity_ioctl,
|
||||||
|
.compat_ioctl = compat_ptr_ioctl,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init_loadpin_securityfs - create the securityfs directory for LoadPin
|
||||||
|
*
|
||||||
|
* We can not put this method normally under the loadpin_init() code path since
|
||||||
|
* the security subsystem gets initialized before the vfs caches.
|
||||||
|
*
|
||||||
|
* Returns 0 if the securityfs directory creation was successful.
|
||||||
|
*/
|
||||||
|
static int __init init_loadpin_securityfs(void)
|
||||||
|
{
|
||||||
|
struct dentry *loadpin_dir, *dentry;
|
||||||
|
|
||||||
|
loadpin_dir = securityfs_create_dir("loadpin", NULL);
|
||||||
|
if (IS_ERR(loadpin_dir)) {
|
||||||
|
pr_err("LoadPin: could not create securityfs dir: %ld\n",
|
||||||
|
PTR_ERR(loadpin_dir));
|
||||||
|
return PTR_ERR(loadpin_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
dentry = securityfs_create_file("dm-verity", 0600, loadpin_dir,
|
||||||
|
(void *)LOADPIN_DM_VERITY, &loadpin_dm_verity_ops);
|
||||||
|
if (IS_ERR(dentry)) {
|
||||||
|
pr_err("LoadPin: could not create securityfs entry 'dm-verity': %ld\n",
|
||||||
|
PTR_ERR(dentry));
|
||||||
|
return PTR_ERR(dentry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_initcall(init_loadpin_securityfs);
|
||||||
|
|
||||||
|
#endif /* CONFIG_SECURITY_LOADPIN_VERITY */
|
||||||
|
|
||||||
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
|
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
|
||||||
module_param(enforce, int, 0);
|
module_param(enforce, int, 0);
|
||||||
MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning");
|
MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning");
|
||||||
|
|||||||
Reference in New Issue
Block a user