lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20080616075832.GE6950@cs.unibo.it>
Date:	Mon, 16 Jun 2008 09:58:32 +0200
From:	renzo@...unibo.it (Renzo Davoli)
To:	LKML <linux-kernel@...r.kernel.org>, Jeff Dike <jdike@...toit.com>,
	Roland McGrath <roland@...hat.com>
Subject: [PATCH 2/2] ptrace_multi: speedup for virtual machines (and debuggers) running on ptrace

Warning:
This is a proof-of-concept. It shows the effectiveness of PTRACE_MULTI
(PATCH 1/2). It is incomplete, not to be included at this stage.

This patch adds some PTRACE_MULTI optimizations to User-Mode Linux client
code. With these optimization UML decreases the overall number of
syscalls (around 15% in my preliminary tests) and shows some speedup
(5%).

I expect better figures when all the UML client code will take advantage
of PTRACE_MULTI and on other architectures (e.g. on powerpc, still
unsupported, where tens of ptrace in a row are needed to get/set
registers).

This patch is against 2.6.26-rc6 (or git2) + ptrace_multi + ptrace_vm

	renzo

Signed-off-by: Renzo Davoli
---
diff -Naur linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/include/ptrace_user.h linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/include/ptrace_user.h
--- linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/include/ptrace_user.h	2008-06-14 20:07:04.000000000 +0200
+++ linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/include/ptrace_user.h	2008-06-15 18:35:28.000000000 +0200
@@ -20,6 +20,18 @@
 #define PTRACE_SYSEMU_SINGLESTEP 32
 #endif
 
+#define PTRACE_MULTI            0x4300
+#define PTRACE_PEEKCHARDATA     0x4301
+#define PTRACE_POKECHARDATA     0x4302
+#define PTRACE_PEEKSTRINGDATA   0x4303
+
+struct ptrace_multi {
+	long request;
+	long addr;
+	void *localaddr;
+	long length;
+};
+
 /* On architectures, that started to support PTRACE_O_TRACESYSGOOD
  * in linux 2.4, there are two different definitions of
  * PTRACE_SETOPTIONS: linux 2.4 uses 21 while linux 2.6 uses 0x4200.
@@ -54,6 +66,9 @@
 void set_using_sysptvm(int value);
 int get_using_sysptvm(void);
 extern int sysptvm_supported;
+void set_using_sysptmulti(int value);
+int get_using_sysptmulti(void);
+extern int sysptmulti_supported;
 
 #define SELECT_PTRACE_OPERATION(sysemu_mode, singlestep_mode) \
 	(((int[3][3] ) { \
diff -Naur linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/kernel/process.c linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/kernel/process.c
--- linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/kernel/process.c	2008-06-14 20:07:04.000000000 +0200
+++ linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/kernel/process.c	2008-06-15 18:35:28.000000000 +0200
@@ -323,8 +323,10 @@
 
 static atomic_t using_sysemu = ATOMIC_INIT(0);
 static atomic_t using_sysptvm = ATOMIC_INIT(0);
+static atomic_t using_sysptmulti = ATOMIC_INIT(0);
 int sysemu_supported;
 int sysptvm_supported;
+int sysptmulti_supported;
 
 void set_using_sysemu(int value)
 {
@@ -348,6 +350,16 @@
 	return atomic_read(&using_sysptvm);
 }
 
+void set_using_sysptmulti(int value)
+{
+	atomic_set(&using_sysptmulti, value);
+}
+
+int get_using_sysptmulti(void)
+{
+	return atomic_read(&using_sysptmulti);
+}
+
 static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data)
 {
 	if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size)
@@ -395,6 +407,28 @@
 	return count;
 }
 
+static int proc_read_sysptmulti(char *buf, char **start, off_t offset, int size,int *eof, void *data)
+{
+	if (snprintf(buf, size, "%d\n", get_using_sysptmulti()) < size)
+		/* No overflow */
+		*eof = 1;
+
+	return strlen(buf);
+}
+
+static int proc_write_sysptmulti(struct file *file,const char __user *buf, unsigned long count,void *data)
+{
+	char tmp[2];
+
+	if (copy_from_user(tmp, buf, 1))
+		return -EFAULT;
+
+	if (tmp[0] >= '0' && tmp[0] <= '2')
+		set_using_sysptmulti(tmp[0] - '0');
+	/* We use the first char, but pretend to write everything */
+	return count;
+}
+
 int __init make_proc_sysemu_or_sysptvm(void)
 {
 	struct proc_dir_entry *ent;
@@ -422,6 +456,18 @@
 		ent->read_proc  = proc_read_sysemu;
 		ent->write_proc = proc_write_sysemu;
 	}
