All of lore.kernel.org
 help / color / mirror / Atom feed
* ptrace_syscall_dropped.c:298:TRACE_syscall.ptrace_syscall_dropped:Expected 1 (1) == syscall(286) (4294967295)
@ 2018-05-26 19:50 Mathieu Malaterre
  0 siblings, 0 replies; only message in thread
From: Mathieu Malaterre @ 2018-05-26 19:50 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 1595 bytes --]

Hi,

On Tue, May 8, 2018 at 4:34 PM, Michael Ellerman <mpe@ellerman.id.au> wrote:
> Mathieu Malaterre <malat@debian.org> writes:
>
>> Hi there,
>>
>> Quick question (I have not investigate root cause): is support for
>> seccomp complete on ppc32 ?
>
> Doesn't look like it does it :)
>
>> $ make KBUILD_OUTPUT=/tmp/kselftest TARGETS=seccomp kselftest
>> ...
>> seccomp_bpf.c:1804:TRACE_syscall.ptrace_syscall_dropped:Expected 1 (1)
>> == syscall(286) (4294967295)
>> TRACE_syscall.ptrace_syscall_dropped: Test failed at step #13
>> [     FAIL ] TRACE_syscall.ptrace_syscall_dropped
>> ...
>> [ RUN      ] global.get_metadata
>> seccomp_bpf.c:2880:global.get_metadata:Expected 0 (0) == seccomp(1, 2,
>> &prog) (4294967295)
>> seccomp_bpf.c:2892:global.get_metadata:Expected 1 (1) ==
>> read(pipefd[0], &buf, 1) (0)
>> global.get_metadata: Test terminated by assertion
>> [     FAIL ] global.get_metadata
>
> I'm not sure sorry.
>
> That could be a test case bug, hard to say without looking at the
> details.

I've reduced the test case to the attached file. Does that help, or
should I reduce it some more ?

$ gcc -m32 -o ptrace_syscall_dropped ptrace_syscall_dropped.c

running it as root:

# ./ptrace_syscall_dropped
[==========] Running 1 tests from 1 test cases.
[ RUN      ] TRACE_syscall.ptrace_syscall_dropped
ptrace_syscall_dropped.c:298:TRACE_syscall.ptrace_syscall_dropped:Expected
1 (1) == syscall(286) (4294967295)
TRACE_syscall.ptrace_syscall_dropped: Test failed at step #13
[     FAIL ] TRACE_syscall.ptrace_syscall_dropped
[==========] 0 / 1 tests passed.
[  FAILED  ]


Thanks

[-- Attachment #2: ptrace_syscall_dropped.c --]
[-- Type: text/x-csrc, Size: 7743 bytes --]

#include <linux/filter.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <linux/ptrace.h>
#include <linux/seccomp.h>
#include <linux/elf.h>
#include <sys/uio.h>

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>

#include "tools/testing/selftests/kselftest_harness.h"

#define IS_SECCOMP_EVENT(status) ((status >> 16) == PTRACE_EVENT_SECCOMP)
static bool tracer_running;
static void tracer_stop(int sig)
{
	tracer_running = false;
}

typedef void tracer_func_t(struct __test_metadata *_metadata,
			   pid_t tracee, int status, void *args);

static void start_tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
	    tracer_func_t tracer_func, void *args, bool ptrace_syscall)
{
	int ret = -1;
	struct sigaction action = {
		.sa_handler = tracer_stop,
	};

	/* Allow external shutdown. */
	tracer_running = true;
	ASSERT_EQ(0, sigaction(SIGUSR1, &action, NULL));

	errno = 0;
	while (ret == -1 && errno != EINVAL)
		ret = ptrace(PTRACE_ATTACH, tracee, NULL, 0);
	ASSERT_EQ(0, ret) {
		kill(tracee, SIGKILL);
	}
	/* Wait for attach stop */
	wait(NULL);

	ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, ptrace_syscall ?
						      PTRACE_O_TRACESYSGOOD :
						      PTRACE_O_TRACESECCOMP);
	ASSERT_EQ(0, ret) {
		TH_LOG("Failed to set PTRACE_O_TRACESECCOMP");
		kill(tracee, SIGKILL);
	}
	ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT,
		     tracee, NULL, 0);
	ASSERT_EQ(0, ret);

	/* Unblock the tracee */
	ASSERT_EQ(1, write(fd, "A", 1));
	ASSERT_EQ(0, close(fd));

	/* Run until we're shut down. Must assert to stop execution. */
	while (tracer_running) {
		int status;

		if (wait(&status) != tracee)
			continue;
		if (WIFSIGNALED(status) || WIFEXITED(status))
			/* Child is dead. Time to go. */
			return;

		/* Check if this is a seccomp event. */
		ASSERT_EQ(!ptrace_syscall, IS_SECCOMP_EVENT(status));

		tracer_func(_metadata, tracee, status, args);

		ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT,
			     tracee, NULL, 0);
		ASSERT_EQ(0, ret);
	}
	/* Directly report the status of our test harness results. */
	syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Common tracer setup/teardown functions. */
