• Jason Wessel's avatar
    i386: fix regression, endless loop in ptrace singlestep over an int80 · 1e2e99f0
    Jason Wessel authored
    The commit 635cf99a introduced a
    regression.  Executing a ptrace single step after certain int80
    accesses will infinitely loop and never advance the PC.
    
    The TIF_SINGLESTEP check should be done on the return from the syscall
    and not before it.
    
    I loops on each single step on the pop right after the int80 which writes out
    to the console.  At that point you can issue as many single steps as you want
    and it will not advance any further.
    
    The test case is below:
    
    /* Test whether singlestep through an int80 syscall works.
     */
    #define _GNU_SOURCE
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/ptrace.h>
    #include <sys/wait.h>
    #include <sys/mman.h>
    #include <asm/user.h>
    #include <string.h>
    
    static int child, status;
    static struct user_regs_struct regs;
    
    static void do_child()
    {
    	char str[80] = "child: int80 test\n";
    
    	ptrace(PTRACE_TRACEME, 0, 0, 0);
    	kill(getpid(), SIGUSR1);
    	write(fileno(stdout),str,strlen(str));
    	asm ("int $0x80" : : "a" (20)); /* getpid */
    }
    
    static void do_parent()
    {
    	unsigned long eip, expected = 0;
    again:
    	waitpid(child, &status, 0);
    	if (WIFEXITED(status) || WIFSIGNALED(status))
    		return;
    
    	if (WIFSTOPPED(status)) {
    		ptrace(PTRACE_GETREGS, child, 0, &regs);
    		eip = regs.eip;
    		if (expected)
    			fprintf(stderr, "child stop @ %08lx, expected %08lx %s\n",
    					eip, expected,
    					eip == expected ? "" : " <== ERROR");
    
    		if (*(unsigned short *)eip == 0x80cd) {
    			fprintf(stderr, "int 0x80 at %08x\n", (unsigned int)eip);
    			expected = eip + 2;
    		} else
    			expected = 0;
    
    		ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);
    	}
    	goto again;
    }
    
    int main(int argc, char * const argv[])
    {
    	child = fork();
    	if (child)
    		do_parent();
    	else
    		do_child();
    	return 0;
    }
    Signed-off-by: default avatarJason Wessel <jason.wessel@windriver.com>
    Cc: Jeremy Fitzhardinge <jeremy@goop.org>
    Cc: <stable@kernel.org>
    Cc: Chuck Ebbert <76306.1226@compuserve.com>
    Acked-by: default avatarAndi Kleen <ak@suse.de>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    1e2e99f0
entry.S 23.6 KB