diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-19 15:11:09 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-19 15:11:09 -0700 |
commit | d471ce53b1fab60110e4e9f647a345cea31752de (patch) | |
tree | 73acb87c823b63d026f9ba4617b5e112835d6abf | |
parent | 1b050180454dc226780f765a33575d4cd8d6e552 (diff) | |
parent | 9e82d450531c79b18ab18c9b9645cdd9db31ee98 (diff) |
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.h | 8 | ||||
-rw-r--r-- | arch/um/kernel/signal.c | 4 | ||||
-rw-r--r-- | arch/um/kernel/skas/mmu.c | 2 | ||||
-rw-r--r-- | arch/um/kernel/skas/uaccess.c | 2 | ||||
-rw-r--r-- | arch/um/os-Linux/mem.c | 230 | ||||
-rw-r--r-- | arch/um/os-Linux/signal.c | 8 | ||||
-rw-r--r-- | arch/um/os-Linux/skas/process.c | 19 | ||||
-rw-r--r-- | arch/x86/um/signal.c | 1 |
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, ®s->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); |