summary refs log tree commit diff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-19 15:11:09 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-19 15:11:09 -0700
commitd471ce53b1fab60110e4e9f647a345cea31752de (patch)
tree73acb87c823b63d026f9ba4617b5e112835d6abf
parent1b050180454dc226780f765a33575d4cd8d6e552 (diff)
parent9e82d450531c79b18ab18c9b9645cdd9db31ee98 (diff)
downloadlinux-d471ce53b1fab60110e4e9f647a345cea31752de.tar.gz
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
Pull UML fixes from Richard Weinberger:
 "Special thanks goes to Toralf Föster for continuously testing UML and
  reporting issues!"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml:
  um: remove dead code
  um: siginfo cleanup
  uml: Fix which_tmpdir failure when /dev/shm is a symlink, and in other edge cases
  um: Fix wait_stub_done() error handling
  um: Mark stub pages mapping with VM_PFNMAP
  um: Fix return value of strnlen_user()
-rw-r--r--arch/um/include/shared/frame_kern.h8
-rw-r--r--arch/um/kernel/signal.c4
-rw-r--r--arch/um/kernel/skas/mmu.c2
-rw-r--r--arch/um/kernel/skas/uaccess.c2
-rw-r--r--arch/um/os-Linux/mem.c230
-rw-r--r--arch/um/os-Linux/signal.c8
-rw-r--r--arch/um/os-Linux/skas/process.c19
-rw-r--r--arch/x86/um/signal.c1
8 files changed, 213 insertions, 61 deletions
diff --git a/arch/um/include/shared/frame_kern.h b/arch/um/include/shared/frame_kern.h
index e584e40ee832..f2ca5702a4e2 100644
--- a/arch/um/include/shared/frame_kern.h
+++ b/arch/um/include/shared/frame_kern.h
@@ -6,13 +6,13 @@
 #ifndef __FRAME_KERN_H_
 #define __FRAME_KERN_H_
 
