hc
2023-10-25 6c2073b7aa40e29d0eca7d571dd7bc590c7ecaa7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
 * Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
 * Copyright 2003 PathScale, Inc.
 *
 * Licensed under the GPL
 */
 
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <asm/prctl.h> /* XXX This should get the constants from libc */
#include <os.h>
#include <registers.h>
 
long arch_prctl(struct task_struct *task, int option,
       unsigned long __user *arg2)
{
   unsigned long *ptr = arg2, tmp;
   long ret;
   int pid = task->mm->context.id.u.pid;
 
   /*
    * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to
    * be safe), we need to call arch_prctl on the host because
    * setting %fs may result in something else happening (like a
    * GDT or thread.fs being set instead).  So, we let the host
    * fiddle the registers and thread struct and restore the
    * registers afterwards.
    *
    * So, the saved registers are stored to the process (this
    * needed because a stub may have been the last thing to run),
    * arch_prctl is run on the host, then the registers are read
    * back.
    */
   switch (option) {
   case ARCH_SET_FS:
   case ARCH_SET_GS:
       ret = restore_pid_registers(pid, &current->thread.regs.regs);
       if (ret)
           return ret;
       break;
   case ARCH_GET_FS:
   case ARCH_GET_GS:
       /*
        * With these two, we read to a local pointer and
        * put_user it to the userspace pointer that we were
        * given.  If addr isn't valid (because it hasn't been
        * faulted in or is just bogus), we want put_user to
        * fault it in (or return -EFAULT) instead of having
        * the host return -EFAULT.
        */
       ptr = &tmp;
   }
 
   ret = os_arch_prctl(pid, option, ptr);
   if (ret)
       return ret;
 
   switch (option) {
   case ARCH_SET_FS:
       current->thread.arch.fs = (unsigned long) ptr;
       ret = save_registers(pid, &current->thread.regs.regs);
       break;
   case ARCH_SET_GS:
       ret = save_registers(pid, &current->thread.regs.regs);
       break;
   case ARCH_GET_FS:
       ret = put_user(tmp, arg2);
       break;
   case ARCH_GET_GS:
       ret = put_user(tmp, arg2);
       break;
   }
 
   return ret;
}
 
SYSCALL_DEFINE2(arch_prctl, int, option, unsigned long, arg2)
{
   return arch_prctl(current, option, (unsigned long __user *) arg2);
}
 
void arch_switch_to(struct task_struct *to)
{
   if ((to->thread.arch.fs == 0) || (to->mm == NULL))
       return;
 
   arch_prctl(to, ARCH_SET_FS, (void __user *) to->thread.arch.fs);
}