static void cont_handler(int num)
{ }
static pid_t setup_trace_fixture(struct __test_metadata *_metadata,
			  tracer_func_t func, void *args, bool ptrace_syscall)
{
	char sync;
	int pipefd[2];
	pid_t tracer_pid;
	pid_t tracee = getpid();

	/* Setup a pipe for clean synchronization. */
	ASSERT_EQ(0, pipe(pipefd));

	/* Fork a child which we'll promote to tracer */
	tracer_pid = fork();
	ASSERT_LE(0, tracer_pid);
	signal(SIGALRM, cont_handler);
	if (tracer_pid == 0) {
		close(pipefd[0]);
		start_tracer(_metadata, pipefd[1], tracee, func, args,
			     ptrace_syscall);
		syscall(__NR_exit, 0);
	}
	close(pipefd[1]);
	prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0);
	read(pipefd[0], &sync, 1);
	close(pipefd[0]);

	return tracer_pid;
}
static void teardown_trace_fixture(struct __test_metadata *_metadata,
			    pid_t tracer)
{
	if (tracer) {
		int status;
		/*
		 * Extract the exit code from the other process and
		 * adopt it for ourselves in case its asserts failed.
		 */
		ASSERT_EQ(0, kill(tracer, SIGUSR1));
		ASSERT_EQ(tracer, waitpid(tracer, &status, 0));
		if (WEXITSTATUS(status))
			_metadata->passed = 0;
	}
}

# define ARCH_REGS	struct pt_regs
# define SYSCALL_NUM	gpr[0]
# define SYSCALL_RET	gpr[3]

/* Architecture-specific syscall fetching routine. */
static int get_syscall(struct __test_metadata *_metadata, pid_t tracee)
{
	ARCH_REGS regs;
	struct iovec iov;

	iov.iov_base = &regs;
	iov.iov_len = sizeof(regs);
	EXPECT_EQ(0, ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov)) {
		TH_LOG("PTRACE_GETREGSET failed");
		return -1;
	}

	return regs.SYSCALL_NUM;
}

/* Architecture-specific syscall changing routine. */
static void change_syscall(struct __test_metadata *_metadata,
		    pid_t tracee, int syscall)
{
	int ret;
	ARCH_REGS regs;
	struct iovec iov;
	iov.iov_base = &regs;
	iov.iov_len = sizeof(regs);
	ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov);
	EXPECT_EQ(0, ret) {}

	{
		regs.SYSCALL_NUM = syscall;
	}

	/* If syscall is skipped, change return value. */
	if (syscall == -1)
		regs.SYSCALL_RET = EPERM;

	iov.iov_base = &regs;
	iov.iov_len = sizeof(regs);
	ret = ptrace(PTRACE_SETREGSET, tracee, NT_PRSTATUS, &iov);
	EXPECT_EQ(0, ret);
}

