diff options
Diffstat (limited to 'kernel/rseq.c')
-rw-r--r-- | kernel/rseq.c | 41 |
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) | |||
115 | static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs) | 115 | static 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 | /* |