linux/arch
Paolo Bonzini 172b2386ed KVM: x86: fix missed hardware breakpoints
Sometimes when setting a breakpoint a process doesn't stop on it.
This is because the debug registers are not loaded correctly on
VCPU load.

The following simple reproducer from Oleg Nesterov tries using debug
registers in two threads.  To see the bug, run a 2-VCPU guest with
"taskset -c 0" and run "./bp 0 1" inside the guest.

    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/wait.h>
    #include <sys/ptrace.h>
    #include <sys/user.h>
    #include <asm/debugreg.h>
    #include <assert.h>

    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

    unsigned long encode_dr7(int drnum, int enable, unsigned int type, unsigned int len)
    {
        unsigned long dr7;

        dr7 = ((len | type) & 0xf)
            << (DR_CONTROL_SHIFT + drnum * DR_CONTROL_SIZE);
        if (enable)
            dr7 |= (DR_GLOBAL_ENABLE << (drnum * DR_ENABLE_SIZE));

        return dr7;
    }

    int write_dr(int pid, int dr, unsigned long val)
    {
        return ptrace(PTRACE_POKEUSER, pid,
                offsetof (struct user, u_debugreg[dr]),
                val);
    }

    void set_bp(pid_t pid, void *addr)
    {
        unsigned long dr7;
        assert(write_dr(pid, 0, (long)addr) == 0);
        dr7 = encode_dr7(0, 1, DR_RW_EXECUTE, DR_LEN_1);
        assert(write_dr(pid, 7, dr7) == 0);
    }

    void *get_rip(int pid)
    {
        return (void*)ptrace(PTRACE_PEEKUSER, pid,
                offsetof(struct user, regs.rip), 0);
    }

    void test(int nr)
    {
        void *bp_addr = &&label + nr, *bp_hit;
        int pid;

        printf("test bp %d\n", nr);
        assert(nr < 16); // see 16 asm nops below

        pid = fork();
        if (!pid) {
            assert(ptrace(PTRACE_TRACEME, 0,0,0) == 0);
            kill(getpid(), SIGSTOP);
            for (;;) {
                label: asm (
                    "nop; nop; nop; nop;"
                    "nop; nop; nop; nop;"
                    "nop; nop; nop; nop;"
                    "nop; nop; nop; nop;"
                );
            }
        }

        assert(pid == wait(NULL));
        set_bp(pid, bp_addr);

        for (;;) {
            assert(ptrace(PTRACE_CONT, pid, 0, 0) == 0);
            assert(pid == wait(NULL));

            bp_hit = get_rip(pid);
            if (bp_hit != bp_addr)
                fprintf(stderr, "ERR!! hit wrong bp %ld != %d\n",
                    bp_hit - &&label, nr);
        }
    }

    int main(int argc, const char *argv[])
    {
        while (--argc) {
            int nr = atoi(*++argv);
            if (!fork())
                test(nr);
        }

        while (wait(NULL) > 0)
            ;
        return 0;
    }

Cc: stable@vger.kernel.org
Suggested-by: Nadav Amit <namit@cs.technion.ac.il>
Reported-by: Andrey Wagin <avagin@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-02-24 14:47:39 +01:00
..
alpha dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
arc ARC fixes for 4.5 2016-02-13 08:18:21 -08:00
arm Merge branch 'fixes' of git://ftp.arm.linux.org.uk/~rmk/linux-arm 2016-02-14 10:46:47 -08:00
arm64 KVM/ARM fixes: 2016-02-16 10:50:46 -08:00
avr32 dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
blackfin dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
c6x dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
cris Merge branch 'akpm' (patches from Andrew) 2016-01-21 12:32:08 -08:00
frv dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
h8300 Merge branch 'akpm' (patches from Andrew) 2016-01-21 12:32:08 -08:00
hexagon dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
ia64 [IA64] Enable copy_file_range syscall for ia64 2016-01-22 14:20:01 -08:00
m32r m32r: fix build failure due to SMP and MMU 2016-02-05 18:10:40 -08:00
m68k m68k/defconfig: Update defconfigs for v4.5-rc1 2016-02-01 10:35:38 +01:00
metag dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
microblaze dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
mips Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus 2016-02-14 10:49:01 -08:00
mn10300 dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
nios2 dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
openrisc dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
parisc dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
powerpc powerpc fixes for 4.5 2016-01-29 16:10:16 -08:00
s390 Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching 2016-02-18 16:34:15 -08:00
score
sh sh: fix smp_store_mb for !SMP 2016-01-26 10:18:29 +02:00
sparc dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
tile dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
um um: asm/page.h: remove the pte_high member from struct pte_t 2016-02-05 18:10:40 -08:00
unicore32 dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00
x86 KVM: x86: fix missed hardware breakpoints 2016-02-24 14:47:39 +01:00
xtensa dma-mapping: remove <asm-generic/dma-coherent.h> 2016-01-20 17:09:18 -08:00
.gitignore
Kconfig dma-mapping: always provide the dma_map_ops based implementation 2016-01-20 17:09:18 -08:00