+	if (sysptmulti_supported) {
+		ent = create_proc_entry("sysptmulti", 0600, NULL);
+
+		if (ent == NULL)
+		{
+			printk(KERN_WARNING "Failed to register /proc/sysptmulti\n");
+			return 0;
+		}
+
+		ent->read_proc  = proc_read_sysptmulti;
+		ent->write_proc = proc_write_sysptmulti;
+	}
 	return 0;
 }
 
diff -Naur linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/os-Linux/skas/mem.c linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/os-Linux/skas/mem.c
--- linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/os-Linux/skas/mem.c	2008-06-14 20:06:12.000000000 +0200
+++ linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/os-Linux/skas/mem.c	2008-06-16 01:47:34.000000000 +0200
@@ -69,19 +69,32 @@
 
 	multi_count++;
 
-	n = ptrace_setregs(pid, syscall_regs);
-	if (n < 0) {
-		printk(UM_KERN_ERR "Registers - \n");
-		for (i = 0; i < MAX_REG_NR; i++)
-			printk(UM_KERN_ERR "\t%d\t0x%lx\n", i, syscall_regs[i]);
-		panic("do_syscall_stub : PTRACE_SETREGS failed, errno = %d\n",
-		      -n);
-	}
+	if (get_using_sysptmulti()) {
+		struct ptrace_multi req[] = {
+			{PTRACE_SETREGS, 0, syscall_regs, 0},
+			{PTRACE_CONT, 0, 0, 0}};
+		err=ptrace(PTRACE_MULTI,pid,req,2);
+		if (err<0) {
+			err = -errno;
+			printk(UM_KERN_ERR "do_syscall_stub: failed ptrace_multi "
+					"failed, pid = %d, errno = %d\n", pid, -err);
+			return err;
+		}
+	} else {
+		n = ptrace_setregs(pid, syscall_regs);
+		if (n < 0) {
+			printk(UM_KERN_ERR "Registers - \n");
+			for (i = 0; i < MAX_REG_NR; i++)
+				printk(UM_KERN_ERR "\t%d\t0x%lx\n", i, syscall_regs[i]);
+			panic("do_syscall_stub : PTRACE_SETREGS failed, errno = %d\n",
+					-n);
+		}
 
-	err = ptrace(PTRACE_CONT, pid, 0, 0);
-	if (err)
-		panic("Failed to continue stub, pid = %d, errno = %d\n", pid,
-		      errno);
+		err = ptrace(PTRACE_CONT, pid, 0, 0);
+		if (err)
+			panic("Failed to continue stub, pid = %d, errno = %d\n", pid,
+					errno);
+	}
 
 	wait_stub_done(pid);
 
diff -Naur linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/os-Linux/skas/process.c linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/os-Linux/skas/process.c
--- linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/os-Linux/skas/process.c	2008-06-14 20:07:04.000000000 +0200
+++ linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/os-Linux/skas/process.c	2008-06-16 01:35:35.000000000 +0200
@@ -355,6 +355,7 @@
 	/* To prevent races if using_sysemu changes under us.*/
 	int local_using_sysemu;
 	int local_using_sysptvm;
+	int local_using_sysptmulti;
 
 	if (getitimer(ITIMER_VIRTUAL, &timer))
 		printk(UM_KERN_ERR "Failed to get itimer, errno = %d\n", errno);
@@ -363,41 +364,53 @@
 	nsecs += os_nsecs();
 
 	while (1) {
-		/*
-		 * This can legitimately fail if the process loads a
-		 * bogus value into a segment register.  It will
-		 * segfault and PTRACE_GETREGS will read that value
-		 * out of the process.  However, PTRACE_SETREGS will
-		 * fail.  In this case, there is nothing to do but
-		 * just kill the process.
-		 */
-		if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp))
-			fatal_sigsegv();
-
 		/* Now we set local_using_sysemu to be used for one loop */
 		local_using_sysemu = get_using_sysemu();
 		local_using_sysptvm = get_using_sysptvm();
