Skip to content

Commit 79e10da

Browse files
mjeansonIngo Molnar
authored andcommitted
rseq: Update kernel fields in lockstep with CONFIG_DEBUG_RSEQ=y
With CONFIG_DEBUG_RSEQ=y, an in-kernel copy of the read-only fields is kept synchronized with the user-space fields. Ensure the updates are done in lockstep in case we error out on a write to user-space. Fixes: 7d5265f ("rseq: Validate read-only fields under DEBUG_RSEQ config") Signed-off-by: Michael Jeanson <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Reviewed-by: Mathieu Desnoyers <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 3c27b40 commit 79e10da

File tree

1 file changed

+40
-40
lines changed

1 file changed

+40
-40
lines changed

kernel/rseq.c

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -78,24 +78,24 @@ static int rseq_validate_ro_fields(struct task_struct *t)
7878
return -EFAULT;
7979
}
8080

81-
static void rseq_set_ro_fields(struct task_struct *t, u32 cpu_id_start, u32 cpu_id,
82-
u32 node_id, u32 mm_cid)
83-
{
84-
rseq_kernel_fields(t)->cpu_id_start = cpu_id;
85-
rseq_kernel_fields(t)->cpu_id = cpu_id;
86-
rseq_kernel_fields(t)->node_id = node_id;
87-
rseq_kernel_fields(t)->mm_cid = mm_cid;
88-
}
81+
/*
82+
* Update an rseq field and its in-kernel copy in lock-step to keep a coherent
83+
* state.
84+
*/
85+
#define rseq_unsafe_put_user(t, value, field, error_label) \
86+
do { \
87+
unsafe_put_user(value, &t->rseq->field, error_label); \
88+
rseq_kernel_fields(t)->field = value; \
89+
} while (0)
90+
8991
#else
9092
static int rseq_validate_ro_fields(struct task_struct *t)
9193
{
9294
return 0;
9395
}
9496

95-
static void rseq_set_ro_fields(struct task_struct *t, u32 cpu_id_start, u32 cpu_id,
96-
u32 node_id, u32 mm_cid)
97-
{
98-
}
97+
#define rseq_unsafe_put_user(t, value, field, error_label) \
98+
unsafe_put_user(value, &t->rseq->field, error_label)
9999
#endif
100100

101101
/*
@@ -173,17 +173,18 @@ static int rseq_update_cpu_node_id(struct task_struct *t)
173173
WARN_ON_ONCE((int) mm_cid < 0);
174174
if (!user_write_access_begin(rseq, t->rseq_len))
175175
goto efault;
176-
unsafe_put_user(cpu_id, &rseq->cpu_id_start, efault_end);
177-
unsafe_put_user(cpu_id, &rseq->cpu_id, efault_end);
178-
unsafe_put_user(node_id, &rseq->node_id, efault_end);
179-
unsafe_put_user(mm_cid, &rseq->mm_cid, efault_end);
176+
177+
rseq_unsafe_put_user(t, cpu_id, cpu_id_start, efault_end);
178+
rseq_unsafe_put_user(t, cpu_id, cpu_id, efault_end);
179+
rseq_unsafe_put_user(t, node_id, node_id, efault_end);
180+
rseq_unsafe_put_user(t, mm_cid, mm_cid, efault_end);
181+
180182
/*
181183
* Additional feature fields added after ORIG_RSEQ_SIZE
182184
* need to be conditionally updated only if
183185
* t->rseq_len != ORIG_RSEQ_SIZE.
184186
*/
185187
user_write_access_end();
186-
rseq_set_ro_fields(t, cpu_id, cpu_id, node_id, mm_cid);
187188
trace_rseq_update(t);
188189
return 0;
189190

@@ -195,45 +196,44 @@ static int rseq_update_cpu_node_id(struct task_struct *t)
195196

196197
static int rseq_reset_rseq_cpu_node_id(struct task_struct *t)
197198
{
199+
struct rseq __user *rseq = t->rseq;
198200
u32 cpu_id_start = 0, cpu_id = RSEQ_CPU_ID_UNINITIALIZED, node_id = 0,
199201
mm_cid = 0;
200202

201203
/*
202204
* Validate read-only rseq fields.
203205
*/
204206
if (rseq_validate_ro_fields(t))
205-
return -EFAULT;
206-
/*
207-
* Reset cpu_id_start to its initial state (0).
208-
*/
209-
if (put_user(cpu_id_start, &t->rseq->cpu_id_start))
210-
return -EFAULT;
211-
/*
212-
* Reset cpu_id to RSEQ_CPU_ID_UNINITIALIZED, so any user coming
213-
* in after unregistration can figure out that rseq needs to be
214-
* registered again.
215-
*/
216-
if (put_user(cpu_id, &t->rseq->cpu_id))
217-
return -EFAULT;
218-
/*
219-
* Reset node_id to its initial state (0).
220-
*/
221-
if (put_user(node_id, &t->rseq->node_id))
222-
return -EFAULT;
207+
goto efault;
208+
209+
if (!user_write_access_begin(rseq, t->rseq_len))
210+
goto efault;
211+
223212
/*
224-
* Reset mm_cid to its initial state (0).
213+
* Reset all fields to their initial state.
214+
*
215+
* All fields have an initial state of 0 except cpu_id which is set to
216+
* RSEQ_CPU_ID_UNINITIALIZED, so that any user coming in after
217+
* unregistration can figure out that rseq needs to be registered
218+
* again.
225219
*/
226-
if (put_user(mm_cid, &t->rseq->mm_cid))
227-
return -EFAULT;
228-
229-
rseq_set_ro_fields(t, cpu_id_start, cpu_id, node_id, mm_cid);
220+
rseq_unsafe_put_user(t, cpu_id_start, cpu_id_start, efault_end);
221+
rseq_unsafe_put_user(t, cpu_id, cpu_id, efault_end);
222+
rseq_unsafe_put_user(t, node_id, node_id, efault_end);
223+
rseq_unsafe_put_user(t, mm_cid, mm_cid, efault_end);
230224

231225
/*
232226
* Additional feature fields added after ORIG_RSEQ_SIZE
233227
* need to be conditionally reset only if
234228
* t->rseq_len != ORIG_RSEQ_SIZE.
235229
*/
230+
user_write_access_end();
236231
return 0;
232+
233+
efault_end:
234+
user_write_access_end();
235+
efault:
236+
return -EFAULT;
237237
}
238238

239239
static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs)

0 commit comments

Comments
 (0)