]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
efivarfs: Limit the rate for non-root to read files
authorLuck, Tony <tony.luck@intel.com>
Thu, 22 Feb 2018 17:15:06 +0000 (09:15 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 22 Feb 2018 18:21:02 +0000 (10:21 -0800)
Each read from a file in efivarfs results in two calls to EFI
(one to get the file size, another to get the actual data).

On X86 these EFI calls result in broadcast system management
interrupts (SMI) which affect performance of the whole system.
A malicious user can loop performing reads from efivarfs bringing
the system to its knees.

Linus suggested per-user rate limit to solve this.

So we add a ratelimit structure to "user_struct" and initialize
it for the root user for no limit. When allocating user_struct for
other users we set the limit to 100 per second. This could be used
for other places that want to limit the rate of some detrimental
user action.

In efivarfs if the limit is exceeded when reading, we take an
interruptible nap for 50ms and check the rate limit again.

Signed-off-by: Tony Luck <tony.luck@intel.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/efivarfs/file.c
include/linux/sched/user.h
kernel/user.c

index 5f22e74bbadea0822e3f6f5378b732ab3c1e333d..8e568428c88be059299b5ada7eb48e7d289a04fe 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/efi.h>
+#include <linux/delay.h>
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/mount.h>
@@ -74,6 +75,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
        ssize_t size = 0;
        int err;
 
+       while (!__ratelimit(&file->f_cred->user->ratelimit)) {
+               if (!msleep_interruptible(50))
+                       return -EINTR;
+       }
+
        err = efivar_entry_size(var, &datasize);
 
        /*
index 0dcf4e480ef73345174bfb7d1446e0e1a89daa5c..96fe289c4c6e496fefd005d3100c84bf58f81698 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/uidgid.h>
 #include <linux/atomic.h>
+#include <linux/ratelimit.h>
 
 struct key;
 
@@ -41,6 +42,9 @@ struct user_struct {
     defined(CONFIG_NET)
        atomic_long_t locked_vm;
 #endif
+
+       /* Miscellaneous per-user rate limit */
+       struct ratelimit_state ratelimit;
 };
 
 extern int uids_sysfs_init(void);
index 9a20acce460d53cf23f7057c7dc122b685ae639d..36288d8406756400f5008ee0631b810301504e0d 100644 (file)
@@ -101,6 +101,7 @@ struct user_struct root_user = {
        .sigpending     = ATOMIC_INIT(0),
        .locked_shm     = 0,
        .uid            = GLOBAL_ROOT_UID,
+       .ratelimit      = RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 0),
 };
 
 /*
@@ -191,6 +192,8 @@ struct user_struct *alloc_uid(kuid_t uid)
 
                new->uid = uid;
                atomic_set(&new->__count, 1);
+               ratelimit_state_init(&new->ratelimit, HZ, 100);
+               ratelimit_set_flags(&new->ratelimit, RATELIMIT_MSG_ON_RELEASE);
 
                /*
                 * Before adding this, check whether we raced