This pull request contains the following changes for UML:

- Clang coverage support
 - Many cleanups from Benjamin Berg
 - Various minor fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCAA0FiEEdgfidid8lnn52cLTZvlZhesYu8EFAmWi86sWHHJpY2hhcmRA
 c2lnbWEtc3Rhci5hdAAKCRBm+VmF6xi7wWB9D/0XLw6RzLkav+fZzaNhNsP1jzGn
 cQ3Yl29WcCsL+7iMVy/fLPZns8SlgBtVDZ6YZMNxrjjOBLu1VTOv30dNwYpvKUDj
 ERakIpZeqg8+w6+pOK3lLWRkjjBVDrFM+e6vgZ+2iEFkvHhyHk7LhcfAanC3CTUO
 qU4dLqZDpO4H98xhkbksc847I8Yg8mx9vLrqZBxQ8tfT2tLHMfzuR7kCvRfxOFso
 4QDe4NImgEF5WLlK3Aj92NWxbMUZvw1f0bEuJGQJPr6GsLbNcsrlQCZIis/mov82
 6176c3Jl1lHyBYsYyPAagVpqVBN+Q1gL1+YGz4Nv+wZFTqk5RvxMzVDdkpP6QEAV
 ZJjdJdBJHrAqrzIdiY8XfPYBsu5iZYCLr8q3wJxQYYBrV3s8eXsFXMqoLHKBVOe6
 OSgTv2OJrSvXZzXgI+5nysVVqOeoImcRzQXuVHha5jhrf6sPeGxB+hOuTccPLOG4
 kIEoC+8CaR+vlHBspg2qU+wN0QAqvIFPID05p0GM37UnskxeOxSksMSkwPAVdhqD
 6Mq+L5TDx6ShRfQ0KC1PGmByv312bRLudZ34BBAhoEspixI1AISbDn2wX7pKqERq
 yC9h/49XmjqcCjmE2TsQ1k4zhUSo8CT/Z8SUmrmqnbrUmchdRCEBplolZpBbKfGO
 U+l4hGt7O5BrPO0Tzw==
 =oH6k
 -----END PGP SIGNATURE-----

Merge tag 'uml-for-linus-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/uml/linux

Pull UML updates from Richard Weinberger:

 - Clang coverage support

 - Many cleanups from Benjamin Berg

 - Various minor fixes

* tag 'uml-for-linus-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/uml/linux:
  um: Mark 32bit syscall helpers as clobbering memory
  um: Remove unused register save/restore functions
  um: Rely on PTRACE_SETREGSET to set FS/GS base registers
  Documentation: kunit: Add clang UML coverage example
  arch: um: Add Clang coverage support
  um: time-travel: fix time corruption
  um: net: Fix return type of uml_net_start_xmit()
  um: Always inline stub functions
  um: Do not use printk in userspace trampoline
  um: Reap winch thread if it fails
  um: Do not use printk in SIGWINCH helper thread
  um: Don't use vfprintf() for os_info()
  um: Make errors to stop ptraced child fatal during startup
  um: Drop NULL check from start_userspace
  um: Drop support for hosts without SYSEMU_SINGLESTEP support
  um: document arch_futex_atomic_op_inuser
  um: mmu: remove stub_pages
  um: Fix naming clash between UML and scheduler
  um: virt-pci: fix platform map offset
This commit is contained in:
Linus Torvalds 2024-01-17 10:44:34 -08:00
commit 6cff79f4b9
34 changed files with 211 additions and 458 deletions

View File

@ -139,6 +139,17 @@ If your installed version of gcc doesn't work, you can tweak the steps:
$ ./tools/testing/kunit/kunit.py run --make_options=CC=/usr/bin/gcc-6
$ lcov -t "my_kunit_tests" -o coverage.info -c -d .kunit/ --gcov-tool=/usr/bin/gcov-6
Alternatively, LLVM-based toolchains can also be used:
.. code-block:: bash
# Build with LLVM and append coverage options to the current config
$ ./tools/testing/kunit/kunit.py run --make_options LLVM=1 --kunitconfig=.kunit/ --kunitconfig=tools/testing/kunit/configs/coverage_uml.config
$ llvm-profdata merge -sparse default.profraw -o default.profdata
$ llvm-cov export --format=lcov .kunit/vmlinux -instr-profile default.profdata > coverage.info
# The coverage.info file is in lcov-compatible format and it can be used to e.g. generate HTML report
$ genhtml -o /tmp/coverage_html coverage.info
Running tests manually
======================

View File

@ -4,7 +4,12 @@
#
GPROF_OPT += -pg
ifdef CONFIG_CC_IS_CLANG
GCOV_OPT += -fprofile-instr-generate -fcoverage-mapping
else
GCOV_OPT += -fprofile-arcs -ftest-coverage
endif
CFLAGS-$(CONFIG_GCOV) += $(GCOV_OPT)
CFLAGS-$(CONFIG_GPROF) += $(GPROF_OPT)

View File

