Skip to content

Commit a42aaad

Browse files
ribaldaakpm00
authored andcommitted
kexec: introduce sysctl parameters kexec_load_limit_*
kexec allows replacing the current kernel with a different one. This is usually a source of concerns for sysadmins that want to harden a system. Linux already provides a way to disable loading new kexec kernel via kexec_load_disabled, but that control is very coard, it is all or nothing and does not make distinction between a panic kexec and a normal kexec. This patch introduces new sysctl parameters, with finer tuning to specify how many times a kexec kernel can be loaded. The sysadmin can set different limits for kexec panic and kexec reboot kernels. The value can be modified at runtime via sysctl, but only with a stricter value. With these new parameters on place, a system with loadpin and verity enabled, using the following kernel parameters: sysctl.kexec_load_limit_reboot=0 sysct.kexec_load_limit_panic=1 can have a good warranty that if initrd tries to load a panic kernel, a malitious user will have small chances to replace that kernel with a different one, even if they can trigger timeouts on the disk where the panic kernel lives. Link: https://lkml.kernel.org/r/20221114-disable-kexec-reset-v6-3-6a8531a09b9a@chromium.org Signed-off-by: Ricardo Ribalda <ribalda@chromium.org> Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org> Acked-by: Baoquan He <bhe@redhat.com> Cc: Bagas Sanjaya <bagasdotme@gmail.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Guilherme G. Piccoli <gpiccoli@igalia.com> # Steam Deck Cc: Joel Fernandes (Google) <joel@joelfernandes.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Philipp Rudo <prudo@redhat.com> Cc: Ross Zwisler <zwisler@kernel.org> Cc: Sergey Senozhatsky <senozhatsky@chromium.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 7e99f8b commit a42aaad

5 files changed

Lines changed: 114 additions & 8 deletions

File tree

Documentation/admin-guide/sysctl/kernel.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,24 @@ allowing a system to set up (and later use) an image without it being
464464
altered.
465465
Generally used together with the `modules_disabled`_ sysctl.
466466

467+
kexec_load_limit_panic
468+
======================
469+
470+
This parameter specifies a limit to the number of times the syscalls
471+
``kexec_load`` and ``kexec_file_load`` can be called with a crash
472+
image. It can only be set with a more restrictive value than the
473+
current one.
474+
475+
== ======================================================
476+
-1 Unlimited calls to kexec. This is the default setting.
477+
N Number of calls left.
478+
== ======================================================
479+
480+
kexec_load_limit_reboot
481+
=======================
482+
483+
Similar functionality as ``kexec_load_limit_panic``, but for a normal
484+
image.
467485

468486
kptr_restrict
469487
=============

include/linux/kexec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ extern int kimage_crash_copy_vmcoreinfo(struct kimage *image);
404404
extern struct kimage *kexec_image;
405405
extern struct kimage *kexec_crash_image;
406406

407-
bool kexec_load_permitted(void);
407+
bool kexec_load_permitted(int kexec_image_type);
408408

409409
#ifndef kexec_flush_icache_page
410410
#define kexec_flush_icache_page(page)