+		local_using_sysptmulti = get_using_sysptmulti();
 
 		op = SELECT_PTRACE_OPERATION(local_using_sysemu,
-					     singlestepping(NULL));
+				singlestepping(NULL));
 
-		if (ptrace(op, pid, local_using_sysptvm, 0)) {
-			printk(UM_KERN_ERR "userspace - ptrace continue "
-			       "failed, op = %d, errno = %d\n", op, errno);
-			fatal_sigsegv();
+		if (local_using_sysptmulti) {
+			struct ptrace_multi req[] = {
+				{PTRACE_SETREGS, 0, regs->gp, 0},
+				{op, local_using_sysptvm, 0, 0}};
+			if (ptrace(PTRACE_MULTI,pid,req,2)) {
+				printk(UM_KERN_ERR "userspace - ptrace multi continue "
+						"failed, op = %d, errno = %d\n", op, errno);
+				fatal_sigsegv();
+			}
+		} else {
+			/*
+			 * This can legitimately fail if the process loads a
+			 * bogus value into a segment register.  It will
+			 * segfault and PTRACE_GETREGS will read that value
+			 * out of the process.  However, PTRACE_SETREGS will
+			 * fail.  In this case, there is nothing to do but
+			 * just kill the process.
+			 */
+			if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp))
+				fatal_sigsegv();
+
+			if (ptrace(op, pid, local_using_sysptvm, 0)) {
+				printk(UM_KERN_ERR "userspace - ptrace continue "
+						"failed, op = %d, errno = %d\n", op, errno);
+				fatal_sigsegv();
+			}
 		}
 
 		CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED | __WALL));
 		if (err < 0) {
 			printk(UM_KERN_ERR "userspace - wait failed, "
-			       "errno = %d\n", errno);
+					"errno = %d\n", errno);
 			fatal_sigsegv();
 		}
 
 		regs->is_user = 1;
 		if (ptrace(PTRACE_GETREGS, pid, 0, regs->gp)) {
 			printk(UM_KERN_ERR "userspace - PTRACE_GETREGS failed, "
-			       "errno = %d\n", errno);
+					"errno = %d\n", errno);
 			fatal_sigsegv();
 		}
 
@@ -497,27 +510,40 @@
 					           { .it_value = tv,
 						     .it_interval = tv }) });
 
-	err = ptrace_setregs(pid, thread_regs);
-	if (err < 0) {
-		err = -errno;
-		printk(UM_KERN_ERR "copy_context_skas0 : PTRACE_SETREGS "
-		       "failed, pid = %d, errno = %d\n", pid, -err);
-		return err;
-	}
-
 	/* set a well known return code for detection of child write failure */
 	child_data->err = 12345678;
 
-	/*
-	 * Wait, until parent has finished its work: read child's pid from
-	 * parent's stack, and check, if bad result.
-	 */
-	err = ptrace(PTRACE_CONT, pid, 0, 0);
-	if (err) {
-		err = -errno;
-		printk(UM_KERN_ERR "Failed to continue new process, pid = %d, "
-		       "errno = %d\n", pid, errno);
-		return err;
+	if (get_using_sysptmulti()) {
+		struct ptrace_multi req[] = {
+			{PTRACE_SETREGS, 0, thread_regs, 0},
+			{PTRACE_CONT, 0, 0, 0}};
+		err=ptrace(PTRACE_MULTI,pid,req,2);
+		if (err<0) {
+			err = -errno;
+			printk(UM_KERN_ERR "copy_context_skas0: failed ptrace_multi "
+					 "failed, pid = %d, errno = %d\n", pid, -err);
+			return err;
+		}
+	} else {
+		err = ptrace_setregs(pid, thread_regs);
+		if (err < 0) {
+			err = -errno;
+			printk(UM_KERN_ERR "copy_context_skas0 : PTRACE_SETREGS "
+					"failed, pid = %d, errno = %d\n", pid, -err);
+			return err;
+		}
+
+		/*
+		 * Wait, until parent has finished its work: read child's pid from
+		 * parent's stack, and check, if bad result.
+		 */
+		err = ptrace(PTRACE_CONT, pid, 0, 0);
+		if (err) {
+			err = -errno;
+			printk(UM_KERN_ERR "Failed to continue new process, pid = %d, "
+					"errno = %d\n", pid, errno);
+			return err;
+		}
 	}
 
 	wait_stub_done(pid);
diff -Naur linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/os-Linux/start_up.c linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/os-Linux/start_up.c
--- linux-2.6.26-rc6-ptrace_multi-ptrace_vm/arch/um/os-Linux/start_up.c	2008-06-14 20:07:04.000000000 +0200
+++ linux-2.6.26-rc6-ptrace_multi-ptrace_vm-umlmulti/arch/um/os-Linux/start_up.c	2008-06-15 18:35:28.000000000 +0200
@@ -225,6 +225,21 @@
 "    Use sysemu instead of sysptvm even when the kernel supports it.\n\n"
 );
 
