Commit d63fba04 authored by James Morse's avatar James Morse Committed by Yang Yingliang
Browse files

ptrace: Add compat PTRACE_{G, S}ETSIGMASK handlers



hulk inclusion
category: feature
bugzilla: NA
CVE: NA
---------------------------

compat_ptrace_request() lacks handlers for PTRACE_{G,S}ETSIGMASK,
instead using those in ptrace_request(). The compat variant should
read a compat_sigset_t from userspace instead of ptrace_request()s
sigset_t.

While compat_sigset_t is the same size as sigset_t, it is defined as
2xu32, instead of a single u64. On a big-endian CPU this means that
compat_sigset_t is passed to user-space using middle-endianness,
where the least-significant u32 is written most significant byte
first.

If ptrace_request()s code is used userspace will read the most
significant u32 where it expected the least significant.

Instead of duplicating ptrace_request()s code as a special case in
the arch code, handle it here.

Fixes: 29000cae ("ptrace: add ability to get/set signal-blocked mask")
CC: Andrey Vagin <avagin@openvz.org>
Signed-off-by: default avatarJames Morse <james.morse@arm.com>

Yury:
Replace sigset_{to,from}_compat() with new {get,put}_compat_sigset()
Signed-off-by: default avatarYury Norov <ynorov@caviumnetworks.com>

 Conflicts:
	kernel/ptrace.c
[wangxiongfeng: conflicts because of the following patch
commit 4afa2bd35  ptrace: take into account saved_sigmask in PTRACE{GET,
SET}SIGMASK
Fix it by calling 'clear_tsk_restore_sigmask()' after
'ptrace_setsigmask()']

Signed-off-by: default avatarXiongfeng Wang <wangxiongfeng2@huawei.com>
Reviewed-by: default avatarHanjun Guo &lt;guohanjun@huawei.com <mailto:guohanjun@huawei.com&gt;>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
parent c190ba97
Loading
Loading
Loading
Loading
+36 −11
Original line number Diff line number Diff line
@@ -895,6 +895,22 @@ static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
EXPORT_SYMBOL_GPL(task_user_regset_view);
#endif

static int ptrace_setsigmask(struct task_struct *child, sigset_t *new_set)
{
	sigdelsetmask(new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));

	/*
	 * Every thread does recalc_sigpending() after resume, so
	 * retarget_shared_pending() and recalc_sigpending() are not
	 * called here.
	 */
	spin_lock_irq(&child->sighand->siglock);
	child->blocked = *new_set;
	spin_unlock_irq(&child->sighand->siglock);

	return 0;
}

int ptrace_request(struct task_struct *child, long request,
		   unsigned long addr, unsigned long data)
{
@@ -974,20 +990,10 @@ int ptrace_request(struct task_struct *child, long request,
			break;
		}

		sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));

		/*
		 * Every thread does recalc_sigpending() after resume, so
		 * retarget_shared_pending() and recalc_sigpending() are not
		 * called here.
		 */
		spin_lock_irq(&child->sighand->siglock);
		child->blocked = new_set;
		spin_unlock_irq(&child->sighand->siglock);
		ret = ptrace_setsigmask(child, &new_set);

		clear_tsk_restore_sigmask(child);

		ret = 0;
		break;
	}

@@ -1206,6 +1212,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
{
	compat_ulong_t __user *datap = compat_ptr(data);
	compat_ulong_t word;
	sigset_t new_set;
	siginfo_t siginfo;
	int ret;

@@ -1246,6 +1253,24 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
		else
			ret = ptrace_setsiginfo(child, &siginfo);
		break;
	case PTRACE_GETSIGMASK:
		if (addr != sizeof(compat_sigset_t))
			return -EINVAL;

		ret = put_compat_sigset((compat_sigset_t __user *) datap,
				&child->blocked, sizeof(compat_sigset_t));
		break;
	case PTRACE_SETSIGMASK:
		if (addr != sizeof(compat_sigset_t))
			return -EINVAL;

		ret = get_compat_sigset(&new_set,
				(compat_sigset_t __user *) datap);
		if (ret)
			break;

		ret = ptrace_setsigmask(child, &new_set);
		break;
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
	case PTRACE_GETREGSET:
	case PTRACE_SETREGSET: