All of lore.kernel.org
 help / color / mirror / Atom feed
* ptrace interface does not permit modification of syscall return
@ 2015-12-21 17:55 Mike Frysinger
  2015-12-22 21:10 ` Helge Deller
  0 siblings, 1 reply; 4+ messages in thread
From: Mike Frysinger @ 2015-12-21 17:55 UTC (permalink / raw)
  To: linux-parisc


[-- Attachment #1.1: Type: text/plain, Size: 1529 bytes --]

i have a ptrace program that watches for specific syscalls and when
matched, will:
 - on entry change the syscall nr to -1 (so the kernel will skip it)
 - on exit change the return to -EPERM so the userspace sees a denial

i have this working on most arches (x86, x86_64, arm, alpha, ia64, etc...).
on parisc, the kernel (using 3.18.7 currently) appears to be wrong.  in my
tests, if i don't mess with the syscall nr, i can change the return value
fine (to EPERM or whatever).  but the syscall executed which i do not want.
if i change the syscall to -1, then i can't change the return value (so the
child sees ENOSYS), but the kernel still executes the original syscall.

i have a simple test case attached to show the issue.  the code does:
 - spawn a child with the parent tracing it
 - child will do:
  - dupe stderr to another fd
  - unlink a file named ".test.flag"
  - write a message through the new fd
  - close a magic # so the parent knows to start denying
    - should see EPERM but it sees ENOSYS
  - close the new fd
    - should see EPERM but it is closed!
  - write to the new fd
    - should work, but the fd is closed
  - call create on ".test.flag"
    - should see EPERM, but the file is created!
 - parent will do:
  - log the syscalls until child runs close(-12345)
  - the parent will then try to deny all close/creat calls
  - uses PTRACE_POKEUSER w/PT_GR20 to set syscall to -1
  - uses PTRACE_POKEUSER w/PT_GR28 to set return to -EPERM

you can run the test case by doing:
$ gcc test.c && ./a.out
-mike

[-- Attachment #1.2: test.c --]
[-- Type: text/x-c, Size: 5650 bytes --]

#define _GNU_SOURCE

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <asm/offset.h>
#include <asm/ptrace.h>

static pid_t trace_pid;

static long _do_ptrace(enum __ptrace_request request, const char *srequest, void *addr, void *data)
{
	long ret;
 try_again:
	errno = 0;
	ret = ptrace(request, trace_pid, addr, data);
	if (ret == -1) {
		/* Child hasn't gotten to the next marker yet ? */
		if (errno == ESRCH) {
			int status;
			if (waitpid(trace_pid, &status, 0) == -1) {
				/* nah, it's dead ... should we whine though ? */
				_exit(0);
			}
			sched_yield();
			goto try_again;
		} else if (errno == EIO || errno == EFAULT) {
			/* This comes up when the child itself tries to use a bad pointer.
			 * That's not something the sandbox should abort on. #560396
			 */
			return ret;
		} else if (!errno)
			if (request == PTRACE_PEEKDATA ||
			    request == PTRACE_PEEKTEXT ||
			    request == PTRACE_PEEKUSER)
				return ret;

		err(1, "do_ptrace: ptrace(%s, ..., %p, %p)", srequest, addr, data);
	}
	return ret;
}
#define do_ptrace(request, addr, data) _do_ptrace(request, #request, addr, data)

static long do_peekuser(long offset)
{
	return do_ptrace(PTRACE_PEEKUSER, (void *)offset, NULL);
}

static long do_pokeuser(long offset, long val)
{
	return do_ptrace(PTRACE_POKEUSER, (void *)offset, (void *)val);
}

static void trace_child_signal(int signo, siginfo_t *info, void *context)
{
#if 0
	warnx("got sig %s(%i): code:%s(%i) status:%s(%i)",
		strsignal(signo), signo,
		"---", info->si_code,
		strsignal(info->si_status), info->si_status);
#endif

	switch (info->si_code) {
		case CLD_DUMPED:
		case CLD_KILLED:
			_exit(128 + info->si_status);

		case CLD_EXITED:
			_exit(info->si_status);

		case CLD_TRAPPED:
			switch (info->si_status) {
				case SIGSTOP:
					kill(trace_pid, SIGCONT);
				case SIGTRAP:
				case SIGCONT:
					return;
			}

			/* For whatever signal the child caught, let's ignore it and
			 * continue on.  If it aborted, segfaulted, whatever, that's
			 * its problem, not ours, so don't whine about it.  We just
			 * have to be sure to bubble it back up.  #265072
			 */
			do_ptrace(PTRACE_CONT, NULL, (void *)(long)info->si_status);
			return;
	}

	errx(1, "unhandled signal case");
}

static const char *lookup_syscall(long nr)
{
	switch (nr) {
#define X(n) case SYS_##n: return #n;
	X(access)
	X(brk)
	X(close)
	X(creat)
	X(dup)
	X(fstat64)
	X(mmap)
	X(mprotect)
	X(munmap)
	X(open)
	X(read)
	X(uname)
	X(write)
#undef X
	}
	return "";
}

void child_main(void)
{
	char test_file[] = ".test.flag";
	char msg[] = "child: you should see two of these\n";
	int fd = dup(2);

	unlink(test_file);
	write(fd, msg, sizeof(msg));

	/* Marker for the parent to watch. */
	errno = 0;
	close(-12345);
	fprintf(stderr, "child: close marker (should be EPERM): %m\n");
	errno = 0;
	close(fd);
	fprintf(stderr, "child: real close (should be EPERM): %m\n");
	errno = 0;
	write(fd, msg, sizeof(msg));
	fprintf(stderr, "child: write (should be success): %m\n");
	errno = 0;
	creat(test_file, 0660);
	fprintf(stderr, "child: creat (should be EPERM): %m\n");
	errno = 0;
	access(test_file, F_OK);
	fprintf(stderr, "child: access (should be ENOENT): %m\n");

	unlink(test_file);
	exit(0);
}

static void parent_main(void)
{
	int status;
	long nr, arg1;

	/* Wait for the child to exec. */
	while (1) {
		do_ptrace(PTRACE_SYSCALL, NULL, NULL);
		waitpid(trace_pid, &status, 0);

		unsigned event = ((unsigned)status >> 16);
		if (event == PTRACE_EVENT_EXEC) {
			warnx("parent: hit exec!");
			break;
		} else
			warnx("parent: waiting for exec; status: %#x", status);
	}

	/* Main loop. */
	bool saw_close = false;
	bool before_syscall = false;
	bool fake_syscall_ret = false;
	while (1) {
		do_ptrace(PTRACE_SYSCALL, NULL, NULL);
		waitpid(trace_pid, &status, 0);

		nr = do_peekuser(PT_GR20);
		arg1 = do_peekuser(PT_GR26);
		if (before_syscall) {
			warnx("parent: NR:%3li %s", nr, lookup_syscall(nr));
			/* Once the child hits the marker, deny all close & creat calls */
			if (nr == __NR_close || nr == __NR_creat) {
				if (saw_close || arg1 == -12345) {
					saw_close = true;
					warnx("parent: setting NR to -1");
					do_pokeuser(PT_GR20, -1);
					fake_syscall_ret = true;
				}
			}
		} else if (fake_syscall_ret) {
			warnx("parent: forcing EPERM");
			do_pokeuser(PT_GR28, -EPERM);
			fake_syscall_ret = false;
		}

		before_syscall = !before_syscall;
	}
}

int main(int argc, char *argv[])
{
	struct sigaction sa, old_sa;

	/* Child will re-exec us so the ptrace is clean for the parent. */
	if (argc > 1)
		child_main();

	/* Set up signal handler to watch for child events. */
	sa.sa_flags = SA_RESTART | SA_SIGINFO;
	sa.sa_sigaction = trace_child_signal;
	sigaction(SIGCHLD, &sa, &old_sa);

	/* Fork a child and have the parent do some early ptrace init. */
	trace_pid = fork();
	if (trace_pid == -1) {
		err(1, "fork() failed");
	} else if (trace_pid) {
		warn("parent waiting for child (pid=%i) to signal", trace_pid);
		waitpid(trace_pid, NULL, 0);
		do_ptrace(PTRACE_SETOPTIONS, NULL,
			(void *)(PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC));
		parent_main();
		errx(1, "child should have quit, as should we");
	}

	/* Have the child set itself up for tracing before execing again. */
	warnx("child setting up ...");
	sigaction(SIGCHLD, &old_sa, NULL);
	do_ptrace(PTRACE_TRACEME, NULL, NULL);
	kill(getpid(), SIGSTOP);
	execl(argv[0], argv[0], "--child", NULL);

	return 0;
}

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: ptrace interface does not permit modification of syscall return
  2015-12-21 17:55 ptrace interface does not permit modification of syscall return Mike Frysinger
@ 2015-12-22 21:10 ` Helge Deller
  2015-12-28 18:03   ` Mike Frysinger
  0 siblings, 1 reply; 4+ messages in thread
From: Helge Deller @ 2015-12-22 21:10 UTC (permalink / raw)
  To: linux-parisc, vapier

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

Hi Mike,

On 21.12.2015 18:55, Mike Frysinger wrote:
> i have a ptrace program that watches for specific syscalls and when
> matched, will:
>  - on entry change the syscall nr to -1 (so the kernel will skip it)
>  - on exit change the return to -EPERM so the userspace sees a denial
> 
> i have this working on most arches (x86, x86_64, arm, alpha, ia64, etc...).
> on parisc, the kernel (using 3.18.7 currently) appears to be wrong.  in my
> tests, if i don't mess with the syscall nr, i can change the return value
> fine (to EPERM or whatever).  but the syscall executed which i do not want.
> if i change the syscall to -1, then i can't change the return value (so the
> child sees ENOSYS), but the kernel still executes the original syscall.
> 
> i have a simple test case attached to show the issue.  the code does:
>  - spawn a child with the parent tracing it
>  - child will do:
>   - dupe stderr to another fd
>   - unlink a file named ".test.flag"
>   - write a message through the new fd
>   - close a magic # so the parent knows to start denying
>     - should see EPERM but it sees ENOSYS
>   - close the new fd
>     - should see EPERM but it is closed!
>   - write to the new fd
>     - should work, but the fd is closed
>   - call create on ".test.flag"
>     - should see EPERM, but the file is created!
>  - parent will do:
>   - log the syscalls until child runs close(-12345)
>   - the parent will then try to deny all close/creat calls
>   - uses PTRACE_POKEUSER w/PT_GR20 to set syscall to -1
>   - uses PTRACE_POKEUSER w/PT_GR28 to set return to -EPERM
> 
> you can run the test case by doing:
> $ gcc test.c && ./a.out

I agree, something is fishy :-)

I did some tests with your testcase.
First problem I had was, that compiling failed since it didn't found the asm/offset.h header file.
Which one did you used? I know it usually should come with the kernel headers, but there it is asm-offsets.h.
If you used debian, which package did you installed?

Instead I used asm-offsets.h.
First problem: I had to install the 64bit header file. PT_GR20 in this one was much higher than it should be for 32bit userspace.

So, I used those defines (taken from the strace source package):
#define PT_GR20 (20*4)
#define PT_GR26 (26*4)
#define PT_GR28 (28*4)
#define PT_IAOQ0 (106*4)
#define PT_IAOQ1 (107*4)

With that I got those output:
root@c3000:~# ./a.out
a.out: parent waiting for child (pid=1344) to signal: Success
a.out: child setting up ...
a.out: parent: waiting for exec; status: 0x1a7f
a.out: parent: waiting for exec; status: 0x857f
a.out: parent: hit exec!
a.out: parent: NR: 45 brk
a.out: parent: NR: 59 uname
a.out: parent: NR: 33 access
a.out: parent: NR: 90 mmap
a.out: parent: NR: 33 access
a.out: parent: NR:  5 open
a.out: parent: NR:112 fstat64
a.out: parent: NR: 90 mmap
a.out: parent: NR:  6 close
a.out: parent: NR: 33 access
a.out: parent: NR:  5 open
a.out: parent: NR:  3 read
a.out: parent: NR:112 fstat64
a.out: parent: NR: 90 mmap
a.out: parent: NR: 90 mmap
a.out: parent: NR: 90 mmap
a.out: parent: NR:  6 close
a.out: parent: NR: 90 mmap
a.out: parent: NR:125 mprotect
a.out: parent: NR: 90 mmap
a.out: parent: NR:125 mprotect
a.out: parent: NR: 91 munmap
a.out: parent: NR: 41 dup
a.out: parent: NR: 10 
a.out: parent: NR:  4 write
child: you should see two of these
a.out: parent: NR:  6 close
a.out: parent: setting NR to -1
a.out: parent: forcing EPERM
child: close marker (should be EPERM): Function not implemented
a.out: parent: NR:  4 write
a.out: parent: NR:  6 close
a.out: parent: setting NR to -1
a.out: parent: forcing EPERM
child: real close (should be EPERM): Success
a.out: parent: NR:  4 write
a.out: parent: NR:  4 write
child: write (should be success): Bad file descriptor
a.out: parent: NR:  4 write
a.out: parent: NR:  8 creat
a.out: parent: setting NR to -1
a.out: parent: forcing EPERM
child: creat (should be EPERM): Success
a.out: parent: NR:  4 write
a.out: parent: NR: 33 access
child: access (should be ENOENT): Success
a.out: parent: NR:  4 write
a.out: parent: NR: 10 

Seems not like what it should be.
I need to look closer at it during the next few days.

Regarding on how to get correct 32bit PT_REG #defines/values even on 64bit kernel,
maybe the attached patch is a way to go.

Helge

[-- Attachment #2: pt_reg_32bit.patch --]
[-- Type: text/x-diff, Size: 6943 bytes --]

diff --git a/arch/parisc/include/uapi/asm/ptrace.h b/arch/parisc/include/uapi/asm/ptrace.h
index c4fa6c8..1f2f892 100644
--- a/arch/parisc/include/uapi/asm/ptrace.h
+++ b/arch/parisc/include/uapi/asm/ptrace.h
@@ -33,6 +33,26 @@ struct pt_regs {
 	unsigned long ipsw;	/* CR22 */
 };
 
+#if defined(__LP64__)
+struct compat_pt_regs {
+	unsigned int gr[32];	/* PSW is in gr[0] */
+	__u64 fr[32];
+	unsigned int sr[ 8];
+	unsigned int iasq[2];
+	unsigned int iaoq[2];
+	unsigned int cr27;
+	unsigned int pad0;     /* available for other uses */
+	unsigned int orig_r28;
+	unsigned int ksp;
+	unsigned int kpc;
+	unsigned int sar;	/* CR11 */
+	unsigned int iir;	/* CR19 */
+	unsigned int isr;	/* CR20 */
+	unsigned int ior;	/* CR21 */
+	unsigned int ipsw;	/* CR22 */
+};
+#endif
+
 /*
  * The numbers chosen here are somewhat arbitrary but absolutely MUST
  * not overlap with any of the number assigned in <linux/ptrace.h>.
diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c
index d2f6257..e2a8030 100644
--- a/arch/parisc/kernel/asm-offsets.c
+++ b/arch/parisc/kernel/asm-offsets.c
@@ -240,6 +240,96 @@ int main(void)
 	DEFINE(PT_SIZE, sizeof(struct pt_regs));
 	/* PT_SZ_ALGN includes space for a stack frame. */
 	DEFINE(PT_SZ_ALGN, align_frame(sizeof(struct pt_regs), FRAME_ALIGN));
+#ifdef CONFIG_64BIT
+	COMMENT("for 32bit userspace:");
+	DEFINE(PT_32_PSW, offsetof(struct compat_pt_regs, gr[ 0]));
+	DEFINE(PT_32_GR1, offsetof(struct compat_pt_regs, gr[ 1]));
+	DEFINE(PT_32_GR2, offsetof(struct compat_pt_regs, gr[ 2]));
+	DEFINE(PT_32_GR3, offsetof(struct compat_pt_regs, gr[ 3]));
+	DEFINE(PT_32_GR4, offsetof(struct compat_pt_regs, gr[ 4]));
+	DEFINE(PT_32_GR5, offsetof(struct compat_pt_regs, gr[ 5]));
+	DEFINE(PT_32_GR6, offsetof(struct compat_pt_regs, gr[ 6]));
+	DEFINE(PT_32_GR7, offsetof(struct compat_pt_regs, gr[ 7]));
+	DEFINE(PT_32_GR8, offsetof(struct compat_pt_regs, gr[ 8]));
+	DEFINE(PT_32_GR9, offsetof(struct compat_pt_regs, gr[ 9]));
+	DEFINE(PT_32_GR10, offsetof(struct compat_pt_regs, gr[10]));
+	DEFINE(PT_32_GR11, offsetof(struct compat_pt_regs, gr[11]));
+	DEFINE(PT_32_GR12, offsetof(struct compat_pt_regs, gr[12]));
+	DEFINE(PT_32_GR13, offsetof(struct compat_pt_regs, gr[13]));
+	DEFINE(PT_32_GR14, offsetof(struct compat_pt_regs, gr[14]));
+	DEFINE(PT_32_GR15, offsetof(struct compat_pt_regs, gr[15]));
+	DEFINE(PT_32_GR16, offsetof(struct compat_pt_regs, gr[16]));
+	DEFINE(PT_32_GR17, offsetof(struct compat_pt_regs, gr[17]));
+	DEFINE(PT_32_GR18, offsetof(struct compat_pt_regs, gr[18]));
+	DEFINE(PT_32_GR19, offsetof(struct compat_pt_regs, gr[19]));
+	DEFINE(PT_32_GR20, offsetof(struct compat_pt_regs, gr[20]));
+	DEFINE(PT_32_GR21, offsetof(struct compat_pt_regs, gr[21]));
+	DEFINE(PT_32_GR22, offsetof(struct compat_pt_regs, gr[22]));
+	DEFINE(PT_32_GR23, offsetof(struct compat_pt_regs, gr[23]));
+	DEFINE(PT_32_GR24, offsetof(struct compat_pt_regs, gr[24]));
+	DEFINE(PT_32_GR25, offsetof(struct compat_pt_regs, gr[25]));
+	DEFINE(PT_32_GR26, offsetof(struct compat_pt_regs, gr[26]));
+	DEFINE(PT_32_GR27, offsetof(struct compat_pt_regs, gr[27]));
+	DEFINE(PT_32_GR28, offsetof(struct compat_pt_regs, gr[28]));
+	DEFINE(PT_32_GR29, offsetof(struct compat_pt_regs, gr[29]));
+	DEFINE(PT_32_GR30, offsetof(struct compat_pt_regs, gr[30]));
+	DEFINE(PT_32_GR31, offsetof(struct compat_pt_regs, gr[31]));
+	DEFINE(PT_32_FR0, offsetof(struct compat_pt_regs, fr[ 0]));
+	DEFINE(PT_32_FR1, offsetof(struct compat_pt_regs, fr[ 1]));
+	DEFINE(PT_32_FR2, offsetof(struct compat_pt_regs, fr[ 2]));
+	DEFINE(PT_32_FR3, offsetof(struct compat_pt_regs, fr[ 3]));
+	DEFINE(PT_32_FR4, offsetof(struct compat_pt_regs, fr[ 4]));
+	DEFINE(PT_32_FR5, offsetof(struct compat_pt_regs, fr[ 5]));
+	DEFINE(PT_32_FR6, offsetof(struct compat_pt_regs, fr[ 6]));
+	DEFINE(PT_32_FR7, offsetof(struct compat_pt_regs, fr[ 7]));
+	DEFINE(PT_32_FR8, offsetof(struct compat_pt_regs, fr[ 8]));
+	DEFINE(PT_32_FR9, offsetof(struct compat_pt_regs, fr[ 9]));
+	DEFINE(PT_32_FR10, offsetof(struct compat_pt_regs, fr[10]));
+	DEFINE(PT_32_FR11, offsetof(struct compat_pt_regs, fr[11]));
+	DEFINE(PT_32_FR12, offsetof(struct compat_pt_regs, fr[12]));
+	DEFINE(PT_32_FR13, offsetof(struct compat_pt_regs, fr[13]));
+	DEFINE(PT_32_FR14, offsetof(struct compat_pt_regs, fr[14]));
+	DEFINE(PT_32_FR15, offsetof(struct compat_pt_regs, fr[15]));
+	DEFINE(PT_32_FR16, offsetof(struct compat_pt_regs, fr[16]));
+	DEFINE(PT_32_FR17, offsetof(struct compat_pt_regs, fr[17]));
+	DEFINE(PT_32_FR18, offsetof(struct compat_pt_regs, fr[18]));
+	DEFINE(PT_32_FR19, offsetof(struct compat_pt_regs, fr[19]));
+	DEFINE(PT_32_FR20, offsetof(struct compat_pt_regs, fr[20]));
+	DEFINE(PT_32_FR21, offsetof(struct compat_pt_regs, fr[21]));
+	DEFINE(PT_32_FR22, offsetof(struct compat_pt_regs, fr[22]));
+	DEFINE(PT_32_FR23, offsetof(struct compat_pt_regs, fr[23]));
+	DEFINE(PT_32_FR24, offsetof(struct compat_pt_regs, fr[24]));
+	DEFINE(PT_32_FR25, offsetof(struct compat_pt_regs, fr[25]));
+	DEFINE(PT_32_FR26, offsetof(struct compat_pt_regs, fr[26]));
+	DEFINE(PT_32_FR27, offsetof(struct compat_pt_regs, fr[27]));
+	DEFINE(PT_32_FR28, offsetof(struct compat_pt_regs, fr[28]));
+	DEFINE(PT_32_FR29, offsetof(struct compat_pt_regs, fr[29]));
+	DEFINE(PT_32_FR30, offsetof(struct compat_pt_regs, fr[30]));
+	DEFINE(PT_32_FR31, offsetof(struct compat_pt_regs, fr[31]));
+	DEFINE(PT_32_SR0, offsetof(struct compat_pt_regs, sr[ 0]));
+	DEFINE(PT_32_SR1, offsetof(struct compat_pt_regs, sr[ 1]));
+	DEFINE(PT_32_SR2, offsetof(struct compat_pt_regs, sr[ 2]));
+	DEFINE(PT_32_SR3, offsetof(struct compat_pt_regs, sr[ 3]));
+	DEFINE(PT_32_SR4, offsetof(struct compat_pt_regs, sr[ 4]));
+	DEFINE(PT_32_SR5, offsetof(struct compat_pt_regs, sr[ 5]));
+	DEFINE(PT_32_SR6, offsetof(struct compat_pt_regs, sr[ 6]));
+	DEFINE(PT_32_SR7, offsetof(struct compat_pt_regs, sr[ 7]));
+	DEFINE(PT_32_IASQ0, offsetof(struct compat_pt_regs, iasq[0]));
+	DEFINE(PT_32_IASQ1, offsetof(struct compat_pt_regs, iasq[1]));
+	DEFINE(PT_32_IAOQ0, offsetof(struct compat_pt_regs, iaoq[0]));
+	DEFINE(PT_32_IAOQ1, offsetof(struct compat_pt_regs, iaoq[1]));
+	DEFINE(PT_32_CR27, offsetof(struct compat_pt_regs, cr27));
+	DEFINE(PT_32_ORIG_R28, offsetof(struct compat_pt_regs, orig_r28));
+	DEFINE(PT_32_KSP, offsetof(struct compat_pt_regs, ksp));
+	DEFINE(PT_32_KPC, offsetof(struct compat_pt_regs, kpc));
+	DEFINE(PT_32_SAR, offsetof(struct compat_pt_regs, sar));
+	DEFINE(PT_32_IIR, offsetof(struct compat_pt_regs, iir));
+	DEFINE(PT_32_ISR, offsetof(struct compat_pt_regs, isr));
+	DEFINE(PT_32_IOR, offsetof(struct compat_pt_regs, ior));
+	DEFINE(PT_32_SIZE, sizeof(struct compat_pt_regs));
+	/* PT_32_SZ_ALGN includes space for a stack frame. */
+	DEFINE(PT_32_SZ_ALGN, align_frame(sizeof(struct compat_pt_regs), FRAME_ALIGN));
+#endif
 	BLANK();
 	DEFINE(TI_TASK, offsetof(struct thread_info, task));
 	DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: ptrace interface does not permit modification of syscall return
  2015-12-22 21:10 ` Helge Deller
@ 2015-12-28 18:03   ` Mike Frysinger
  2016-01-19 15:11     ` Helge Deller
  0 siblings, 1 reply; 4+ messages in thread
From: Mike Frysinger @ 2015-12-28 18:03 UTC (permalink / raw)
  To: Helge Deller; +Cc: linux-parisc

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

On 22 Dec 2015 22:10, Helge Deller wrote:
> On 21.12.2015 18:55, Mike Frysinger wrote:
> > i have a ptrace program that watches for specific syscalls and when
> > matched, will:
> >  - on entry change the syscall nr to -1 (so the kernel will skip it)
> >  - on exit change the return to -EPERM so the userspace sees a denial
> > 
> > i have this working on most arches (x86, x86_64, arm, alpha, ia64, etc...).
> > on parisc, the kernel (using 3.18.7 currently) appears to be wrong.  in my
> > tests, if i don't mess with the syscall nr, i can change the return value
> > fine (to EPERM or whatever).  but the syscall executed which i do not want.
> > if i change the syscall to -1, then i can't change the return value (so the
> > child sees ENOSYS), but the kernel still executes the original syscall.
> > 
> > i have a simple test case attached to show the issue.  the code does:
> >  - spawn a child with the parent tracing it
> >  - child will do:
> >   - dupe stderr to another fd
> >   - unlink a file named ".test.flag"
> >   - write a message through the new fd
> >   - close a magic # so the parent knows to start denying
> >     - should see EPERM but it sees ENOSYS
> >   - close the new fd
> >     - should see EPERM but it is closed!
> >   - write to the new fd
> >     - should work, but the fd is closed
> >   - call create on ".test.flag"
> >     - should see EPERM, but the file is created!
> >  - parent will do:
> >   - log the syscalls until child runs close(-12345)
> >   - the parent will then try to deny all close/creat calls
> >   - uses PTRACE_POKEUSER w/PT_GR20 to set syscall to -1
> >   - uses PTRACE_POKEUSER w/PT_GR28 to set return to -EPERM
> > 
> > you can run the test case by doing:
> > $ gcc test.c && ./a.out
> 
> I agree, something is fishy :-)
> 
> I did some tests with your testcase.
> First problem I had was, that compiling failed since it didn't found the asm/offset.h header file.
> Which one did you used? I know it usually should come with the kernel headers, but there it is asm-offsets.h.

