aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuck, Tony <tony.luck@intel.com>2018-02-22 12:15:06 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2018-02-22 13:21:02 -0500
commitbef3efbeb897b56867e271cdbc5f8adaacaeb9cd (patch)
treed3635a816ed945d7790b0202b3b40d6785ac7c8b
parent28128c61e08eaeced9cc8ec0e6b5d677b5b94690 (diff)
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>
-rw-r--r--fs/efivarfs/file.c6
-rw-r--r--include/linux/sched/user.h4
-rw-r--r--kernel/user.c3
3 files changed, 13 insertions, 0 deletions
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 5f22e74bbade..8e568428c88b 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -8,6 +8,7 @@
8 */ 8 */
9 9
10#include <linux/efi.h> 10#include <linux/efi.h>
11#include <linux/delay.h>
11#include <linux/fs.h> 12#include <linux/fs.h>
12#include <linux/slab.h> 13#include <linux/slab.h>
13#include <linux/mount.h> 14#include <linux/mount.h>
@@ -74,6 +75,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
74 ssize_t size = 0; 75 ssize_t size = 0;
75 int err; 76 int err;
76 77
78 while (!__ratelimit(&file->f_cred->user->ratelimit)) {
79 if (!msleep_interruptible(50))
80 return -EINTR;
81 }
82
77 err = efivar_entry_size(var, &datasize); 83 err = efivar_entry_size(var, &datasize);
78 84
79 /* 85 /*
diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h
index 0dcf4e480ef7..96fe289c4c6e 100644
--- a/include/linux/sched/user.h
+++ b/include/linux/sched/user.h
@@ -4,6 +4,7 @@
4 4
5#include <linux/uidgid.h> 5#include <linux/uidgid.h>
6#include <linux/atomic.h> 6#include <linux/atomic.h>
7#include <linux/ratelimit.h>
7 8
8struct key; 9struct key;
9 10
@@ -41,6 +42,9 @@ struct user_struct {
41 defined(CONFIG_NET) 42 defined(CONFIG_NET)
42 atomic_long_t locked_vm; 43 atomic_long_t locked_vm;
43#endif 44#endif
45
46 /* Miscellaneous per-user rate limit */
47 struct ratelimit_state ratelimit;
44}; 48};
45 49
46extern int uids_sysfs_init(void); 50extern int uids_sysfs_init(void);
diff --git a/kernel/user.c b/kernel/user.c
index 9a20acce460d..36288d840675 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -101,6 +101,7 @@ struct user_struct root_user = {
101 .sigpending = ATOMIC_INIT(0), 101 .sigpending = ATOMIC_INIT(0),
102 .locked_shm = 0, 102 .locked_shm = 0,
103 .uid = GLOBAL_ROOT_UID, 103 .uid = GLOBAL_ROOT_UID,
104 .ratelimit = RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 0),
104}; 105};
105 106
106/* 107/*
@@ -191,6 +192,8 @@ struct user_struct *alloc_uid(kuid_t uid)
191 192
192 new->uid = uid; 193 new->uid = uid;
193 atomic_set(&new->__count, 1); 194 atomic_set(&new->__count, 1);
195 ratelimit_state_init(&new->ratelimit, HZ, 100);
196 ratelimit_set_flags(&new->ratelimit, RATELIMIT_MSG_ON_RELEASE);
194 197
195 /* 198 /*
196 * Before adding this, check whether we raced 199 * Before adding this, check whether we raced