-extern int setup_signal_stack_sc(unsigned long stack_top, int sig, 
+extern int setup_signal_stack_sc(unsigned long stack_top, int sig,
 				 struct k_sigaction *ka,
-				 struct pt_regs *regs, 
+				 struct pt_regs *regs,
 				 sigset_t *mask);
-extern int setup_signal_stack_si(unsigned long stack_top, int sig, 
+extern int setup_signal_stack_si(unsigned long stack_top, int sig,
 				 struct k_sigaction *ka,
-				 struct pt_regs *regs, siginfo_t *info, 
+				 struct pt_regs *regs, struct siginfo *info,
 				 sigset_t *mask);
 
 #endif
diff --git a/arch/um/kernel/signal.c b/arch/um/kernel/signal.c
index 3e831b3fd07b..f57e02e7910f 100644
--- a/arch/um/kernel/signal.c
+++ b/arch/um/kernel/signal.c
@@ -19,7 +19,7 @@ EXPORT_SYMBOL(unblock_signals);
  * OK, we're invoking a handler
  */
 static void handle_signal(struct pt_regs *regs, unsigned long signr,
-			 struct k_sigaction *ka, siginfo_t *info)
+			 struct k_sigaction *ka, struct siginfo *info)
 {
 	sigset_t *oldset = sigmask_to_save();
 	int singlestep = 0;
@@ -71,7 +71,7 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
 static int kern_do_signal(struct pt_regs *regs)
 {
 	struct k_sigaction ka_copy;
-	siginfo_t info;
+	struct siginfo info;
 	int sig, handled_sig = 0;
 
 	while ((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0) {
diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c
index ff03067a3b14..007d5503f49b 100644
--- a/arch/um/kernel/skas/mmu.c
+++ b/arch/um/kernel/skas/mmu.c
@@ -123,7 +123,7 @@ void uml_setup_stubs(struct mm_struct *mm)
 	/* dup_mmap already holds mmap_sem */
 	err = install_special_mapping(mm, STUB_START, STUB_END - STUB_START,
 				      VM_READ | VM_MAYREAD | VM_EXEC |
-				      VM_MAYEXEC | VM_DONTCOPY,
+				      VM_MAYEXEC | VM_DONTCOPY | VM_PFNMAP,
 				      mm->context.stub_pages);
 	if (err) {
 		printk(KERN_ERR "install_special_mapping returned %d\n", err);
diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
index 1d3e0c17340b..4ffb644d6c07 100644
--- a/arch/um/kernel/skas/uaccess.c
+++ b/arch/um/kernel/skas/uaccess.c
@@ -254,6 +254,6 @@ int strnlen_user(const void __user *str, int len)
 	n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
 	if (n == 0)
 		return count + 1;
-	return -EFAULT;
+	return 0;
 }
 EXPORT_SYMBOL(strnlen_user);
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c
index ba4398056fe9..3c4af77e51a2 100644
--- a/arch/um/os-Linux/mem.c
+++ b/arch/um/os-Linux/mem.c
@@ -53,6 +53,25 @@ static void __init find_tempdir(void)
 }
 
 /*
+ * Remove bytes from the front of the buffer and refill it so that if there's a
+ * partial string that we care about, it will be completed, and we can recognize
+ * it.
+ */
+static int pop(int fd, char *buf, size_t size, size_t npop)
+{
+	ssize_t n;
+	size_t len = strlen(&buf[npop]);
+
+	memmove(buf, &buf[npop], len + 1);
+	n = read(fd, &buf[len], size - len - 1);
+	if (n < 0)
+		return -errno;
+
+	buf[len + n] = '\0';
+	return 1;
+}
+
+/*
  * This will return 1, with the first character in buf being the
  * character following the next instance of c in the file.  This will
  * read the file as needed.  If there's an error, -errno is returned;
@@ -61,7 +80,6 @@ static void __init find_tempdir(void)
 static int next(int fd, char *buf, size_t size, char c)
 {
 	ssize_t n;
-	size_t len;
 	char *ptr;
 
 	while ((ptr = strchr(buf, c)) == NULL) {
@@ -74,20 +92,129 @@ static int next(int fd, char *buf, size_t size, char c)
 		buf[n] = '\0';
 	}
 
-	ptr++;
-	len = strlen(ptr);
-	memmove(buf, ptr, len + 1);
+	return pop(fd, buf, size, ptr - buf + 1);
+}
+
+/*
+ * Decode an octal-escaped and space-terminated path of the form used by
+ * /proc/mounts. May be used to decode a path in-place. "out" must be at least
+ * as large as the input. The output is always null-terminated. "len" gets the
+ * length of the output, excluding the trailing null. Returns 0 if a full path
+ * was successfully decoded, otherwise an error.
+ */
+static int decode_path(const char *in, char *out, size_t *len)
+{
+	char *first = out;
+	int c;
+	int i;
+	int ret = -EINVAL;
+	while (1) {
+		switch (*in) {
+		case '\0':
+			goto out;
+
+		case ' ':
+			ret = 0;
+			goto out;
+
+		case '\\':
+			in++;
+			c = 0;
+			for (i = 0; i < 3; i++) {
+				if (*in < '0' || *in > '7')
+					goto out;
+				c = (c << 3) | (*in++ - '0');
+			}
+			*(unsigned char *)out++ = (unsigned char) c;
+			break;
+
+		default:
+			*out++ = *in++;
+			break;
+		}
+	}
+
+out:
+	*out = '\0';
+	*len = out - first;
+	return ret;
+}
+
+/*
+ * Computes the length of s when encoded with three-digit octal escape sequences
+ * for the characters in chars.
+ */
+static size_t octal_encoded_length(const char *s, const char *chars)
+{
+	size_t len = strlen(s);
+	while ((s = strpbrk(s, chars)) != NULL) {
+		len += 3;
+		s++;
+	}
+
+	return len;
+}
+
+enum {
+	OUTCOME_NOTHING_MOUNTED,
+	OUTCOME_TMPFS_MOUNT,
+	OUTCOME_NON_TMPFS_MOUNT,
+};
+
+/* Read a line of /proc/mounts data looking for a tmpfs mount at "path". */
+static int read_mount(int fd, char *buf, size_t bufsize, const char *path,
+		      int *outcome)
+{
+	int found;
+	int match;
+	char *space;
+	size_t len;
+
+	enum {
+		MATCH_NONE,
+		MATCH_EXACT,
+		MATCH_PARENT,
+	};
+
+	found = next(fd, buf, bufsize, ' ');
+	if (found != 1)
+		return found;
 
 	/*
-	 * Refill the buffer so that if there's a partial string that we care
-	 * about, it will be completed, and we can recognize it.
+	 * If there's no following space in the buffer, then this path is
+	 * truncated, so it can't be the one we're looking for.
 	 */
-	n = read(fd, &buf[len], size - len - 1);
-	if (n < 0)
-		return -errno;
+	space = strchr(buf, ' ');
+	if (space) {
+		match = MATCH_NONE;
+		if (!decode_path(buf, buf, &len)) {
+			if (!strcmp(buf, path))
+				match = MATCH_EXACT;
+			else if (!strncmp(buf, path, len)
+				 && (path[len] == '/' || !strcmp(buf, "/")))
+				match = MATCH_PARENT;
+		}
+
+		found = pop(fd, buf, bufsize, space - buf + 1);
+		if (found != 1)
+			return found;
+
+		switch (match) {
+		case MATCH_EXACT:
+			if (!strncmp(buf, "tmpfs", strlen("tmpfs")))
+				*outcome = OUTCOME_TMPFS_MOUNT;
+			else
+				*outcome = OUTCOME_NON_TMPFS_MOUNT;
+			break;
 
-	buf[len + n] = '\0';
-	return 1;
+		case MATCH_PARENT:
+			/* This mount obscures any previous ones. */
+			*outcome = OUTCOME_NOTHING_MOUNTED;
+			break;
+		}
+	}
+
+	return next(fd, buf, bufsize, '\n');
 }
 
 /* which_tmpdir is called only during early boot */
@@ -106,8 +233,12 @@ static int checked_tmpdir = 0;
  */
 static void which_tmpdir(void)
 {
-	int fd, found;
-	char buf[128] = { '\0' };
+	int fd;
+	int found;
+	int outcome;
+	char *path;
+	char *buf;
+	size_t bufsize;
 
 	if (checked_tmpdir)
 		return;
@@ -116,49 +247,66 @@ static void which_tmpdir(void)
 
 	printf("Checking for tmpfs mount on /dev/shm...");
 
+	path = realpath("/dev/shm", NULL);
+	if (!path) {
+		printf("failed to check real path, errno = %d\n", errno);
+		return;
+	}
+	printf("%s...", path);
+
+	/*
+	 * The buffer needs to be able to fit the full octal-escaped path, a
+	 * space, and a trailing null in order to successfully decode it.
+	 */
+	bufsize = octal_encoded_length(path, " \t\n\\") + 2;
+
+	if (bufsize < 128)
+		bufsize = 128;
+
+	buf = malloc(bufsize);
+	if (!buf) {
+		printf("malloc failed, errno = %d\n", errno);
+		goto out;
+	}
+	buf[0] = '\0';
+
 	fd = open("/proc/mounts", O_RDONLY);
 	if (fd < 0) {
 		printf("failed to open /proc/mounts, errno = %d\n", errno);
-		return;
+		goto out1;
 	}
 
+	outcome = OUTCOME_NOTHING_MOUNTED;
 	while (1) {
-		found = next(fd, buf, ARRAY_SIZE(buf), ' ');
-		if (found != 1)
-			break;
-
-		if (!strncmp(buf, "/dev/shm", strlen("/dev/shm")))
-			goto found;
-
-		found = next(fd, buf, ARRAY_SIZE(buf), '\n');
+		found = read_mount(fd, buf, bufsize, path, &outcome);
 		if (found != 1)
 			break;
 	}
 
-err:
-	if (found == 0)
-		printf("nothing mounted on /dev/shm\n");
-	else if (found < 0)
+	if (found < 0) {
 		printf("read returned errno %d\n", -found);
+	} else {
+		switch (outcome) {
+		case OUTCOME_TMPFS_MOUNT:
+			printf("OK\n");
+			default_tmpdir = "/dev/shm";
+			break;
 
-out:
-	close(fd);
-
-	return;
-
-found:
-	found = next(fd, buf, ARRAY_SIZE(buf), ' ');
-	if (found != 1)
-		goto err;
+		case OUTCOME_NON_TMPFS_MOUNT:
+			printf("not tmpfs\n");
+			break;
 
-	if (strncmp(buf, "tmpfs", strlen("tmpfs"))) {
-		printf("not tmpfs\n");
-		goto out;
+		default:
+			printf("nothing mounted on /dev/shm\n");
+			break;
+		}
 	}
 
-	printf("OK\n");
-	default_tmpdir = "/dev/shm";
-	goto out;
+	close(fd);
+out1:
+	free(buf);
+out:
+	free(path);
 }
 
 static int __init make_tempfile(const char *template, char **out_tempname,
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index 9d9f1b4bf826..905924b773d3 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -25,7 +25,7 @@ void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
 	[SIGIO]		= sigio_handler,
 	[SIGVTALRM]	= timer_handler };
 
-static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc)
+static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
 {
 	struct uml_pt_regs r;
 	int save_errno = errno;
@@ -61,7 +61,7 @@ static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc)
 static int signals_enabled;
 static unsigned int signals_pending;
 
-void sig_handler(int sig, siginfo_t *si, mcontext_t *mc)
+void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
 {
 	int enabled;
 
@@ -120,7 +120,7 @@ void set_sigstack(void *sig_stack, int size)
 		panic("enabling signal stack failed, errno = %d\n", errno);
 }
 
-static void (*handlers[_NSIG])(int sig, siginfo_t *si, mcontext_t *mc) = {
+static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
 	[SIGSEGV] = sig_handler,
 	[SIGBUS] = sig_handler,
 	[SIGILL] = sig_handler,
@@ -162,7 +162,7 @@ static void hard_handler(int sig, siginfo_t *si, void *p)
 		while ((sig = ffs(pending)) != 0){
 			sig--;
 			pending &= ~(1 << sig);
-			(*handlers[sig])(sig, si, mc);
+			(*handlers[sig])(sig, (struct siginfo *)si, mc);
 		}
 
 		/*
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 4625949bf1e4..d531879a4617 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -54,7 +54,7 @@ static int ptrace_dump_regs(int pid)
 
 void wait_stub_done(int pid)
 {
-	int n, status, err;
+	int n, status, err, bad_stop = 0;
 
 	while (1) {
 		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED | __WALL));
@@ -74,6 +74,8 @@ void wait_stub_done(int pid)
 
 	if (((1 << WSTOPSIG(status)) & STUB_DONE_MASK) != 0)
 		return;
+	else
+		bad_stop = 1;
 
 bad_wait:
 	err = ptrace_dump_regs(pid);
@@ -83,7 +85,10 @@ bad_wait:
 	printk(UM_KERN_ERR "wait_stub_done : failed to wait for SIGTRAP, "
 	       "pid = %d, n = %d, errno = %d, status = 0x%x\n", pid, n, errno,
 	       status);
-	fatal_sigsegv();
+	if (bad_stop)
+		kill(pid, SIGKILL);
+	else
+		fatal_sigsegv();
 }
 
 extern unsigned long current_stub_stack(void);
@@ -409,7 +414,7 @@ void userspace(struct uml_pt_regs *regs)
 		if (WIFSTOPPED(status)) {
 			int sig = WSTOPSIG(status);
 
-			ptrace(PTRACE_GETSIGINFO, pid, 0, &si);
+			ptrace(PTRACE_GETSIGINFO, pid, 0, (struct siginfo *)&si);
 
 			switch (sig) {
 			case SIGSEGV:
@@ -417,7 +422,7 @@ void userspace(struct uml_pt_regs *regs)
 				    !ptrace_faultinfo) {
 					get_skas_faultinfo(pid,
 							   &regs->faultinfo);
-					(*sig_info[SIGSEGV])(SIGSEGV, &si,
+					(*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si,
 							     regs);
 				}
 				else handle_segv(pid, regs);
@@ -426,14 +431,14 @@ void userspace(struct uml_pt_regs *regs)
 			        handle_trap(pid, regs, local_using_sysemu);
 				break;
 			case SIGTRAP:
-				relay_signal(SIGTRAP, &si, regs);
+				relay_signal(SIGTRAP, (struct siginfo *)&si, regs);
 				break;
 			case SIGVTALRM:
 				now = os_nsecs();
 				if (now < nsecs)
 					break;
 				block_signals();
-				(*sig_info[sig])(sig, &si, regs);
+				(*sig_info[sig])(sig, (struct siginfo *)&si, regs);
 				unblock_signals();
 				nsecs = timer.it_value.tv_sec *
 					UM_NSEC_PER_SEC +
@@ -447,7 +452,7 @@ void userspace(struct uml_pt_regs *regs)
 			case SIGFPE:
 			case SIGWINCH:
 				block_signals();
-				(*sig_info[sig])(sig, &si, regs);
+				(*sig_info[sig])(sig, (struct siginfo *)&si, regs);
 				unblock_signals();
 				break;
 			default:
diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c
index ae7319db18ee..5e04a1c899fa 100644
--- a/arch/x86/um/signal.c
+++ b/arch/x86/um/signal.c
@@ -508,7 +508,6 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
 {
 	struct rt_sigframe __user *frame;
 	int err = 0;
-	struct task_struct *me = current;
 
 	frame = (struct rt_sigframe __user *)
 		round_down(stack_top - sizeof(struct rt_sigframe), 16);