[PATCH 06/15] x86/fsgsbase/64: Add putregs() to handle multiple elements' setting

From: Chang S. Bae
Date: Mon Mar 19 2018 - 14:09:01 EST


putregs() can be used to handle multiple elements flexibly.

It is useful when inter-dependency lies in updating a
group of context entries. There will be a case with FSGSBASE.

Signed-off-by: Chang S. Bae <chang.seok.bae@xxxxxxxxx>
Cc: Markus T. Metzger <markus.t.metzger@xxxxxxxxx>
Cc: H. Peter Anvin <hpa@xxxxxxxxx>
Cc: Andi Kleen <ak@xxxxxxxxxxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxx>
---
arch/x86/kernel/ptrace.c | 51 +++++++++++++++++++++++++++++++++++++-----------
1 file changed, 40 insertions(+), 11 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index d8a1e1b..9c09bf0 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -421,6 +421,22 @@ static int putreg(struct task_struct *child,
return 0;
}

+static int putregs(struct task_struct *child,
+ unsigned int offset,
+ unsigned int count,
+ const unsigned long *values)
+{
+ const unsigned long *v = values;
+ int ret = 0;
+
+ while (count >= sizeof(*v) && !ret) {
+ ret = putreg(child, offset, *v++);
+ count -= sizeof(*v);
+ offset += sizeof(*v);
+ }
+ return ret;
+}
+
static unsigned long getreg(struct task_struct *task, unsigned long offset)
{
switch (offset) {
@@ -477,24 +493,37 @@ static int genregs_set(struct task_struct *target,
const void *kbuf, const void __user *ubuf)
{
int ret = 0;
+
if (kbuf) {
- const unsigned long *k = kbuf;
- while (count >= sizeof(*k) && !ret) {
- ret = putreg(target, pos, *k++);
- count -= sizeof(*k);
- pos += sizeof(*k);
- }
+ ret = putregs(target, pos, count, kbuf);
} else {
const unsigned long __user *u = ubuf;
- while (count >= sizeof(*u) && !ret) {
+ const unsigned long *genregs = NULL;
+ unsigned long *buf = NULL;
+ unsigned int remains = 0;
+
+ buf = kmalloc(count, GFP_KERNEL);
+
+ if (unlikely(!buf))
+ return -ENOMEM;
+
+ genregs = buf;
+ remains = count;
+
+ while (remains >= sizeof(*u) && !ret) {
unsigned long word;
+
ret = __get_user(word, u++);
- if (ret)
+ if (unlikely(ret))
break;
- ret = putreg(target, pos, word);
- count -= sizeof(*u);
- pos += sizeof(*u);
+ memcpy(buf++, &word, sizeof(*u));
+ remains -= sizeof(*u);
}
+
+ if (likely(!ret))
+ /* Allows to handle multiple elements accordingly. */
+ ret = putregs(target, pos, count, genregs);
+ kfree(genregs);
}
return ret;
}
--
2.7.4