hmm, looks like it got installed by hand at some point (Jul 2004 datestamp!)
and never cleaned up.

> First problem: I had to install the 64bit header file. PT_GR20 in this one was much higher than it should be for 32bit userspace.
> 
> So, I used those defines (taken from the strace source package):
> #define PT_GR20 (20*4)
> #define PT_GR26 (26*4)
> #define PT_GR28 (28*4)
> #define PT_IAOQ0 (106*4)
> #define PT_IAOQ1 (107*4)

these are the values in my local asm/offset.h, and what i was using
in my original code -- the register # multiplied by 4.

> With that I got those output:

looks like you're seeing the same as me.  i'm only testing 32bit user
and 32bit kernel currently as we don't have a 64bit userspace :).
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: ptrace interface does not permit modification of syscall return
  2015-12-28 18:03   ` Mike Frysinger
@ 2016-01-19 15:11     ` Helge Deller
  0 siblings, 0 replies; 4+ messages in thread
From: Helge Deller @ 2016-01-19 15:11 UTC (permalink / raw)
  To: linux-parisc, Mike Frysinger

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On 28.12.2015 19:03, Mike Frysinger wrote:
> On 22 Dec 2015 22:10, Helge Deller wrote:
>> On 21.12.2015 18:55, Mike Frysinger wrote:
>>> i have a ptrace program that watches for specific syscalls and when
>>> matched, will:
>>>  - on entry change the syscall nr to -1 (so the kernel will skip it)
>>>  - on exit change the return to -EPERM so the userspace sees a denial
>>>
>>> i have this working on most arches (x86, x86_64, arm, alpha, ia64, etc...).
>>> on parisc, the kernel (using 3.18.7 currently) appears to be wrong.  in my
>>> tests, if i don't mess with the syscall nr, i can change the return value
>>> fine (to EPERM or whatever).  but the syscall executed which i do not want.
>>> if i change the syscall to -1, then i can't change the return value (so the
>>> child sees ENOSYS), but the kernel still executes the original syscall.
>>>
>>> i have a simple test case attached to show the issue.  the code does:
>>>  - spawn a child with the parent tracing it
>>>  - child will do:
>>>   - dupe stderr to another fd
>>>   - unlink a file named ".test.flag"
>>>   - write a message through the new fd
>>>   - close a magic # so the parent knows to start denying
>>>     - should see EPERM but it sees ENOSYS
>>>   - close the new fd
>>>     - should see EPERM but it is closed!
>>>   - write to the new fd
>>>     - should work, but the fd is closed
>>>   - call create on ".test.flag"
>>>     - should see EPERM, but the file is created!
>>>  - parent will do:
>>>   - log the syscalls until child runs close(-12345)
>>>   - the parent will then try to deny all close/creat calls
>>>   - uses PTRACE_POKEUSER w/PT_GR20 to set syscall to -1
>>>   - uses PTRACE_POKEUSER w/PT_GR28 to set return to -EPERM
>>>
>>> you can run the test case by doing:
>>> $ gcc test.c && ./a.out
>>
>> I agree, something is fishy :-)
>>
>> I did some tests with your testcase.
>> First problem I had was, that compiling failed since it didn't found the asm/offset.h header file.
>> Which one did you used? I know it usually should come with the kernel headers, but there it is asm-offsets.h.
> 
> hmm, looks like it got installed by hand at some point (Jul 2004 datestamp!)
> and never cleaned up.
> 
>> First problem: I had to install the 64bit header file. PT_GR20 in this one was much higher than it should be for 32bit userspace.
>>
>> So, I used those defines (taken from the strace source package):
>> #define PT_GR20 (20*4)
>> #define PT_GR26 (26*4)
>> #define PT_GR28 (28*4)
>> #define PT_IAOQ0 (106*4)
>> #define PT_IAOQ1 (107*4)
> 
> these are the values in my local asm/offset.h, and what i was using
> in my original code -- the register # multiplied by 4.
> 
>> With that I got those output:
> 
> looks like you're seeing the same as me.  i'm only testing 32bit user
> and 32bit kernel currently as we don't have a 64bit userspace :).

