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....@intel.com>
Cc: Markus T. Metzger <markus.t.metz...@intel.com>
Cc: H. Peter Anvin <h...@zytor.com>
Cc: Andi Kleen <a...@linux.intel.com>
Cc: Andy Lutomirski <l...@kernel.org>
---
 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

Reply via email to