Merge tag 'hardening-v6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull hardening updates from Kees Cook:
 "Beyond some specific LoadPin, UBSAN, and fortify features, there are
  other fixes scattered around in various subsystems where maintainers
  were okay with me carrying them in my tree or were non-responsive but
  the patches were reviewed by others:

   - Replace 0-length and 1-element arrays with flexible arrays in
     various subsystems (Paulo Miguel Almeida, Stephen Rothwell, Kees
     Cook)

   - randstruct: Disable Clang 15 support (Eric Biggers)

   - GCC plugins: Drop -std=gnu++11 flag (Sam James)

   - strpbrk(): Refactor to use strchr() (Andy Shevchenko)

   - LoadPin LSM: Allow root filesystem switching when non-enforcing

   - fortify: Use dynamic object size hints when available

   - ext4: Fix CFI function prototype mismatch

   - Nouveau: Fix DP buffer size arguments

   - hisilicon: Wipe entire crypto DMA pool on error

   - coda: Fully allocate sig_inputArgs

   - UBSAN: Improve arm64 trap code reporting

   - copy_struct_from_user(): Add minimum bounds check on kernel buffer
     size"

* tag 'hardening-v6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  randstruct: disable Clang 15 support
  uaccess: Add minimum bounds check on kernel buffer size
  arm64: Support Clang UBSAN trap codes for better reporting
  coda: Avoid partial allocation of sig_inputArgs
  gcc-plugins: drop -std=gnu++11 to fix GCC 13 build
  lib/string: Use strchr() in strpbrk()
  crypto: hisilicon: Wipe entire pool on error
  net/i40e: Replace 0-length array with flexible array
  io_uring: Replace 0-length array with flexible array
  ext4: Fix function prototype mismatch for ext4_feat_ktype
  i915/gvt: Replace one-element array with flexible-array member
  drm/nouveau/disp: Fix nvif_outp_acquire_dp() argument size
  LoadPin: Allow filesystem switch when not enforcing
  LoadPin: Move pin reporting cleanly out of locking
  LoadPin: Refactor sysctl initialization
  LoadPin: Refactor read-only check into a helper
  ARM: ixp4xx: Replace 0-length arrays with flexible arrays
  fortify: Use __builtin_dynamic_object_size() when available
  rxrpc: replace zero-lenth array with DECLARE_FLEX_ARRAY() helper
This commit is contained in:
Linus Torvalds
2023-02-21 11:07:23 -08:00
24 changed files with 229 additions and 60 deletions

View File

@@ -281,6 +281,9 @@ endmenu
config CC_HAS_RANDSTRUCT
def_bool $(cc-option,-frandomize-layout-seed-file=/dev/null)
# Randstruct was first added in Clang 15, but it isn't safe to use until
# Clang 16 due to https://github.com/llvm/llvm-project/issues/60349
depends on !CC_IS_CLANG || CLANG_VERSION >= 160000
choice
prompt "Randomize layout of sensitive kernel structures"

View File

@@ -52,7 +52,6 @@ static bool deny_reading_verity_digests;
#endif
#ifdef CONFIG_SYSCTL
static struct ctl_path loadpin_sysctl_path[] = {
{ .procname = "kernel", },
{ .procname = "loadpin", },
@@ -66,59 +65,70 @@ static struct ctl_table loadpin_sysctl_table[] = {
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra1 = SYSCTL_ONE,
.extra2 = SYSCTL_ONE,
},
{ }
};
/*
* This must be called after early kernel init, since then the rootdev
* is available.
*/
static void check_pinning_enforcement(struct super_block *mnt_sb)
static void set_sysctl(bool is_writable)
{
bool ro = false;
/*
* If load pinning is not enforced via a read-only block
* device, allow sysctl to change modes for testing.
*/
if (is_writable)
loadpin_sysctl_table[0].extra1 = SYSCTL_ZERO;
else
loadpin_sysctl_table[0].extra1 = SYSCTL_ONE;
}
#else
static inline void set_sysctl(bool is_writable) { }
#endif
static void report_writable(struct super_block *mnt_sb, bool writable)
{
if (mnt_sb->s_bdev) {
ro = bdev_read_only(mnt_sb->s_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");
writable ? "writable" : "read-only");
} else
pr_info("mnt_sb lacks block device, treating as: writable\n");
if (!ro) {
if (!register_sysctl_paths(loadpin_sysctl_path,
loadpin_sysctl_table))
pr_notice("sysctl registration failed!\n");
else
pr_info("enforcement can be disabled.\n");
} else
if (!writable)
pr_info("load pinning engaged.\n");
}
#else
static void check_pinning_enforcement(struct super_block *mnt_sb)
/*
* This must be called after early kernel init, since then the rootdev
* is available.
*/
static bool sb_is_writable(struct super_block *mnt_sb)
{
pr_info("load pinning engaged.\n");
bool writable = true;
if (mnt_sb->s_bdev)
writable = !bdev_read_only(mnt_sb->s_bdev);
return writable;
}
#endif
static void loadpin_sb_free_security(struct super_block *mnt_sb)
{
/*
* When unmounting the filesystem we were using for load
* pinning, we acknowledge the superblock release, but make sure
* no other modules or firmware can be loaded.
* no other modules or firmware can be loaded when we are in
* enforcing mode. Otherwise, allow the root to be reestablished.
*/
if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) {
pinned_root = ERR_PTR(-EIO);
pr_info("umount pinned fs: refusing further loads\n");
if (enforce) {
pinned_root = ERR_PTR(-EIO);
pr_info("umount pinned fs: refusing further loads\n");
} else {
pinned_root = NULL;
}
}
}
@@ -126,6 +136,8 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
{
struct super_block *load_root;
const char *origin = kernel_read_file_id_str(id);
bool first_root_pin = false;
bool load_root_writable;
/* If the file id is excluded, ignore the pinning. */
if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) &&
@@ -146,26 +158,25 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
}
load_root = file->f_path.mnt->mnt_sb;
load_root_writable = sb_is_writable(load_root);
/* First loaded module/firmware defines the root for all others. */
spin_lock(&pinned_root_spinlock);
/*
* pinned_root is only NULL at startup. Otherwise, it is either
* a valid reference, or an ERR_PTR.
* pinned_root is only NULL at startup or when the pinned root has
* been unmounted while we are not in enforcing mode. Otherwise, it
* is either a valid reference, or an ERR_PTR.
*/
if (!pinned_root) {
pinned_root = load_root;
/*
* Unlock now since it's only pinned_root we care about.
* In the worst case, we will (correctly) report pinning
* failures before we have announced that pinning is
* enforcing. This would be purely cosmetic.
*/
spin_unlock(&pinned_root_spinlock);
check_pinning_enforcement(pinned_root);
first_root_pin = true;
}
spin_unlock(&pinned_root_spinlock);
if (first_root_pin) {
report_writable(pinned_root, load_root_writable);
set_sysctl(load_root_writable);
report_load(origin, file, "pinned");
} else {
spin_unlock(&pinned_root_spinlock);
}
if (IS_ERR_OR_NULL(pinned_root) ||
@@ -250,6 +261,10 @@ static int __init loadpin_init(void)
pr_info("ready to pin (currently %senforcing)\n",
enforce ? "" : "not ");
parse_exclude();
#ifdef CONFIG_SYSCTL
if (!register_sysctl_paths(loadpin_sysctl_path, loadpin_sysctl_table))
pr_notice("sysctl registration failed!\n");
#endif
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
return 0;