Mike, can you please test the patch I just posted:
https://patchwork.kernel.org/patch/8063301/

Helge
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJWnlIGAAoJEKGDlV8wpRJB01AP/1e5cO5DfF2UtDGAC8SNqB1x
4uCIEyCrKyAkNbrfy3HSQ2OVdenuZnxq/z5LiQeY9k8j4IfZgpU8v7fADZz76L1z
CsVyIAwi7BQqUUeV6otWo5o1mAZ1F9dvS4jPFpxROotgmBCkhDcAYP6xGpoKbAET
VDhgwjgsZLXOuONoxfONaveZGRMjZGbm5nrvra30kfQhzilew5EmWGchWvv5QFAE
scukm0Prfi9roefagIllvZrLjGLenQ7Wa/opWrH/KxbkN3t8cQprXUv8ejEEUG8v
mCrUQRfHdbgVpzMFyCbdtoRVBRRtRl3Z9Ht8DqgYiuaW34iDA6Una/ntBC/Z0SdD
WySBINwuG6VL8gLrS7zMuKyBrhY37PV+eeo+GC0C6bNr37oPgJ1HLqOj8B2tlHU+
/EJYaLSDMXHsrukF+XSOzq9pbiCpPvU3ApzpZnPcypNMid3/6LKf6fIvu/4P/8xK
09gCZrHRXI5J65Qya6oQHP2N1YJYRJzHUct1N6hEJ01DEDfUZxkHun8EDYHC879f
RZ7UE2jNJlBAMXTel0HGthgXrqPyM18ePlA4yv5YVK1Z7Ai+wT7j+CL5hnI46x9X
71PugI5vMzZZQ+N9alW9KG2E/RAzcx1Mu/SjntoXBD7jdv1u+DiQtbhD/ahP61Pt
+4I+Icqo62/Ql3Y2r8Ia
=2LNU
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-01-19 15:11 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-21 17:55 ptrace interface does not permit modification of syscall return Mike Frysinger
2015-12-22 21:10 ` Helge Deller
2015-12-28 18:03   ` Mike Frysinger
2016-01-19 15:11     ` Helge Deller

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.