static void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee,
		    int status, void *args)
{
	int ret;
	unsigned long msg;

	/* Make sure we got the right message. */
	ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
	EXPECT_EQ(0, ret);

	/* Validate and take action on expected syscalls. */
	switch (msg) {
	case 0x1002:
		/* change getpid to getppid. */
		EXPECT_EQ(__NR_getpid, get_syscall(_metadata, tracee));
		change_syscall(_metadata, tracee, __NR_getppid);
		break;
	case 0x1003:
		/* skip gettid. */
		EXPECT_EQ(__NR_gettid, get_syscall(_metadata, tracee));
		change_syscall(_metadata, tracee, -1);
		break;
	case 0x1004:
		/* do nothing (allow getppid) */
		EXPECT_EQ(__NR_getppid, get_syscall(_metadata, tracee));
		break;
	default:
		EXPECT_EQ(0, msg) {
			TH_LOG("Unknown PTRACE_GETEVENTMSG: 0x%lx", msg);
			kill(tracee, SIGKILL);
		}
	}

}

static void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
		   int status, void *args)
{
	int ret, nr;
	unsigned long msg;
	static bool entry;

	/* Make sure we got an empty message. */
	ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
	EXPECT_EQ(0, ret);
	EXPECT_EQ(0, msg);

	/* The only way to tell PTRACE_SYSCALL entry/exit is by counting. */
	entry = !entry;
	if (!entry)
		return;

	nr = get_syscall(_metadata, tracee);

	if (nr == __NR_getpid)
		change_syscall(_metadata, tracee, __NR_getppid);
	if (nr == __NR_openat)
		change_syscall(_metadata, tracee, -1);
}

FIXTURE_DATA(TRACE_syscall) {
	struct sock_fprog prog;
	pid_t tracer, mytid, mypid, parent;
};

FIXTURE_SETUP(TRACE_syscall)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
			offsetof(struct seccomp_data, nr)),
		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1002),
		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_gettid, 0, 1),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1003),
		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1004),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
	};

	memset(&self->prog, 0, sizeof(self->prog));
	self->prog.filter = malloc(sizeof(filter));
	ASSERT_NE(NULL, self->prog.filter);
	memcpy(self->prog.filter, filter, sizeof(filter));
	self->prog.len = (unsigned short)ARRAY_SIZE(filter);

	/* Prepare some testable syscall results. */
	self->mytid = syscall(__NR_gettid);
	ASSERT_GT(self->mytid, 0);
	ASSERT_NE(self->mytid, 1) {
		TH_LOG("Running this test as init is not supported. :)");
	}

	self->mypid = getpid();
	ASSERT_GT(self->mypid, 0);
	ASSERT_EQ(self->mytid, self->mypid);

	self->parent = getppid();
	ASSERT_GT(self->parent, 0);
	ASSERT_NE(self->parent, self->mypid);

	/* Launch tracer. */
	self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL,
					   false);
}

FIXTURE_TEARDOWN(TRACE_syscall)
{
	teardown_trace_fixture(_metadata, self->tracer);
	if (self->prog.filter)
		free(self->prog.filter);
}

TEST_F(TRACE_syscall, ptrace_syscall_dropped)
{
	/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
	teardown_trace_fixture(_metadata, self->tracer);
	self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
					   true);

	/* Tracer should skip the open syscall, resulting in EPERM. */
	EXPECT_EQ(EPERM, syscall(__NR_openat));
}

TEST_HARNESS_MAIN

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2018-05-26 19:50 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-26 19:50 ptrace_syscall_dropped.c:298:TRACE_syscall.ptrace_syscall_dropped:Expected 1 (1) == syscall(286) (4294967295) Mathieu Malaterre

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.