Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ef2d523 authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky
Browse files

s390/ptrace: fix guarded storage regset handling



If the guarded storage regset for current is supposed to be changed,
the regset from user space is copied directly into the guarded storage
control block.

If then the process gets scheduled away while the control block is
being copied and before the new control block has been loaded, the
result is random: the process can be scheduled away due to a page
fault or preemption. If that happens the already copied parts will be
overwritten by save_gs_cb(), called from switch_to().

Avoid this by copying the data to a temporary buffer on the stack and
do the actual update with preemption disabled.

Fixes: f5bbd721 ("s390/ptrace: guarded storage regset for the current task")
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent fa1edf3f
Loading
Loading
Loading
Loading
+22 −11
Original line number Original line Diff line number Diff line
@@ -1171,26 +1171,37 @@ static int s390_gs_cb_set(struct task_struct *target,
			  unsigned int pos, unsigned int count,
			  unsigned int pos, unsigned int count,
			  const void *kbuf, const void __user *ubuf)
			  const void *kbuf, const void __user *ubuf)
{
{
	struct gs_cb *data = target->thread.gs_cb;
	struct gs_cb gs_cb = { }, *data = NULL;
	int rc;
	int rc;


	if (!MACHINE_HAS_GS)
	if (!MACHINE_HAS_GS)
		return -ENODEV;
		return -ENODEV;
	if (!data) {
	if (!target->thread.gs_cb) {
		data = kzalloc(sizeof(*data), GFP_KERNEL);
		data = kzalloc(sizeof(*data), GFP_KERNEL);
		if (!data)
		if (!data)
			return -ENOMEM;
			return -ENOMEM;
		data->gsd = 25;
	}
	if (!target->thread.gs_cb)
		gs_cb.gsd = 25;
	else if (target == current)
		save_gs_cb(&gs_cb);
	else
		gs_cb = *target->thread.gs_cb;
	rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
				&gs_cb, 0, sizeof(gs_cb));
	if (rc) {
		kfree(data);
		return -EFAULT;
	}
	preempt_disable();
	if (!target->thread.gs_cb)
		target->thread.gs_cb = data;
		target->thread.gs_cb = data;
		if (target == current)
	*target->thread.gs_cb = gs_cb;
	if (target == current) {
		__ctl_set_bit(2, 4);
		__ctl_set_bit(2, 4);
	} else if (target == current) {
		restore_gs_cb(target->thread.gs_cb);
		save_gs_cb(data);
	}
	}
	rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
	preempt_enable();
				data, 0, sizeof(struct gs_cb));
	if (target == current)
		restore_gs_cb(data);
	return rc;
	return rc;
}
}