@ -141,7 +141,7 @@ struct winch_data {
int pipe_fd;
};
static int winch_thread(void *arg)
static __noreturn int winch_thread(void *arg)
{
struct winch_data *data = arg;
sigset_t sigs;
@ -153,8 +153,8 @@ static int winch_thread(void *arg)
pipe_fd = data->pipe_fd;
count = write(pipe_fd, &c, sizeof(c));
if (count != sizeof(c))
printk(UM_KERN_ERR "winch_thread : failed to write "
"synchronization byte, err = %d\n", -count);
os_info("winch_thread : failed to write synchronization byte, err = %d\n",
-count);
/*
* We are not using SIG_IGN on purpose, so don't fix it as I thought to
@ -166,29 +166,29 @@ static int winch_thread(void *arg)
sigfillset(&sigs);
/* Block all signals possible. */
if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) {
printk(UM_KERN_ERR "winch_thread : sigprocmask failed, "
"errno = %d\n", errno);
exit(1);
os_info("winch_thread : sigprocmask failed, errno = %d\n",
errno);
goto wait_kill;
}
/* In sigsuspend(), block anything else than SIGWINCH. */
sigdelset(&sigs, SIGWINCH);
if (setsid() < 0) {
printk(UM_KERN_ERR "winch_thread : setsid failed, errno = %d\n",
os_info("winch_thread : setsid failed, errno = %d\n",
errno);
exit(1);
goto wait_kill;
}
if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) {
printk(UM_KERN_ERR "winch_thread : TIOCSCTTY failed on "
"fd %d err = %d\n", pty_fd, errno);
exit(1);
os_info("winch_thread : TIOCSCTTY failed on "
"fd %d err = %d\n", pty_fd, errno);
goto wait_kill;
}
if (tcsetpgrp(pty_fd, os_getpid()) < 0) {
printk(UM_KERN_ERR "winch_thread : tcsetpgrp failed on "
"fd %d err = %d\n", pty_fd, errno);
exit(1);
os_info("winch_thread : tcsetpgrp failed on fd %d err = %d\n",
pty_fd, errno);
goto wait_kill;
}
/*
@ -199,8 +199,8 @@ static int winch_thread(void *arg)
*/
count = read(pipe_fd, &c, sizeof(c));
if (count != sizeof(c))
printk(UM_KERN_ERR "winch_thread : failed to read "
"synchronization byte, err = %d\n", errno);
os_info("winch_thread : failed to read synchronization byte, err = %d\n",
errno);
while(1) {
/*
@ -211,9 +211,15 @@ static int winch_thread(void *arg)
count = write(pipe_fd, &c, sizeof(c));
if (count != sizeof(c))
printk(UM_KERN_ERR "winch_thread : write failed, "
"err = %d\n", errno);
os_info("winch_thread : write failed, err = %d\n",
errno);
}
wait_kill:
c = 2;
count = write(pipe_fd, &c, sizeof(c));
while (1)
pause();
}
static int winch_tramp(int fd, struct tty_port *port, int *fd_out,

View File

@ -629,15 +629,18 @@ static irqreturn_t winch_interrupt(int irq, void *data)
if (fd != -1) {
err = generic_read(fd, &c, NULL);
if (err < 0) {
/* A read of 2 means the winch thread failed and has warned */
if (err < 0 || (err == 1 && c == 2)) {
if (err != -EAGAIN) {
winch->fd = -1;
list_del(&winch->list);
os_close_file(fd);
printk(KERN_ERR "winch_interrupt : "
"read failed, errno = %d\n", -err);
printk(KERN_ERR "fd %d is losing SIGWINCH "
"support\n", winch->tty_fd);
if (err < 0) {
printk(KERN_ERR "winch_interrupt : read failed, errno = %d\n",
-err);
printk(KERN_ERR "fd %d is losing SIGWINCH support\n",
winch->tty_fd);
}
INIT_WORK(&winch->work, __free_winch);
schedule_work(&winch->work);
return IRQ_HANDLED;

View File

@ -204,7 +204,7 @@ static int uml_net_close(struct net_device *dev)
return 0;
}
static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct uml_net_private *lp = netdev_priv(dev);
unsigned long flags;

View File

@ -971,7 +971,7 @@ static long um_pci_map_platform(unsigned long offset, size_t size,
*ops = &um_pci_device_bar_ops;
*priv = &um_pci_platform_device->resptr[0];
return 0;
return offset;
}
static const struct logic_iomem_region_ops um_pci_platform_ops = {

View File

@ -12,7 +12,6 @@
typedef struct mm_context {
struct mm_id id;
struct uml_arch_mm_context arch;
struct page *stub_pages[2];
} mm_context_t;
extern void __switch_mm(struct mm_id * mm_idp);

View File

@ -22,7 +22,6 @@ struct mm_struct;
struct thread_struct {
struct pt_regs regs;
struct pt_regs *segv_regs;
int singlestep_syscall;
void *fault_addr;
jmp_buf *fault_catcher;
struct task_struct *prev_sched;

View File

@ -34,7 +34,6 @@ extern int handle_page_fault(unsigned long address, unsigned long ip,
extern unsigned int do_IRQ(int irq, struct uml_pt_regs *regs);
extern void initial_thread_cb(void (*proc)(void *), void *arg);
extern int is_syscall(unsigned long addr);
extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
@ -50,7 +49,7 @@ extern void do_uml_exitcalls(void);
* Are we disallowed to sleep? Used to choose between GFP_KERNEL and
* GFP_ATOMIC.
*/
extern int __cant_sleep(void);
extern int __uml_cant_sleep(void);
extern int get_current_pid(void);
extern int copy_from_user_proc(void *to, void *from, int size);
extern char *uml_strdup(const char *string);
@ -58,7 +57,7 @@ extern char *uml_strdup(const char *string);
extern unsigned long to_irq_stack(unsigned long *mask_out);
extern unsigned long from_irq_stack(int nested);
extern int singlestepping(void *t);
extern int singlestepping(void);
extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs);

View File

@ -323,9 +323,6 @@ extern void sigio_broken(int fd);
extern int __add_sigio_fd(int fd);
extern int __ignore_sigio_fd(int fd);
/* prctl.c */
extern int os_arch_prctl(int pid, int option, unsigned long *arg2);
/* tty.c */
extern int get_pty(void);

View File

@ -12,45 +12,4 @@
extern int ptrace_getregs(long pid, unsigned long *regs_out);
extern int ptrace_setregs(long pid, unsigned long *regs_in);
/* syscall emulation path in ptrace */
#ifndef PTRACE_SYSEMU
#define PTRACE_SYSEMU 31
#endif
#ifndef PTRACE_SYSEMU_SINGLESTEP
#define PTRACE_SYSEMU_SINGLESTEP 32
#endif
/* 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.
* For binary compatibility, 2.6 also supports the old "21", named
* PTRACE_OLDSETOPTION. On these architectures, UML always must use
* "21", to ensure the kernel runs on 2.4 and 2.6 host without
* recompilation. So, we use PTRACE_OLDSETOPTIONS in UML.
* We also want to be able to build the kernel on 2.4, which doesn't
* have PTRACE_OLDSETOPTIONS. So, if it is missing, we declare
* PTRACE_OLDSETOPTIONS to be the same as PTRACE_SETOPTIONS.
*
* On architectures, that start to support PTRACE_O_TRACESYSGOOD on
* linux 2.6, PTRACE_OLDSETOPTIONS never is defined, and also isn't
* supported by the host kernel. In that case, our trick lets us use
* the new 0x4200 with the name PTRACE_OLDSETOPTIONS.
*/
#ifndef PTRACE_OLDSETOPTIONS
#define PTRACE_OLDSETOPTIONS PTRACE_SETOPTIONS
#endif
void set_using_sysemu(int value);
int get_using_sysemu(void);
extern int sysemu_supported;
#define SELECT_PTRACE_OPERATION(sysemu_mode, singlestep_mode) \
(((int[3][3] ) { \
{ PTRACE_SYSCALL, PTRACE_SYSCALL, PTRACE_SINGLESTEP }, \
{ PTRACE_SYSEMU, PTRACE_SYSEMU, PTRACE_SINGLESTEP }, \
{ PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP, \
PTRACE_SYSEMU_SINGLESTEP } }) \
[sysemu_mode][singlestep_mode])
#endif

View File

@ -14,8 +14,6 @@ extern int save_fp_registers(int pid, unsigned long *fp_regs);
extern int restore_fp_registers(int pid, unsigned long *fp_regs);
extern int save_fpx_registers(int pid, unsigned long *fp_regs);
extern int restore_fpx_registers(int pid, unsigned long *fp_regs);
extern int save_registers(int pid, struct uml_pt_regs *regs);
extern int restore_pid_registers(int pid, struct uml_pt_regs *regs);
extern int init_pid_registers(int pid);
extern void get_safe_registers(unsigned long *regs, unsigned long *fp_regs);
extern int get_fp_registers(int pid, unsigned long *regs);

View File

@ -220,7 +220,7 @@ void arch_cpu_idle(void)
um_idle_sleep();
}
int __cant_sleep(void) {
int __uml_cant_sleep(void) {
return in_atomic() || irqs_disabled() || in_interrupt();
/* Is in_interrupt() really needed? */
}
@ -332,17 +332,9 @@ int __init make_proc_sysemu(void)
late_initcall(make_proc_sysemu);
int singlestepping(void * t)
int singlestepping(void)
{
struct task_struct *task = t ? t : current;
if (!test_thread_flag(TIF_SINGLESTEP))
return 0;
if (task->thread.singlestep_syscall)
return 1;
return 2;
return test_thread_flag(TIF_SINGLESTEP);
}
/*

View File

@ -12,7 +12,6 @@
void user_enable_single_step(struct task_struct *child)
{
set_tsk_thread_flag(child, TIF_SINGLESTEP);
child->thread.singlestep_syscall = 0;
#ifdef SUBARCH_SET_SINGLESTEPPING
SUBARCH_SET_SINGLESTEPPING(child, 1);
@ -22,7 +21,6 @@ void user_enable_single_step(struct task_struct *child)
void user_disable_single_step(struct task_struct *child)
{
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
child->thread.singlestep_syscall = 0;
#ifdef SUBARCH_SET_SINGLESTEPPING
SUBARCH_SET_SINGLESTEPPING(child, 0);

View File

@ -120,18 +120,6 @@ void do_signal(struct pt_regs *regs)
}
}
/*
* This closes a way to execute a system call on the host. If
* you set a breakpoint on a system call instruction and singlestep
* from it, the tracing thread used to PTRACE_SINGLESTEP the process
* rather than PTRACE_SYSCALL it, allowing the system call to execute
* on the host. The tracing thread will check this flag and
* PTRACE_SYSCALL if necessary.
*/
if (test_thread_flag(TIF_SINGLESTEP))
current->thread.singlestep_syscall =
is_syscall(PT_REGS_IP(&current->thread.regs));
/*
* if there's no signal to deliver, we just put the saved sigmask
* back

View File

@ -236,7 +236,9 @@ EXPORT_SYMBOL(strnlen_user);
* argument and comparison of the previous
* futex value with another constant.
*
* @encoded_op: encoded operation to execute
* @op: operation to execute
* @oparg: argument to operation
* @oval: old value at uaddr
* @uaddr: pointer to user space address
*
* Return:

View File

@ -432,9 +432,29 @@ static void time_travel_update_time(unsigned long long next, bool idle)
time_travel_del_event(&ne);
}
static void time_travel_update_time_rel(unsigned long long offs)
{
unsigned long flags;
/*
* Disable interrupts before calculating the new time so
* that a real timer interrupt (signal) can't happen at
* a bad time e.g. after we read time_travel_time but
* before we've completed updating the time.
*/
local_irq_save(flags);
time_travel_update_time(time_travel_time + offs, false);
local_irq_restore(flags);
}
void time_travel_ndelay(unsigned long nsec)
{
time_travel_update_time(time_travel_time + nsec, false);
/*
* Not strictly needed to use _rel() version since this is
* only used in INFCPU/EXT modes, but it doesn't hurt and
* is more readable too.
*/
time_travel_update_time_rel(nsec);
}
EXPORT_SYMBOL(time_travel_ndelay);
@ -568,7 +588,11 @@ static void time_travel_set_start(void)
#define time_travel_time 0
#define time_travel_ext_waiting 0
static inline void time_travel_update_time(unsigned long long ns, bool retearly)
static inline void time_travel_update_time(unsigned long long ns, bool idle)
{
}
static inline void time_travel_update_time_rel(unsigned long long offs)
{
}
@ -720,9 +744,7 @@ static u64 timer_read(struct clocksource *cs)
*/
if (!irqs_disabled() && !in_interrupt() && !in_softirq() &&
!time_travel_ext_waiting)
time_travel_update_time(time_travel_time +
TIMER_MULTIPLIER,
false);
time_travel_update_time_rel(TIMER_MULTIPLIER);
return time_travel_time / TIMER_MULTIPLIER;
}

View File

@ -46,7 +46,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
unsigned long stack, sp;
int pid, fds[2], ret, n;
stack = alloc_stack(0, __cant_sleep());
stack = alloc_stack(0, __uml_cant_sleep());
if (stack == 0)
return -ENOMEM;
@ -70,7 +70,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
data.pre_data = pre_data;
data.argv = argv;
data.fd = fds[1];
data.buf = __cant_sleep() ? uml_kmalloc(PATH_MAX, UM_GFP_ATOMIC) :
data.buf = __uml_cant_sleep() ? uml_kmalloc(PATH_MAX, UM_GFP_ATOMIC) :
uml_kmalloc(PATH_MAX, UM_GFP_KERNEL);
pid = clone(helper_child, (void *) sp, CLONE_VM, &data);
if (pid < 0) {
@ -121,7 +121,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
unsigned long stack, sp;
int pid, status, err;
stack = alloc_stack(0, __cant_sleep());
stack = alloc_stack(0, __uml_cant_sleep());
if (stack == 0)
return -ENOMEM;

View File

@ -11,26 +11,6 @@
#include <sysdep/ptrace_user.h>
#include <registers.h>
int save_registers(int pid, struct uml_pt_regs *regs)
{
int err;
err = ptrace(PTRACE_GETREGS, pid, 0, regs->gp);
if (err < 0)
return -errno;
return 0;
}
int restore_pid_registers(int pid, struct uml_pt_regs *regs)
{
int err;
err = ptrace(PTRACE_SETREGS, pid, 0, regs->gp);
if (err < 0)
return -errno;
return 0;
}
/* This is set once at boot time and not changed thereafter */
static unsigned long exec_regs[MAX_REG_NR];

View File

@ -177,48 +177,11 @@ static void handle_segv(int pid, struct uml_pt_regs *regs, unsigned long *aux_fp
segv(regs->faultinfo, 0, 1, NULL);
}
/*
* To use the same value of using_sysemu as the caller, ask it that value
* (in local_using_sysemu
*/
static void handle_trap(int pid, struct uml_pt_regs *regs,
int local_using_sysemu)
static void handle_trap(int pid, struct uml_pt_regs *regs)
{
int err, status;
if ((UPT_IP(regs) >= STUB_START) && (UPT_IP(regs) < STUB_END))
fatal_sigsegv();
if (!local_using_sysemu)
{
err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
__NR_getpid);
if (err < 0) {
printk(UM_KERN_ERR "%s - nullifying syscall failed, errno = %d\n",
__func__, errno);
fatal_sigsegv();
}
err = ptrace(PTRACE_SYSCALL, pid, 0, 0);
if (err < 0) {
printk(UM_KERN_ERR "%s - continuing to end of syscall failed, errno = %d\n",
__func__, errno);
fatal_sigsegv();
}
CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED | __WALL));
if ((err < 0) || !WIFSTOPPED(status) ||
(WSTOPSIG(status) != SIGTRAP + 0x80)) {
err = ptrace_dump_regs(pid);
if (err)
printk(UM_KERN_ERR "Failed to get registers from process, errno = %d\n",
-err);
printk(UM_KERN_ERR "%s - failed to wait at end of syscall, errno = %d, status = %d\n",
__func__, errno, status);
fatal_sigsegv();
}
}
handle_syscall(regs);
}
@ -226,7 +189,7 @@ extern char __syscall_stub_start[];
/**
* userspace_tramp() - userspace trampoline
* @stack: pointer to the new userspace stack page, can be NULL, if? FIXME:
* @stack: pointer to the new userspace stack page
*
* The userspace trampoline is used to setup a new userspace process in start_userspace() after it was clone()'ed.
* This function will run on a temporary stack page.
@ -241,9 +204,13 @@ extern char __syscall_stub_start[];
*/
static int userspace_tramp(void *stack)
{
struct sigaction sa;
void *addr;
int fd;
unsigned long long offset;
unsigned long segv_handler = STUB_CODE +
(unsigned long) stub_segv_handler -
(unsigned long) __syscall_stub_start;
ptrace(PTRACE_TRACEME, 0, 0, 0);
@ -254,39 +221,30 @@ static int userspace_tramp(void *stack)
addr = mmap64((void *) STUB_CODE, UM_KERN_PAGE_SIZE,
PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, offset);
if (addr == MAP_FAILED) {
printk(UM_KERN_ERR "mapping mmap stub at 0x%lx failed, errno = %d\n",
STUB_CODE, errno);
os_info("mapping mmap stub at 0x%lx failed, errno = %d\n",
STUB_CODE, errno);
exit(1);
}
if (stack != NULL) {
fd = phys_mapping(uml_to_phys(stack), &offset);
addr = mmap((void *) STUB_DATA,
STUB_DATA_PAGES * UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, fd, offset);
if (addr == MAP_FAILED) {
printk(UM_KERN_ERR "mapping segfault stack at 0x%lx failed, errno = %d\n",
STUB_DATA, errno);
exit(1);
}
fd = phys_mapping(uml_to_phys(stack), &offset);
addr = mmap((void *) STUB_DATA,
STUB_DATA_PAGES * UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, fd, offset);
if (addr == MAP_FAILED) {
os_info("mapping segfault stack at 0x%lx failed, errno = %d\n",
STUB_DATA, errno);
exit(1);
}
if (stack != NULL) {
struct sigaction sa;
unsigned long v = STUB_CODE +
(unsigned long) stub_segv_handler -
(unsigned long) __syscall_stub_start;
set_sigstack((void *) STUB_DATA, STUB_DATA_PAGES * UM_KERN_PAGE_SIZE);
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO;
sa.sa_sigaction = (void *) v;
sa.sa_restorer = NULL;
if (sigaction(SIGSEGV, &sa, NULL) < 0) {
printk(UM_KERN_ERR "%s - setting SIGSEGV handler failed - errno = %d\n",
__func__, errno);
exit(1);
}
set_sigstack((void *) STUB_DATA, STUB_DATA_PAGES * UM_KERN_PAGE_SIZE);
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO;
sa.sa_sigaction = (void *) segv_handler;
sa.sa_restorer = NULL;
if (sigaction(SIGSEGV, &sa, NULL) < 0) {
os_info("%s - setting SIGSEGV handler failed - errno = %d\n",
__func__, errno);
exit(1);
}
kill(os_getpid(), SIGSTOP);
@ -298,7 +256,7 @@ int kill_userspace_mm[NR_CPUS];
/**
* start_userspace() - prepare a new userspace process
* @stub_stack: pointer to the stub stack. Can be NULL, if? FIXME:
* @stub_stack: pointer to the stub stack.
*
* Setups a new temporary stack page that is used while userspace_tramp() runs
* Clones the kernel process into a new userspace process, with FDs only.
@ -355,10 +313,10 @@ int start_userspace(unsigned long stub_stack)
goto out_kill;
}
if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL,
if (ptrace(PTRACE_SETOPTIONS, pid, NULL,
(void *) PTRACE_O_TRACESYSGOOD) < 0) {
err = -errno;
printk(UM_KERN_ERR "%s : PTRACE_OLDSETOPTIONS failed, errno = %d\n",
printk(UM_KERN_ERR "%s : PTRACE_SETOPTIONS failed, errno = %d\n",
__func__, errno);
goto out_kill;
}
@ -380,8 +338,6 @@ int start_userspace(unsigned long stub_stack)
void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
{
int err, status, op, pid = userspace_pid[0];
/* To prevent races if using_sysemu changes under us.*/
int local_using_sysemu;
siginfo_t si;
/* Handle any immediate reschedules or signals */
@ -411,11 +367,10 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
fatal_sigsegv();
}
/* Now we set local_using_sysemu to be used for one loop */
local_using_sysemu = get_using_sysemu();
op = SELECT_PTRACE_OPERATION(local_using_sysemu,
singlestepping(NULL));
if (singlestepping())
op = PTRACE_SYSEMU_SINGLESTEP;
else
op = PTRACE_SYSEMU;
if (ptrace(op, pid, 0, 0)) {
printk(UM_KERN_ERR "%s - ptrace continue failed, op = %d, errno = %d\n",
@ -474,7 +429,7 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
else handle_segv(pid, regs, aux_fp_regs);
break;
case SIGTRAP + 0x80:
handle_trap(pid, regs, local_using_sysemu);
handle_trap(pid, regs);
break;
case SIGTRAP:
relay_signal(SIGTRAP, (struct siginfo *)&si, regs);
@ -597,10 +552,10 @@ int copy_context_skas0(unsigned long new_stack, int pid)
goto out_kill;
}
if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL,
if (ptrace(PTRACE_SETOPTIONS, pid, NULL,
(void *)PTRACE_O_TRACESYSGOOD) < 0) {
err = -errno;
printk(UM_KERN_ERR "%s : PTRACE_OLDSETOPTIONS failed, errno = %d\n",
printk(UM_KERN_ERR "%s : PTRACE_SETOPTIONS failed, errno = %d\n",
__func__, errno);
goto out_kill;
}

View File

@ -112,102 +112,32 @@ static int start_ptraced_child(void)
return pid;
}
/* When testing for SYSEMU support, if it is one of the broken versions, we
* must just avoid using sysemu, not panic, but only if SYSEMU features are
* broken.
* So only for SYSEMU features we test mustpanic, while normal host features
* must work anyway!
*/
static int stop_ptraced_child(int pid, int exitcode, int mustexit)
static void stop_ptraced_child(int pid, int exitcode)
{
int status, n, ret = 0;
int status, n;
if (ptrace(PTRACE_CONT, pid, 0, 0) < 0)
fatal_perror("stop_ptraced_child : ptrace failed");
if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
perror("stop_ptraced_child : ptrace failed");
return -1;
}
CATCH_EINTR(n = waitpid(pid, &status, 0));
if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
int exit_with = WEXITSTATUS(status);
if (exit_with == 2)
non_fatal("check_ptrace : child exited with status 2. "
"\nDisabling SYSEMU support.\n");
non_fatal("check_ptrace : child exited with exitcode %d, while "
"expecting %d; status 0x%x\n", exit_with,
exitcode, status);
if (mustexit)
exit(1);
ret = -1;
fatal("stop_ptraced_child : child exited with exitcode %d, "
"while expecting %d; status 0x%x\n", exit_with,
exitcode, status);
}
return ret;
}
/* Changed only during early boot */
static int force_sysemu_disabled = 0;
static int __init nosysemu_cmd_param(char *str, int* add)
{
force_sysemu_disabled = 1;
return 0;
}
__uml_setup("nosysemu", nosysemu_cmd_param,
"nosysemu\n"
" Turns off syscall emulation patch for ptrace (SYSEMU).\n"
" SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
" behaviour of ptrace() and helps reduce host context switch rates.\n"
" To make it work, you need a kernel patch for your host, too.\n"
" See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
" information.\n\n");
static void __init check_sysemu(void)
{
unsigned long regs[MAX_REG_NR];
int pid, n, status, count=0;
os_info("Checking syscall emulation patch for ptrace...");
sysemu_supported = 0;
os_info("Checking syscall emulation for ptrace...");
pid = start_ptraced_child();
if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
goto fail;
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
if (n < 0)
fatal_perror("check_sysemu : wait failed");
if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
fatal("check_sysemu : expected SIGTRAP, got status = %d\n",
status);
if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
fatal_perror("check_sysemu : PTRACE_GETREGS failed");
if (PT_SYSCALL_NR(regs) != __NR_getpid) {
non_fatal("check_sysemu got system call number %d, "
"expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
goto fail;
}
n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
if (n < 0) {
non_fatal("check_sysemu : failed to modify system call "
"return");
goto fail;
}
if (stop_ptraced_child(pid, 0, 0) < 0)
goto fail_stopped;
sysemu_supported = 1;
os_info("OK\n");
set_using_sysemu(!force_sysemu_disabled);
os_info("Checking advanced syscall emulation patch for ptrace...");
pid = start_ptraced_child();
if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
if ((ptrace(PTRACE_SETOPTIONS, pid, 0,
(void *) PTRACE_O_TRACESYSGOOD) < 0))
fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed");
fatal_perror("check_sysemu: PTRACE_SETOPTIONS failed");
while (1) {
count++;
@ -240,20 +170,15 @@ static void __init check_sysemu(void)
goto fail;
}
}
if (stop_ptraced_child(pid, 0, 0) < 0)
goto fail_stopped;
stop_ptraced_child(pid, 0);
sysemu_supported = 2;
os_info("OK\n");
if (!force_sysemu_disabled)
set_using_sysemu(sysemu_supported);
return;
fail:
stop_ptraced_child(pid, 1, 0);
fail_stopped:
non_fatal("missing\n");
stop_ptraced_child(pid, 1);
fatal("missing\n");
}
static void __init check_ptrace(void)
@ -263,9 +188,9 @@ static void __init check_ptrace(void)
os_info("Checking that ptrace can change system call numbers...");
pid = start_ptraced_child();
if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
if ((ptrace(PTRACE_SETOPTIONS, pid, 0,
(void *) PTRACE_O_TRACESYSGOOD) < 0))
fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
fatal_perror("check_ptrace: PTRACE_SETOPTIONS failed");
while (1) {
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
@ -291,7 +216,7 @@ static void __init check_ptrace(void)
break;
}
}
stop_ptraced_child(pid, 0, 1);
stop_ptraced_child(pid, 0);
os_info("OK\n");
check_sysemu();
}
@ -370,7 +295,7 @@ void __init os_early_checks(void)
pid = start_ptraced_child();
if (init_pid_registers(pid))
fatal("Failed to initialize default registers");
stop_ptraced_child(pid, 1, 1);
stop_ptraced_child(pid, 1);
}
int __init parse_iomem(char *str, int *add)

