Re: memmove crash
- From: "Richard Cooper" <spamandviruses@xxxxxxxxxxxxxxxxx>
- Date: Mon, 19 Sep 2005 17:22:00 GMT
On Mon, 19 Sep 2005 11:58:13 -0400, T.M. Sommers <tms@xxxxxx> wrote:
Why not reduce the code to the bare minimum necessary to show the problem, and post it?That was the first thing I tried to do, but my test program didn't reproduce the problem.
Which usually means that the problem is in the code that was removed.
Except that I was certain from my testing that it had nothing to do with the removed code. And it obviously didn't, since working with the test program some more I figured out how to get it to do the same.
Anyway, a simple strace reveals the part of the problem:
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Two consecuative segmentation faults without a re-registration of the signal handler.
Which is what I suggested. Some Linux kernels and libcs follow the old signal behavior, in which the handler is reset to SIG_DLF when a signal is caught; other versions follow the BSD model, and don't. So the first step is to determine which behavior your system follows.
I know what it does, it doesn't re-register, but even if it did that wouldn't fix the problem, as memmove is doing something that's causing the signal handler to crash. It's obviously not overwriting the code because that isn't possible, so then question is what is it doing?
Now this in all likelyhood isn't memmove's fault. As best I can guess, C is just so dependant on some particular piece data in memory somewhere that it crashes if it gets overwritten. Now I don't have this problem in assembly, because code segments can't be written to, and I can just make sure my code doesn't depend on anything in the case of a segmentation fault, which is easy to do.
As you can see, my signal handler in this case doesn't depend on anything either, all it does is try to print a string of text. It's C which is dependant on whatever is being overwritten, not my code, and so there's nothing I can do to keep this from happening besides write the program in assembly instead of C.
I would guess that what's happening is that memmove is overwriting some tables that tell malloc what/how to allocate memory, and then the fprintf function allocates a small chunk of memory for a buffer, gets a bad pointer returned from malloc, and proceeds to use it, causing another segmentation fault.
Now I could remove the fprintf call, but in my real program there's all kinds of stuff that has to be done when that signal handler is called, so I'd have to go through it all and make sure none of it calls any libc functions that are going to do a malloc, and it's not like it's easy to tell which ones do and which ones don't.
So I guess the thing to do would be to either just live with the problem, or to not use any of the standard library and write my own functions, that way I at least know what they're dependant on.
int result; asm volatile ( "int $0x80" :"=a" (result) :"a" (__NR_signal), "b" (11), "c" (handler) ); printf("Result of kernel call: %d\n", result);
signal() does not return an int; it returns a sighandler_t in Linux.
The values that come out of assembly are whatever I say they are. If I want to say that it's a int, then it's an int, if I want to say that it's a float, then it's a float. The number in EAX is just some bits, and those bits can be whatever I want to say that they are. I'm never going to use it as a pointer, all that matters is that it's not less than zero. So since pointers are unsigned, I made it an signed integer, because if it was a pointer then it would never be less than zero.
/* Allocate some memory... */ char *memory1; memory1 = (char*) malloc(4096); char *memory2; memory2 = (char*) malloc(4096);
Don't cast the value returned by malloc(),
Malloc returns a void pointer, which isn't a character pointer, so if I don't do a cast then I get bitched at by GCC. Now I could make memory1 and memory2 void pointers, but then I get *** like "void value not ignored as it ought to be." So I've learned that void pointers can't actually be used for anything without GCC having a fit, and so I've learned to cast all pointers to the data type that I'm going to use them for.
What's so bad about that anyway? It actually seems like the right thing to do to me.
And, of course, you should test the return value from malloc().
I thought that was covered by printing them to the screen. A line that reads:
Malloc's pointer: -1
Is every bit as good as any other error message in my opinion, especially considering the program was designed to crash anyway and will do that just fine even if malloc fails.
If you care about portability,
I think the direct system call pretty much killed any chance of portability.
/* Do a perfectly safe memmove(). */ memmove(memory1, memory2, 4096); /* Cause a segmentation fault. */ volatile int *crash = NULL; *crash++;
Why not use raise()?
I didn't know about it. I just learned C a week or two ago.
Even so, an honest segmentation fault does seem like the best way to test a segmentation fault handler.
/* Cause a different segmentation fault. */ memmove(memory2, memory1, -4096);
This, I think, is the real problem. My understanding is that this invokes undefined behavior. I see nothing in the C or POSIX standards that requires a SIGSEGV to be raised in the case of undefined behavior. The system can do anything at all.
All behaviour is defined in assembly language. Sometimes it's defined to something retarded, but it's at least defined.
FreeBSD seems to get into an infinite loop,or at least a loop that goes on beyond my patience.
Yes, you'll have the signal handlers that you don't have to re-register in FreeBSD, and so since your signal handler is crashing, it'll be stuck in a loop. (Thus I prefer the handlers that have to be re-registered for things like SIGSEGV, that way if the signal handler crashes, at least the program terminates.)
Solaris does raise a signal.
Well, at least something works right.
/* Keep GCC from complaining... */ return 0;
You put in a return not to keep gcc happy, but because main is supposed to return an int, so you should return one.
I understand why the return belongs there, the comment exists only because it's a program designed to crash and so that return value is never going to be used. So in effect, the line is there for no reason other than to make GCC not give me a warning when I compile the program.
void handler(int signum) { printf("Recieved Signal #%d\n", signum);
This is a potential problem. It is not safe to call printf() in a signal handler. write() is okay, though.
Eew. C is yucky.
But the solution to your problem is easy: use atexit().
atexit - register a function to be called at normal program termination
Somehow I don't think that's going to help me with abnormal program termination.
It seems pretty clear that the signal handlers are functioning exactly as they're supposed to. It's just that some libc function is crashing as a result of memmove overwriting some data that it depends on.
.
- Follow-Ups:
- Re: memmove crash
- From: T.M. Sommers
- Re: memmove crash
- References:
- Windows Assembly
- From: Richard Cooper
- Re: Windows Assembly
- From: JGCASEY
- Re: Windows Assembly
- From: Richard Cooper
- Re: Windows Assembly
- From: T.M. Sommers
- memmove crash
- From: Richard Cooper
- Re: memmove crash
- From: T.M. Sommers
- Windows Assembly
- Prev by Date: Re: Maybe we should stop "Paging Beth Stone" already...
- Next by Date: Sorry, another newbie!
- Previous by thread: Re: memmove crash
- Next by thread: Re: memmove crash
- Index(es):