Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

efivarfs: Limit the rate for non-root to read files

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>

authored by

Luck, Tony and committed by
Linus Torvalds
bef3efbe 28128c61

+13
+6
fs/efivarfs/file.c
··· 8 8 */ 9 9 10 10 #include <linux/efi.h> 11 + #include <linux/delay.h> 11 12 #include <linux/fs.h> 12 13 #include <linux/slab.h> 13 14 #include <linux/mount.h> ··· 74 73 void *data; 75 74 ssize_t size = 0; 76 75 int err; 76 + 77 + while (!__ratelimit(&file->f_cred->user->ratelimit)) { 78 + if (!msleep_interruptible(50)) 79 + return -EINTR; 80 + } 77 81 78 82 err = efivar_entry_size(var, &datasize); 79 83
+4
include/linux/sched/user.h
··· 4 4 5 5 #include <linux/uidgid.h> 6 6 #include <linux/atomic.h> 7 + #include <linux/ratelimit.h> 7 8 8 9 struct key; 9 10 ··· 42 41 defined(CONFIG_NET) 43 42 atomic_long_t locked_vm; 44 43 #endif 44 + 45 + /* Miscellaneous per-user rate limit */ 46 + struct ratelimit_state ratelimit; 45 47 }; 46 48 47 49 extern int uids_sysfs_init(void);
+3
kernel/user.c
··· 101 101 .sigpending = ATOMIC_INIT(0), 102 102 .locked_shm = 0, 103 103 .uid = GLOBAL_ROOT_UID, 104 + .ratelimit = RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 0), 104 105 }; 105 106 106 107 /* ··· 192 191 193 192 new->uid = uid; 194 193 atomic_set(&new->__count, 1); 194 + ratelimit_state_init(&new->ratelimit, HZ, 100); 195 + ratelimit_set_flags(&new->ratelimit, RATELIMIT_MSG_ON_RELEASE); 195 196 196 197 /* 197 198 * Before adding this, check whether we raced