View File

@ -173,23 +173,38 @@ __uml_setup("quiet", quiet_cmd_param,
"quiet\n"
" Turns off information messages during boot.\n\n");
/*
* The os_info/os_warn functions will be called by helper threads. These
* have a very limited stack size and using the libc formatting functions
* may overflow the stack.
* So pull in the kernel vscnprintf and use that instead with a fixed
* on-stack buffer.
*/
int vscnprintf(char *buf, size_t size, const char *fmt, va_list args);
void os_info(const char *fmt, ...)
{
char buf[256];
va_list list;
int len;
if (quiet_info)
return;
va_start(list, fmt);
vfprintf(stderr, fmt, list);
len = vscnprintf(buf, sizeof(buf), fmt, list);
fwrite(buf, len, 1, stderr);
va_end(list);
}
void os_warn(const char *fmt, ...)
{
char buf[256];
va_list list;
int len;
va_start(list, fmt);
vfprintf(stderr, fmt, list);
len = vscnprintf(buf, sizeof(buf), fmt, list);
fwrite(buf, len, 1, stderr);
va_end(list);
}

View File

@ -168,8 +168,8 @@ do { \
(pr_reg)[18] = (_regs)->regs.gp[18]; \
(pr_reg)[19] = (_regs)->regs.gp[19]; \
(pr_reg)[20] = (_regs)->regs.gp[20]; \
(pr_reg)[21] = current->thread.arch.fs; \
(pr_reg)[22] = 0; \
(pr_reg)[21] = (_regs)->regs.gp[21]; \
(pr_reg)[22] = (_regs)->regs.gp[22]; \
(pr_reg)[23] = 0; \
(pr_reg)[24] = 0; \
(pr_reg)[25] = 0; \

View File

@ -10,13 +10,11 @@
struct arch_thread {
unsigned long debugregs[8];
int debugregs_seq;
unsigned long fs;
struct faultinfo faultinfo;
};
#define INIT_ARCH_THREAD { .debugregs = { [ 0 ... 7 ] = 0 }, \
.debugregs_seq = 0, \
.fs = 0, \
.faultinfo = { 0, 0, 0 } }
#define STACKSLOTS_PER_LINE 4
@ -28,7 +26,6 @@ static inline void arch_flush_thread(struct arch_thread *thread)
static inline void arch_copy_thread(struct arch_thread *from,
struct arch_thread *to)
{
to->fs = from->fs;
}
#define current_sp() ({ void *sp; __asm__("movq %%rsp, %0" : "=r" (sp) : ); sp; })