kernel/kexec.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,12 @@ static int do_kexec_load(unsigned long entry, unsigned long nr_segments,
190190
static inline int kexec_load_check(unsigned long nr_segments,
191191
unsigned long flags)
192192
{
193+
int image_type = (flags & KEXEC_ON_CRASH) ?
194+
KEXEC_TYPE_CRASH : KEXEC_TYPE_DEFAULT;
193195
int result;
194196

195197
/* We only trust the superuser with rebooting the system. */
196-
if (!kexec_load_permitted())
198+
if (!kexec_load_permitted(image_type))
197199
return -EPERM;
198200

199201
/* Permit LSMs and IMA to fail the kexec */

kernel/kexec_core.c

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,10 +920,64 @@ int kimage_load_segment(struct kimage *image,
920920
return result;
921921
}
922922

923+
struct kexec_load_limit {
924+
/* Mutex protects the limit count. */
925+
struct mutex mutex;
926+
int limit;
927+
};
928+
929+
static struct kexec_load_limit load_limit_reboot = {
930+
.mutex = __MUTEX_INITIALIZER(load_limit_reboot.mutex),
931+
.limit = -1,
932+
};
933+
934+
static struct kexec_load_limit load_limit_panic = {
935+
.mutex = __MUTEX_INITIALIZER(load_limit_panic.mutex),
936+
.limit = -1,
937+
};
938+
923939
struct kimage *kexec_image;
924940
struct kimage *kexec_crash_image;
925941
static int kexec_load_disabled;
942+
926943
#ifdef CONFIG_SYSCTL
944+
static int kexec_limit_handler(struct ctl_table *table, int write,
945+
void *buffer, size_t *lenp, loff_t *ppos)
946+
{
947+
struct kexec_load_limit *limit = table->data;
948+
int val;
949+
struct ctl_table tmp = {
950+
.data = &val,
951+
.maxlen = sizeof(val),
952+
.mode = table->mode,
953+
};
954+
int ret;
955+
956+
if (write) {
957+
ret = proc_dointvec(&tmp, write, buffer, lenp, ppos);
958+
if (ret)
959+
return ret;
960+
961+
if (val < 0)
962+
return -EINVAL;
963+
964+
mutex_lock(&limit->mutex);
965+
if (limit->limit != -1 && val >= limit->limit)
966+
ret = -EINVAL;
967+
else
968+
limit->limit = val;
969+
mutex_unlock(&limit->mutex);
970+
971+
return ret;
972+
}
973+
974+
mutex_lock(&limit->mutex);
975+
val = limit->limit;
976+
mutex_unlock(&limit->mutex);
977+
978+
return proc_dointvec(&tmp, write, buffer, lenp, ppos);
979+
}
980+
927981
static struct ctl_table kexec_core_sysctls[] = {
928982
{
929983
.procname = "kexec_load_disabled",
@@ -935,6 +989,18 @@ static struct ctl_table kexec_core_sysctls[] = {
935989
.extra1 = SYSCTL_ONE,
936990
.extra2 = SYSCTL_ONE,
937991
},
992+
{
993+
.procname = "kexec_load_limit_panic",
994+
.data = &load_limit_panic,
995+
.mode = 0644,
996+
.proc_handler = kexec_limit_handler,
997+
},
998+
{
999+
.procname = "kexec_load_limit_reboot",
1000+
.data = &load_limit_reboot,
1001+
.mode = 0644,
1002+
.proc_handler = kexec_limit_handler,
1003+
},
9381004
{ }
9391005
};
9401006

@@ -946,13 +1012,30 @@ static int __init kexec_core_sysctl_init(void)
9461012
late_initcall(kexec_core_sysctl_init);
9471013
#endif
9481014

949-
bool kexec_load_permitted(void)
1015+
bool kexec_load_permitted(int kexec_image_type)
9501016
{
1017+
struct kexec_load_limit *limit;
1018+
9511019
/*
9521020
* Only the superuser can use the kexec syscall and if it has not
9531021
* been disabled.
9541022
*/
955-
return capable(CAP_SYS_BOOT) && !kexec_load_disabled;
1023+
if (!capable(CAP_SYS_BOOT) || kexec_load_disabled)
1024+
return false;
1025+
1026+
/* Check limit counter and decrease it.*/
1027+
limit = (kexec_image_type == KEXEC_TYPE_CRASH) ?
1028+
&load_limit_panic : &load_limit_reboot;
1029+
mutex_lock(&limit->mutex);
1030+
if (!limit->limit) {
1031+
mutex_unlock(&limit->mutex);
1032+
return false;
1033+
}
1034+
if (limit->limit != -1)
1035+
limit->limit--;
1036+
mutex_unlock(&limit->mutex);
1037+
1038+
return true;
9561039
}
9571040

9581041
/*

kernel/kexec_file.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,13 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
326326
unsigned long, cmdline_len, const char __user *, cmdline_ptr,
327327
unsigned long, flags)
328328
{
329-
int ret = 0, i;
329+
int image_type = (flags & KEXEC_FILE_ON_CRASH) ?
330+
KEXEC_TYPE_CRASH : KEXEC_TYPE_DEFAULT;
330331
struct kimage **dest_image, *image;
332+
int ret = 0, i;
331333

332334
/* We only trust the superuser with rebooting the system. */
333-
if (!kexec_load_permitted())
335+
if (!kexec_load_permitted(image_type))
334336
return -EPERM;
335337

336338
/* Make sure we have a legal set of flags */
@@ -342,11 +344,12 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
342344
if (!kexec_trylock())
343345
return -EBUSY;
344346

345-
dest_image = &kexec_image;
346-
if (flags & KEXEC_FILE_ON_CRASH) {
347+
if (image_type == KEXEC_TYPE_CRASH) {
347348
dest_image = &kexec_crash_image;
348349
if (kexec_crash_image)
349350
arch_kexec_unprotect_crashkres();
351+
} else {
352+
dest_image = &kexec_image;
350353
}
351354

352355
if (flags & KEXEC_FILE_UNLOAD)

0 commit comments

Comments
 (0)