summaryrefslogtreecommitdiffstats
path: root/kernel/rseq.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/rseq.c')
-rw-r--r--kernel/rseq.c41
1 files changed, 25 insertions, 16 deletions
diff --git a/kernel/rseq.c b/kernel/rseq.c
index 22b6acf1ad63..c6242d8594dc 100644
--- a/kernel/rseq.c
+++ b/kernel/rseq.c
@@ -85,9 +85,9 @@ static int rseq_update_cpu_id(struct task_struct *t)
85{ 85{
86 u32 cpu_id = raw_smp_processor_id(); 86 u32 cpu_id = raw_smp_processor_id();
87 87
88 if (__put_user(cpu_id, &t->rseq->cpu_id_start)) 88 if (put_user(cpu_id, &t->rseq->cpu_id_start))
89 return -EFAULT; 89 return -EFAULT;
90 if (__put_user(cpu_id, &t->rseq->cpu_id)) 90 if (put_user(cpu_id, &t->rseq->cpu_id))
91 return -EFAULT; 91 return -EFAULT;
92 trace_rseq_update(t); 92 trace_rseq_update(t);
93 return 0; 93 return 0;
@@ -100,14 +100,14 @@ static int rseq_reset_rseq_cpu_id(struct task_struct *t)
100 /* 100 /*
101 * Reset cpu_id_start to its initial state (0). 101 * Reset cpu_id_start to its initial state (0).
102 */ 102 */
103 if (__put_user(cpu_id_start, &t->rseq->cpu_id_start)) 103 if (put_user(cpu_id_start, &t->rseq->cpu_id_start))
104 return -EFAULT; 104 return -EFAULT;
105 /* 105 /*
106 * Reset cpu_id to RSEQ_CPU_ID_UNINITIALIZED, so any user coming 106 * Reset cpu_id to RSEQ_CPU_ID_UNINITIALIZED, so any user coming
107 * in after unregistration can figure out that rseq needs to be 107 * in after unregistration can figure out that rseq needs to be
108 * registered again. 108 * registered again.
109 */ 109 */
110 if (__put_user(cpu_id, &t->rseq->cpu_id)) 110 if (put_user(cpu_id, &t->rseq->cpu_id))
111 return -EFAULT; 111 return -EFAULT;
112 return 0; 112 return 0;
113} 113}
@@ -115,29 +115,36 @@ static int rseq_reset_rseq_cpu_id(struct task_struct *t)
115static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs) 115static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs)
116{ 116{
117 struct rseq_cs __user *urseq_cs; 117 struct rseq_cs __user *urseq_cs;
118 unsigned long ptr; 118 u64 ptr;
119 u32 __user *usig; 119 u32 __user *usig;
120 u32 sig; 120 u32 sig;
121 int ret; 121 int ret;
122 122
123 ret = __get_user(ptr, &t->rseq->rseq_cs); 123 if (copy_from_user(&ptr, &t->rseq->rseq_cs.ptr64, sizeof(ptr)))
124 if (ret) 124 return -EFAULT;
125 return ret;
126 if (!ptr) { 125 if (!ptr) {
127 memset(rseq_cs, 0, sizeof(*rseq_cs)); 126 memset(rseq_cs, 0, sizeof(*rseq_cs));
128 return 0; 127 return 0;
129 } 128 }
130 urseq_cs = (struct rseq_cs __user *)ptr; 129 if (ptr >= TASK_SIZE)
130 return -EINVAL;
131 urseq_cs = (struct rseq_cs __user *)(unsigned long)ptr;
131 if (copy_from_user(rseq_cs, urseq_cs, sizeof(*rseq_cs))) 132 if (copy_from_user(rseq_cs, urseq_cs, sizeof(*rseq_cs)))
132 return -EFAULT; 133 return -EFAULT;
133 if (rseq_cs->version > 0)
134 return -EINVAL;
135 134
135 if (rseq_cs->start_ip >= TASK_SIZE ||
136 rseq_cs->start_ip + rseq_cs->post_commit_offset >= TASK_SIZE ||
137 rseq_cs->abort_ip >= TASK_SIZE ||
138 rseq_cs->version > 0)
139 return -EINVAL;
140 /* Check for overflow. */
141 if (rseq_cs->start_ip + rseq_cs->post_commit_offset < rseq_cs->start_ip)
142 return -EINVAL;
136 /* Ensure that abort_ip is not in the critical section. */ 143 /* Ensure that abort_ip is not in the critical section. */
137 if (rseq_cs->abort_ip - rseq_cs->start_ip < rseq_cs->post_commit_offset) 144 if (rseq_cs->abort_ip - rseq_cs->start_ip < rseq_cs->post_commit_offset)
138 return -EINVAL; 145 return -EINVAL;
139 146
140 usig = (u32 __user *)(rseq_cs->abort_ip - sizeof(u32)); 147 usig = (u32 __user *)(unsigned long)(rseq_cs->abort_ip - sizeof(u32));
141 ret = get_user(sig, usig); 148 ret = get_user(sig, usig);
142 if (ret) 149 if (ret)
143 return ret; 150 return ret;
@@ -146,7 +153,7 @@ static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs)
146 printk_ratelimited(KERN_WARNING 153 printk_ratelimited(KERN_WARNING
147 "Possible attack attempt. Unexpected rseq signature 0x%x, expecting 0x%x (pid=%d, addr=%p).\n", 154 "Possible attack attempt. Unexpected rseq signature 0x%x, expecting 0x%x (pid=%d, addr=%p).\n",
148 sig, current->rseq_sig, current->pid, usig); 155 sig, current->rseq_sig, current->pid, usig);
149 return -EPERM; 156 return -EINVAL;
150 } 157 }
151 return 0; 158 return 0;
152} 159}
@@ -157,7 +164,7 @@ static int rseq_need_restart(struct task_struct *t, u32 cs_flags)
157 int ret; 164 int ret;
158 165
159 /* Get thread flags. */ 166 /* Get thread flags. */
160 ret = __get_user(flags, &t->rseq->flags); 167 ret = get_user(flags, &t->rseq->flags);
161 if (ret) 168 if (ret)
162 return ret; 169 return ret;
163 170
@@ -195,9 +202,11 @@ static int clear_rseq_cs(struct task_struct *t)
195 * of code outside of the rseq assembly block. This performs 202 * of code outside of the rseq assembly block. This performs
196 * a lazy clear of the rseq_cs field. 203 * a lazy clear of the rseq_cs field.
197 * 204 *
198 * Set rseq_cs to NULL with single-copy atomicity. 205 * Set rseq_cs to NULL.
199 */ 206 */
200 return __put_user(0UL, &t->rseq->rseq_cs); 207 if (clear_user(&t->rseq->rseq_cs.ptr64, sizeof(t->rseq->rseq_cs.ptr64)))
208 return -EFAULT;
209 return 0;
201} 210}
202 211
203/* 212/*