+static int force_sysptmulti_disabled = 0;
+
+static int __init nosysptmulti_cmd_param(char *str, int* add)
+{
+	  force_sysptmulti_disabled = 1;
+		  return 0;
+}
+
+__uml_setup("nosysptmulti", nosysptmulti_cmd_param,
+		"nosysptmulti\n"
+		"    Turns off syscall multi for ptrace (ptrace_vm) on.\n"
+		"    Ptrace_vm is a feature introduced by Renzo Davoli: several\n"
+		"    ptrace() requests can be evaluated using one system call.\n"
+		"\n");
+
 static void __init check_sysemu(void)
 {
 	unsigned long regs[MAX_REG_NR];
@@ -344,6 +359,7 @@
 	return 0;
 }
 
+#define PTRACE_SUPPORTS_PTMULTI 0x80000000
 /* kernel feature test: 
  * it returns:
  *   -1 error 
@@ -351,7 +367,7 @@
  *   PTRACE_SYSCALL_SKIPEXIT: just skip_exit is provided
  *   PTRACE_SYSCALL_SKIPCALL: the entire syntax is implemented
  *   by the running kernel */
-static int __init test_ptrace_sysptvm(void) {
+static int __init test_ptrace_sysptvm_and_ptmulti(void) {
 	int pid, status, rv, feature;
 	static char stack[1024];
 	feature=0;
@@ -383,6 +399,8 @@
 	if(waitpid(pid, &status, WUNTRACED) < 0)
 		return 0;
 out:
+	if (ptrace(PTRACE_MULTI, pid, stack, 0) >= 0)
+		feature |= PTRACE_SUPPORTS_PTMULTI;
 	ptrace(PTRACE_KILL,pid,0,0);
 	/* eliminate zombie */
 	if(waitpid(pid, &status, WUNTRACED) < 0)
@@ -392,10 +410,11 @@
 
 static int  __init check_sysptvm(void)
 {
-	int feature=test_ptrace_sysptvm();
+	int feature=test_ptrace_sysptvm_and_ptmulti();
+	int rv=0;
   
 	non_fatal("Checking ptrace new tags for syscall emulation...");
-	if (feature==PTRACE_SYSCALL_SKIPCALL) {
+	if ((feature & ~PTRACE_SUPPORTS_PTMULTI)==PTRACE_SYSCALL_SKIPCALL) {
 		sysptvm_supported=1;
 		non_fatal("OK");
 		if (!force_sysptvm_disabled) 
@@ -403,10 +422,21 @@
 		else
 			non_fatal(" (disabled)");
 		non_fatal("\n");
-		return 1;
+		rv=1;
 	} else
 		non_fatal("unsupported\n");
-	return 0;
+	non_fatal("Checking ptrace multi speedup...");
+	if (feature & PTRACE_SUPPORTS_PTMULTI) {
+		sysptmulti_supported=1;
+		non_fatal("OK");
+		if (force_sysptmulti_disabled)
+			non_fatal(" (disabled)");
+		else
+			set_using_sysptmulti(1);
+		non_fatal("\n");
+	} else
+		non_fatal("unsupported\n");
+	return rv;
 }
 
 static void __init check_ptrace(void)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