lkml.org 
[lkml]   [2020]   [Nov]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 3/4] x86/bus_lock: Set rate limit for bus lock
Date
To enforce user application throttling or mitigations, extend the
existing split lock detect kernel parameter:
split_lock_detect=ratelimit:N

It limits bus lock rate to N per second for non-root users.

Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Reviewed-by: Tony Luck <tony.luck@intel.com>
---
arch/x86/kernel/cpu/intel.c | 37 ++++++++++++++++++++++++++++++++-----
include/linux/sched/user.h | 4 +++-
kernel/user.c | 7 +++++++
3 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 4c8a22c36798..9d33fc9295ea 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -10,6 +10,9 @@
#include <linux/thread_info.h>
#include <linux/init.h>
#include <linux/uaccess.h>
+#include <linux/cred.h>
+#include <linux/delay.h>
+#include <linux/sched/user.h>

#include <asm/cpufeature.h>
#include <asm/msr.h>
@@ -40,6 +43,7 @@ enum split_lock_detect_state {
sld_off = 0,
sld_warn,
sld_fatal,
+ sld_ratelimit,
};

/*
@@ -998,13 +1002,25 @@ static const struct {
{ "off", sld_off },
{ "warn", sld_warn },
{ "fatal", sld_fatal },
+ { "ratelimit:", sld_ratelimit },
};

static inline bool match_option(const char *arg, int arglen, const char *opt)
{
- int len = strlen(opt);

- return len == arglen && !strncmp(arg, opt, len);
+ int len = strlen(opt), ratelimit;
+
+ if (strncmp(arg, opt, len))
+ return false;
+
+ if (sscanf(arg, "ratelimit:%d", &ratelimit) == 1 && ratelimit > 0 &&
+ ratelimit_bl <= HZ / 2) {
+ ratelimit_bl = ratelimit;
+
+ return true;
+ }
+
+ return len == arglen;
}

static bool split_lock_verify_msr(bool on)
@@ -1085,10 +1101,10 @@ static void sld_update_msr(bool on)
static void split_lock_init(void)
{
/*
- * If supported, #DB for bus lock will handle warn
+ * If supported, #DB for bus lock will handle warn or ratelimit
* and #AC for split lock is disabled.
*/
- if (bld && sld_state == sld_warn) {
+ if ((bld && sld_state == sld_warn) || sld_state == sld_ratelimit) {
split_lock_verify_msr(false);
return;
}
@@ -1149,7 +1165,8 @@ static void bus_lock_init(void)

bool handle_user_split_lock(struct pt_regs *regs, long error_code)
{
- if ((regs->flags & X86_EFLAGS_AC) || !sld || sld_state == sld_fatal)
+ if ((regs->flags & X86_EFLAGS_AC) || !sld || sld_state == sld_fatal ||
+ sld_state == sld_ratelimit)
return false;
split_lock_warn(regs->ip);
return true;
@@ -1162,6 +1179,11 @@ void handle_bus_lock(struct pt_regs *regs)

pr_warn_ratelimited("#DB: %s/%d took a bus_lock trap at address: 0x%lx\n",
current->comm, current->pid, regs->ip);
+
+ if (sld_state == sld_ratelimit) {
+ while (!__ratelimit(&get_current_user()->ratelimit_bl))
+ msleep(1000 / ratelimit_bl);
+ }
}

/*
@@ -1256,6 +1278,11 @@ static void sld_state_show(void)
else
pr_info("#DB: sending SIGBUS on user-space bus_locks\n");
break;
+
+ case sld_ratelimit:
+ if (bld)
+ pr_info("#DB: setting rate limit to %d/sec per user on non-root user-space bus_locks\n", ratelimit_bl);
+ break;
}
}

diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h
index a8ec3b6093fc..79f95002a123 100644
--- a/include/linux/sched/user.h
+++ b/include/linux/sched/user.h
@@ -40,8 +40,9 @@ struct user_struct {
atomic_t nr_watches; /* The number of watches this user currently has */
#endif

- /* Miscellaneous per-user rate limit */
+ /* Miscellaneous per-user rate limits */
struct ratelimit_state ratelimit;
+ struct ratelimit_state ratelimit_bl;
};

extern int uids_sysfs_init(void);
@@ -51,6 +52,7 @@ extern struct user_struct *find_user(kuid_t);
extern struct user_struct root_user;
#define INIT_USER (&root_user)

+extern int ratelimit_bl;

/* per-UID process charging. */
extern struct user_struct * alloc_uid(kuid_t);
diff --git a/kernel/user.c b/kernel/user.c
index b1635d94a1f2..8fc19706bc91 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -103,6 +103,7 @@ struct user_struct root_user = {
.locked_shm = 0,
.uid = GLOBAL_ROOT_UID,
.ratelimit = RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 0),
+ .ratelimit_bl = RATELIMIT_STATE_INIT(root_user.ratelimit_bl, 0, 0),
};

/*
@@ -172,6 +173,9 @@ void free_uid(struct user_struct *up)
free_user(up, flags);
}

+/* Architectures (e.g. X86) may set this for rate-limited bus locks. */
+int ratelimit_bl;
+
struct user_struct *alloc_uid(kuid_t uid)
{
struct hlist_head *hashent = uidhashentry(uid);
@@ -190,6 +194,9 @@ struct user_struct *alloc_uid(kuid_t uid)
refcount_set(&new->__count, 1);
ratelimit_state_init(&new->ratelimit, HZ, 100);
ratelimit_set_flags(&new->ratelimit, RATELIMIT_MSG_ON_RELEASE);
+ ratelimit_state_init(&new->ratelimit_bl, HZ, ratelimit_bl);
+ ratelimit_set_flags(&new->ratelimit_bl,
+ RATELIMIT_MSG_ON_RELEASE);

/*
* Before adding this, check whether we raced
--
2.29.2
\
 
 \ /
  Last update: 2020-11-08 05:30    [W:0.062 / U:0.296 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site