View File

@ -6,7 +6,6 @@
obj-y = registers.o task_size.o mcontext.o
obj-$(CONFIG_X86_32) += tls.o
obj-$(CONFIG_64BIT) += prctl.o
USER_OBJS := $(obj-y)

View File

@ -1,12 +0,0 @@
/*
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com})
* Licensed under the GPL
*/
#include <sys/ptrace.h>
#include <asm/ptrace.h>
int os_arch_prctl(int pid, int option, unsigned long *arg2)
{
return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) arg2, option);
}

View File

@ -25,30 +25,6 @@ void arch_switch_to(struct task_struct *to)
printk(KERN_WARNING "arch_switch_tls failed, errno = EINVAL\n");
}
int is_syscall(unsigned long addr)
{
unsigned short instr;
int n;
n = copy_from_user(&instr, (void __user *) addr, sizeof(instr));
if (n) {
/* access_process_vm() grants access to vsyscall and stub,
* while copy_from_user doesn't. Maybe access_process_vm is
* slow, but that doesn't matter, since it will be called only
* in case of singlestepping, if copy_from_user failed.
*/
n = access_process_vm(current, addr, &instr, sizeof(instr),
FOLL_FORCE);
if (n != sizeof(instr)) {
printk(KERN_ERR "is_syscall : failed to read "
"instruction from 0x%lx\n", addr);
return 1;
}
}
/* int 0x80 or sysenter */
return (instr == 0x80cd) || (instr == 0x340f);
}
/* determines which flags the user has access to. */
/* 1 = access 0 = no access */
#define FLAG_MASK 0x00044dd5

