Added a wrapper function for the invocation of debugger commands. It

sets a fault handler, so that an invalid memory access while executing
the command (address typos always do the trick :-) won't result in
another KDL session on top of the current one, which wouldn't even be
"cont"able.

All pieces of code setting a fault handler do now save and reset the
previous one, so that e.g. a user_memcpy() in a debugger command doesn't
disturb the mechanism.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20500 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2007-04-01 17:15:37 +00:00
parent 9bf1f552d9
commit 962b0b887d
5 changed files with 70 additions and 18 deletions

View File

@ -151,6 +151,7 @@ arch_cpu_user_memcpy(void *to, const void *from, size_t size,
{
char *tmp = (char *)to;
char *s = (char *)from;
addr_t oldFaultHandler = *faultHandler;
if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
goto error;
@ -158,11 +159,11 @@ arch_cpu_user_memcpy(void *to, const void *from, size_t size,
while (size--)
*tmp++ = *s++;
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return 0;
error:
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}
@ -181,6 +182,7 @@ ssize_t
arch_cpu_user_strlcpy(char *to, const char *from, size_t size, addr_t *faultHandler)
{
int from_length = 0;
addr_t oldFaultHandler = *faultHandler;
if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
goto error;
@ -197,11 +199,11 @@ arch_cpu_user_strlcpy(char *to, const char *from, size_t size, addr_t *faultHand
while (*from++ != '\0')
from_length++;
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return from_length;
error:
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}
@ -210,6 +212,7 @@ status_t
arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
{
char *xs = (char *)s;
addr_t oldFaultHandler = *faultHandler;
if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
goto error;
@ -217,11 +220,11 @@ arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
while (count--)
*xs++ = c;
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return 0;
error:
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}

View File

@ -65,6 +65,7 @@ static status_t
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
{
struct thread *thread = thread_get_current_thread();
addr_t oldFaultHandler = thread->fault_handler;
// set fault handler, so that we can safely access user stacks
if (thread) {
@ -76,11 +77,11 @@ get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
*next = (addr_t)((struct stack_frame *)framePointer)->previous;
if (thread)
thread->fault_handler = NULL;
thread->fault_handler = oldFaultHandler;
return B_OK;
error:
thread->fault_handler = NULL;
thread->fault_handler = oldFaultHandler;
return B_BAD_ADDRESS;
}
@ -287,4 +288,3 @@ arch_debug_init(kernel_args *args)
return B_NO_ERROR;
}

View File

@ -586,6 +586,7 @@ arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandl
{
char *tmp = (char *)to;
char *s = (char *)from;
addr_t oldFaultHandler = *faultHandler;
// this check is to trick the gcc4 compiler and have it keep the error label
if (to == NULL)
@ -596,11 +597,11 @@ arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandl
while (size--)
*tmp++ = *s++;
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return 0;
error:
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}
@ -609,6 +610,7 @@ ssize_t
arch_cpu_user_strlcpy(char *to, const char *from, size_t size, addr_t *faultHandler)
{
int fromLength = 0;
addr_t oldFaultHandler = *faultHandler;
// this check is to trick the gcc4 compiler and have it keep the error label
if (to == NULL)
@ -629,11 +631,11 @@ arch_cpu_user_strlcpy(char *to, const char *from, size_t size, addr_t *faultHand
fromLength++;
}
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return fromLength;
error:
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}
@ -642,6 +644,7 @@ status_t
arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
{
char *xs = (char *)s;
addr_t oldFaultHandler = *faultHandler;
// this check is to trick the gcc4 compiler and have it keep the error label
if (s == NULL)
@ -652,11 +655,11 @@ arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
while (count--)
*xs++ = c;
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return 0;
error:
*faultHandler = 0;
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}

View File

@ -56,6 +56,7 @@ static status_t
get_next_frame(addr_t ebp, addr_t *_next, addr_t *_eip)
{
// set fault handler, so that we can safely access user stacks
addr_t oldFaultHandler = thread_get_current_thread()->fault_handler;
thread_get_current_thread()->fault_handler = (addr_t)&&error;
// Fake goto to trick the compiler not to optimize the code at the label
// away.
@ -65,11 +66,11 @@ get_next_frame(addr_t ebp, addr_t *_next, addr_t *_eip)
*_eip = ((struct stack_frame *)ebp)->return_address;
*_next = (addr_t)((struct stack_frame *)ebp)->previous;
thread_get_current_thread()->fault_handler = NULL;
thread_get_current_thread()->fault_handler = oldFaultHandler;
return B_OK;
error:
thread_get_current_thread()->fault_handler = NULL;
thread_get_current_thread()->fault_handler = oldFaultHandler;
return B_BAD_ADDRESS;
}

View File

@ -16,6 +16,7 @@
#include <frame_buffer_console.h>
#include <int.h>
#include <smp.h>
#include <thread.h>
#include <vm.h>
#include <arch/debug_console.h>
@ -25,6 +26,7 @@
#include <syslog_daemon.h>
#include <ctype.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -59,6 +61,9 @@ static bool sSyslogDropped = false;
static struct debugger_command *sCommands;
static jmp_buf sInvokeCommandEnv;
static bool sInvokeCommandDirectly = false;
#define SYSLOG_BUFFER_SIZE 65536
#define OUTPUT_BUFFER_SIZE 1024
static char sOutputBuffer[OUTPUT_BUFFER_SIZE];
@ -316,6 +321,46 @@ parse_line(const char *buffer, char **argv, int *_argc, int32 maxArgs)
return *_argc = index;
}
/*! This function is a safe gate through which debugger commands are invoked.
It sets a fault handler before invoking the command, so that an invalid
memory access will not result in another KDL session on top of this one
(and "cont" not to work anymore). We use setjmp() + longjmp() to "unwind"
the stack after catching a fault.
*/
static int
invoke_command(int (*command)(int, char **), int argc, char** argv)
{
struct thread* thread = thread_get_current_thread();
addr_t oldFaultHandler = thread->fault_handler;
// Invoking the command directly might be useful when debugging debugger
// commands.
if (sInvokeCommandDirectly)
return command(argc, argv);
if (setjmp(sInvokeCommandEnv) == 0) {
int result;
thread->fault_handler = (addr_t)&&error;
// Fake goto to trick the compiler not to optimize the code at the label
// away.
if (!thread)
goto error;
result = command(argc, argv);
thread->fault_handler = oldFaultHandler;
return result;
error:
longjmp(sInvokeCommandEnv, 1);
// jump into the else branch
} else {
kprintf("\n[*** READ/WRITE FAULT ***]\n");
}
thread->fault_handler = oldFaultHandler;
return 0;
}
static void
kernel_debugger_loop(void)
@ -347,7 +392,7 @@ kernel_debugger_loop(void)
if (cmd == NULL)
kprintf("unknown command, enter \"help\" to get a list of all supported commands\n");
else {
int rc = cmd->func(argc, args);
int rc = invoke_command(cmd->func, argc, args);
if (rc == B_KDEBUG_QUIT)
break; // okay, exit now.