diff options
Diffstat (limited to 'kernel')
| -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 | /* |