View File

@ -188,32 +188,6 @@ int peek_user(struct task_struct *child, long addr, long data)
return put_user(tmp, (unsigned long *) data);
}
/* XXX Mostly copied from sys-i386 */
int is_syscall(unsigned long addr)
{
unsigned short instr;
int n;
n = copy_from_user(&instr, (void __user *) addr, sizeof(instr));
if (n) {
/*
* access_process_vm() grants access to vsyscall and stub,
* while copy_from_user doesn't. Maybe access_process_vm is
* slow, but that doesn't matter, since it will be called only
* in case of singlestepping, if copy_from_user failed.
*/
n = access_process_vm(current, addr, &instr, sizeof(instr),
FOLL_FORCE);
if (n != sizeof(instr)) {
printk("is_syscall : failed to read instruction from "
"0x%lx\n", addr);
return 1;
}
}
/* sysenter */
return instr == 0x050f;
}
static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
{
int err, n, cpu = ((struct thread_info *) child->stack)->cpu;

View File

@ -8,10 +8,6 @@
#define MAX_FP_NR HOST_FPX_SIZE
void set_using_sysemu(int value);
int get_using_sysemu(void);
extern int sysemu_supported;
#define UPT_SYSCALL_ARG1(r) UPT_BX(r)
#define UPT_SYSCALL_ARG2(r) UPT_CX(r)
#define UPT_SYSCALL_ARG3(r) UPT_DX(r)

View File

@ -15,14 +15,12 @@
#define FP_SIZE ((HOST_FPX_SIZE > HOST_FP_SIZE) ? HOST_FPX_SIZE : HOST_FP_SIZE)
#else
#define FP_SIZE HOST_FP_SIZE
#endif
/*
* x86_64 FC3 doesn't define this in /usr/include/linux/ptrace.h even though
* it's defined in the kernel's include/linux/ptrace.h. Additionally, use the
* 2.4 name and value for 2.4 host compatibility.
* glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum,
* ensure we have a definition by (re-)defining it here.
*/
#ifndef PTRACE_OLDSETOPTIONS
#define PTRACE_OLDSETOPTIONS 21
#endif
#ifndef PTRACE_SYSEMU_SINGLESTEP
#define PTRACE_SYSEMU_SINGLESTEP 32
#endif

View File

@ -12,72 +12,79 @@
#define STUB_MMAP_NR __NR_mmap2
#define MMAP_OFFSET(o) ((o) >> UM_KERN_PAGE_SHIFT)
static inline long stub_syscall0(long syscall)
static __always_inline long stub_syscall0(long syscall)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall));
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall)
: "memory");
return ret;
}
static inline long stub_syscall1(long syscall, long arg1)
static __always_inline long stub_syscall1(long syscall, long arg1)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1));
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1)
: "memory");
return ret;
}
static inline long stub_syscall2(long syscall, long arg1, long arg2)
static __always_inline long stub_syscall2(long syscall, long arg1, long arg2)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2));
"c" (arg2)
: "memory");
return ret;
}
static inline long stub_syscall3(long syscall, long arg1, long arg2, long arg3)
static __always_inline long stub_syscall3(long syscall, long arg1, long arg2,
long arg3)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2), "d" (arg3));
"c" (arg2), "d" (arg3)
: "memory");
return ret;
}
static inline long stub_syscall4(long syscall, long arg1, long arg2, long arg3,
long arg4)
static __always_inline long stub_syscall4(long syscall, long arg1, long arg2,
long arg3, long arg4)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2), "d" (arg3), "S" (arg4));
"c" (arg2), "d" (arg3), "S" (arg4)
: "memory");
return ret;
}
static inline long stub_syscall5(long syscall, long arg1, long arg2, long arg3,
long arg4, long arg5)
static __always_inline long stub_syscall5(long syscall, long arg1, long arg2,
long arg3, long arg4, long arg5)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2), "d" (arg3), "S" (arg4), "D" (arg5));
"c" (arg2), "d" (arg3), "S" (arg4), "D" (arg5)
: "memory");
return ret;
}
static inline void trap_myself(void)
static __always_inline void trap_myself(void)
{
__asm("int3");
}
static inline void remap_stack_and_trap(void)
static __always_inline void remap_stack_and_trap(void)
{
__asm__ volatile (
"movl %%esp,%%ebx ;"

View File

@ -16,7 +16,7 @@
#define __syscall_clobber "r11","rcx","memory"
#define __syscall "syscall"
static inline long stub_syscall0(long syscall)
static __always_inline long stub_syscall0(long syscall)
{
long ret;
@ -27,7 +27,7 @@ static inline long stub_syscall0(long syscall)
return ret;
}
static inline long stub_syscall2(long syscall, long arg1, long arg2)
static __always_inline long stub_syscall2(long syscall, long arg1, long arg2)
{
long ret;
@ -38,7 +38,8 @@ static inline long stub_syscall2(long syscall, long arg1, long arg2)
return ret;
}
static inline long stub_syscall3(long syscall, long arg1, long arg2, long arg3)
static __always_inline long stub_syscall3(long syscall, long arg1, long arg2,
long arg3)
{
long ret;
@ -50,7 +51,7 @@ static inline long stub_syscall3(long syscall, long arg1, long arg2, long arg3)
return ret;
}
static inline long stub_syscall4(long syscall, long arg1, long arg2, long arg3,
static __always_inline long stub_syscall4(long syscall, long arg1, long arg2, long arg3,
long arg4)
{
long ret;
@ -64,8 +65,8 @@ static inline long stub_syscall4(long syscall, long arg1, long arg2, long arg3,
return ret;
}
static inline long stub_syscall5(long syscall, long arg1, long arg2, long arg3,
long arg4, long arg5)
static __always_inline long stub_syscall5(long syscall, long arg1, long arg2,
long arg3, long arg4, long arg5)
{
long ret;
@ -78,12 +79,12 @@ static inline long stub_syscall5(long syscall, long arg1, long arg2, long arg3,
return ret;
}
static inline void trap_myself(void)
static __always_inline void trap_myself(void)
{
__asm("int3");
}
static inline void remap_stack_and_trap(void)
static __always_inline void remap_stack_and_trap(void)
{
__asm__ volatile (
"movq %0,%%rax ;"

View File

@ -16,60 +16,24 @@
long arch_prctl(struct task_struct *task, int option,
unsigned long __user *arg2)
{
unsigned long *ptr = arg2, tmp;
long ret;
int pid = task->mm->context.id.u.pid;
/*
* With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to
* be safe), we need to call arch_prctl on the host because
* setting %fs may result in something else happening (like a
* GDT or thread.fs being set instead). So, we let the host
* fiddle the registers and thread struct and restore the
* registers afterwards.
*
* So, the saved registers are stored to the process (this
* needed because a stub may have been the last thing to run),
* arch_prctl is run on the host, then the registers are read
* back.
*/
switch (option) {
case ARCH_SET_FS:
case ARCH_SET_GS:
ret = restore_pid_registers(pid, &current->thread.regs.regs);
if (ret)
return ret;
break;
case ARCH_GET_FS:
case ARCH_GET_GS:
/*
* With these two, we read to a local pointer and
* put_user it to the userspace pointer that we were
* given. If addr isn't valid (because it hasn't been
* faulted in or is just bogus), we want put_user to
* fault it in (or return -EFAULT) instead of having
* the host return -EFAULT.
*/
ptr = &tmp;
}
ret = os_arch_prctl(pid, option, ptr);
if (ret)
return ret;
long ret = -EINVAL;
switch (option) {
case ARCH_SET_FS:
current->thread.arch.fs = (unsigned long) ptr;
ret = save_registers(pid, &current->thread.regs.regs);
current->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)] =
(unsigned long) arg2;
ret = 0;
break;
case ARCH_SET_GS:
ret = save_registers(pid, &current->thread.regs.regs);
current->thread.regs.regs.gp[GS_BASE / sizeof(unsigned long)] =
(unsigned long) arg2;
ret = 0;
break;
case ARCH_GET_FS:
ret = put_user(tmp, arg2);
ret = put_user(current->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)], arg2);
break;
case ARCH_GET_GS:
ret = put_user(tmp, arg2);
ret = put_user(current->thread.regs.regs.gp[GS_BASE / sizeof(unsigned long)], arg2);
break;
}
@ -83,10 +47,10 @@ SYSCALL_DEFINE2(arch_prctl, int, option, unsigned long, arg2)
void arch_switch_to(struct task_struct *to)
{
if ((to->thread.arch.fs == 0) || (to->mm == NULL))
return;
arch_prctl(to, ARCH_SET_FS, (void __user *) to->thread.arch.fs);
/*
* Nothing needs to be done on x86_64.
* The FS_BASE/GS_BASE registers are saved in the ptrace register set.
*/
}
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,

View File

@ -12,7 +12,7 @@ int arch_set_tls(struct task_struct *t, unsigned long tls)
* If CLONE_SETTLS is set, we need to save the thread id
* so it can be set during context switches.
*/
t->thread.arch.fs = tls;
t->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)] = tls;
return 0;
}