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

Commit 3dbc4f54 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull seccomp updates from James Morris:
 "Add support for retrieving seccomp metadata"

* 'next-seccomp' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  ptrace, seccomp: add support for retrieving seccomp metadata
  seccomp: hoist out filter resolving logic
parents ae0cb7be 26025bf5
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -95,11 +95,19 @@ static inline void get_seccomp_filter(struct task_struct *tsk)
#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
extern long seccomp_get_filter(struct task_struct *task,
			       unsigned long filter_off, void __user *data);
extern long seccomp_get_metadata(struct task_struct *task,
				 unsigned long filter_off, void __user *data);
#else
static inline long seccomp_get_filter(struct task_struct *task,
				      unsigned long n, void __user *data)
{
	return -EINVAL;
}
static inline long seccomp_get_metadata(struct task_struct *task,
					unsigned long filter_off,
					void __user *data)
{
	return -EINVAL;
}
#endif /* CONFIG_SECCOMP_FILTER && CONFIG_CHECKPOINT_RESTORE */
#endif /* _LINUX_SECCOMP_H */
+6 −0
Original line number Diff line number Diff line
@@ -66,6 +66,12 @@ struct ptrace_peeksiginfo_args {
#define PTRACE_SETSIGMASK	0x420b

#define PTRACE_SECCOMP_GET_FILTER	0x420c
#define PTRACE_SECCOMP_GET_METADATA	0x420d

struct seccomp_metadata {
	unsigned long filter_off;	/* Input: which filter */
	unsigned int flags;		/* Output: filter's flags */
};

/* Read signals from a shared (process wide) queue */
#define PTRACE_PEEKSIGINFO_SHARED	(1 << 0)
+4 −0
Original line number Diff line number Diff line
@@ -1092,6 +1092,10 @@ int ptrace_request(struct task_struct *child, long request,
		ret = seccomp_get_filter(child, addr, datavp);
		break;

	case PTRACE_SECCOMP_GET_METADATA:
		ret = seccomp_get_metadata(child, addr, datavp);
		break;

	default:
		break;
	}
+76 −30
Original line number Diff line number Diff line
@@ -978,49 +978,68 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
}

#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
			void __user *data)
static struct seccomp_filter *get_nth_filter(struct task_struct *task,
					     unsigned long filter_off)
{
	struct seccomp_filter *filter;
	struct sock_fprog_kern *fprog;
	long ret;
	unsigned long count = 0;

	if (!capable(CAP_SYS_ADMIN) ||
	    current->seccomp.mode != SECCOMP_MODE_DISABLED) {
		return -EACCES;
	}
	struct seccomp_filter *orig, *filter;
	unsigned long count;

	/*
	 * Note: this is only correct because the caller should be the (ptrace)
	 * tracer of the task, otherwise lock_task_sighand is needed.
	 */
	spin_lock_irq(&task->sighand->siglock);

	if (task->seccomp.mode != SECCOMP_MODE_FILTER) {
		ret = -EINVAL;
		goto out;
		spin_unlock_irq(&task->sighand->siglock);
		return ERR_PTR(-EINVAL);
	}

	filter = task->seccomp.filter;
	while (filter) {
		filter = filter->prev;
	orig = task->seccomp.filter;
	__get_seccomp_filter(orig);
	spin_unlock_irq(&task->sighand->siglock);

	count = 0;
	for (filter = orig; filter; filter = filter->prev)
		count++;
	}

	if (filter_off >= count) {
		ret = -ENOENT;
		filter = ERR_PTR(-ENOENT);
		goto out;
	}
	count -= filter_off;

	filter = task->seccomp.filter;
	while (filter && count > 1) {
		filter = filter->prev;
	count -= filter_off;
	for (filter = orig; filter && count > 1; filter = filter->prev)
		count--;
	}

	if (WARN_ON(count != 1 || !filter)) {
		/* The filter tree shouldn't shrink while we're using it. */
		ret = -ENOENT;
		filter = ERR_PTR(-ENOENT);
		goto out;
	}

	__get_seccomp_filter(filter);

out:
	__put_seccomp_filter(orig);
	return filter;
}

long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
			void __user *data)
{
	struct seccomp_filter *filter;
	struct sock_fprog_kern *fprog;
	long ret;

	if (!capable(CAP_SYS_ADMIN) ||
	    current->seccomp.mode != SECCOMP_MODE_DISABLED) {
		return -EACCES;
	}

	filter = get_nth_filter(task, filter_off);
	if (IS_ERR(filter))
		return PTR_ERR(filter);

	fprog = filter->prog->orig_prog;
	if (!fprog) {
		/* This must be a new non-cBPF filter, since we save
@@ -1035,17 +1054,44 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
	if (!data)
		goto out;

	__get_seccomp_filter(filter);
	spin_unlock_irq(&task->sighand->siglock);

	if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
		ret = -EFAULT;

out:
	__put_seccomp_filter(filter);
	return ret;
}

out:
	spin_unlock_irq(&task->sighand->siglock);
long seccomp_get_metadata(struct task_struct *task,
			  unsigned long size, void __user *data)
{
	long ret;
	struct seccomp_filter *filter;
	struct seccomp_metadata kmd = {};

	if (!capable(CAP_SYS_ADMIN) ||
	    current->seccomp.mode != SECCOMP_MODE_DISABLED) {
		return -EACCES;
	}

	size = min_t(unsigned long, size, sizeof(kmd));

	if (copy_from_user(&kmd, data, size))
		return -EFAULT;

	filter = get_nth_filter(task, kmd.filter_off);
	if (IS_ERR(filter))
		return PTR_ERR(filter);

	memset(&kmd, 0, sizeof(kmd));
	if (filter->log)
		kmd.flags |= SECCOMP_FILTER_FLAG_LOG;

	ret = size;
	if (copy_to_user(data, &kmd, size))
		ret = -EFAULT;

	__put_seccomp_filter(filter);
	return ret;
}
#endif