Skip to content

Commit

Permalink
efivarfs: Limit the rate for non-root to read files
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Ard Biesheuvel <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
aegl authored and torvalds committed Feb 22, 2018
1 parent 28128c6 commit bef3efb
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 0 deletions.
6 changes: 6 additions & 0 deletions fs/efivarfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

#include <linux/efi.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/mount.h>
Expand Down Expand Up @@ -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);

/*
Expand Down
4 changes: 4 additions & 0 deletions include/linux/sched/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <linux/uidgid.h>
#include <linux/atomic.h>
#include <linux/ratelimit.h>

struct key;

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions kernel/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};

/*
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit bef3efb

Please sign in to comment.