Re: memmove crash



On Tue, 20 Sep 2005 01:22:04 -0400, T.M. Sommers <tms@xxxxxx> wrote:

What you can do is make sure that memmove(), and all other standard library functions, do not get invalid arguments, thus invoking undefined behavior. It is not good practice to rely on signal handlers to catch your mistakes. Much better not to make the mistakes in the first place.

The problem with that is how do you tell valid arguments from invalid arguments? It's easy to say that a length of 0xFFFF0000 is invalid, but certainly the piece of memory which is so important is much smaller than that (I suspect it's either immediately before or immediately after the mapped memory), and so it could just as easily be overwritten with arguments that appear to be just fine.


Signal handlers were not meant to be debugging tools, really.

I can't imagine what else the SIGSEGV and SIGILL signals could be used for. Surely they were intended to be used for the only thing that they're good for.


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.

Exactly. There is not much that can safely be done in a signal handler.

Now the interesting thing is that from assembly I can do whatever I want to inside a signal handler. I have to have some checks to take into consideration what the rest of my code is doing, for instance if it was reprogramming the VGA registers when the signal occured, then I can't reprogram them from within the signal handler as well, as that would mess things up when it returns. On the other hand, if it was a SIGSEGV, then I'm not going to return, and so then I can do that as well as anything else I want to do.


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.

Assuming you will need to call some graphics library to clean up the screen, the problem will still be there. Those library functions will likely be unsafe.

Nope, no graphics library, I'm stuck doing all of that myself.

No, a pointer by any other name is still a pointer. You can interpret the bits any way you want, but that does not change their nature.

If signal is successful, then it returns a pointer, if it's unsuccessful, then it returns an integer. I'm not interested in that pointer, I'm only interested in the integer which indicates an error, and like I said, if I had used sighandler_t, which is a pointer, then something like "if (result < 0)" would always be false, because pointers are unsigned.


Now if I was also interested in the pointer, then the right thing to do would be to make result a signhandler_t, and then cast it to an integer to check it against zero, but since I'm not, I might as well just make it an integer to begin with.

If you include stdlib.h, gcc gives no warning without a cast. What you should learn it to always include stdlib.h when you use malloc().

Not another one! I've already got so many...

#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <ucontext.h>
#include <unistd.h>
//#include <asm/siginfo.h> /* I fucking hate C */
#include <asm/unistd.h>
#include <linux/fb.h>
#include <sys/fsuid.h>
#include <sys/io.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vt.h>

Of course, it looks like stdlib.h is in there already for something else, but regardless, 20% of the time I spend programming is tracking down the correct files to include, and even more when I come across things like siginfo_t which I don't seem to get with any of the includes mentioned in the man page, and for some reason the only file I can find it in creates a shitload of duplicate definition errors if I include it. I eventually just said to hell with it and cast it to an integer array pointer and just referenced si_code as siginfo[2].

What's so bad about that anyway? It actually seems like the right thing to do to me.

Failing to include stdlib.h makes a call to malloc default to return an int. Pointers and ints are not the same size on all systems.

Seems like a pretty stupid default then.

Of course, nothing else in C makes sense, so why should that?

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.

But it won't crash where you expect it to, so you might interpret the results incorrectly.

No, I'm smarter than that.

It is simply good practice to check the return of malloc() in a real program.

I do.

If you care about portability,

I think the direct system call pretty much killed any chance of portability.

As I mentioned, your code did not even work on my Linux system, which has an older version of gcc.

Assuming we're still talking about this:

If you care about portability, you should probably
not use the C99 feature that allows intermixing of
data definitions and code, as that is not yetimplemented everywhere.

....then I'm not sure what you're talking about. I recompiled with "-ansi" and I still didn't get any errors.


Now if something that compiles with "-ansi" just fine on my Linux system doesn't compile on yours, then I think I definately want to go back to assembly language.

When I use "-ansi" I don't get to do inline assembly anymore, that sucks.

The signal handler can't tell the difference between a 'real' signal and one raised by raise() (barring the use of something like siginfo()). Additionally, your crash line above did not cause a SIGSEGV on one of the systems I tried (I forget which).

DOS maybe? I can't imagine what wouldn't have memory protection set up to create a segmentation fault if you try to dereference the NULL pointer.


On the other hand, it is possible in Linux to map memory to address 0, and NULL in Linux is 0 instead of something more sensable like -1, so I guess it could happen there as well.

Anyway, I wouldn't put something like that in a real program anyway, as I only want my test programs to crash.

All behaviour is defined in assembly language. Sometimes it's defined to something retarded, but it's at least defined.

memmove() is not assembly, it is C. The term 'undefined behavior' has a specific meaning, and is defined in the C and POSIX standards.

I know, I'm just saying that in assembly language I don't have to deal with things like "undefined behavior" as everything just does what you expect.


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.

No signals were raised, at least according to truss. It just seemed to get stuck in a loop.

Strange... Anyway, I tested it on my computer using the libc signal function, which sets up the signals so that they automatically restart. That made it so that it gets stuck in a loop of segmentation faults (which show up with strace) and after several thousands of them have occured the program terminates with "segmentation fault".


(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.)

In the real world, with real programs, it is much safer not to have to reregister the handler, because a signal can come at any time, even between the automatic reset to SIG_DFL and the reregistering.

In that case, the signal handler has crashed, and another SIGSEGV isn't going to do any good.


However, those automatically re-registering signals are nice. I just discovered sigaction (the direct linux system call, not the libc function) the other day, which is much nicer than signal (the direct linux system call, not the libc function) in that it lets you do the automatic re-registration of handlers, whereas signal does not, and it lets you mask other signals while in the handler. So all of the crap I've been dealing with wrt linux sending console switching signals that interrupt other console switching signals I never would have had to deal with in the first place if only I had discovered sigaction before I discovered signal. The thought never occured to me that there might be two system calls to set signal handlers, but in fact there seems to be lots of signal related system calls, like sigpending, sigsuspend and sigprocmask, as well as the seemingly undocumented sgetmask and ssetmask.

This is a potential problem. It is not safe to call printf() in a signal handler. write() is okay, though.

Eew. C is yucky.

No, signals and undefined behavior are yucky.

No, C and it's undefined behavior are yucky. I don't understand how you can start with a system where variables are allocated on the stack and end up with a system where many of the functions aren't reentrant.


Just think about what has to happen to make a function so that it can't be called from a signal handler. It pretty much has to access a static variable somewhere. Why is printf accessing a static variable?

Now a lot of my assembly language functions use static variables, but that's because that's what you do in assembly. In C the thing to do is to declare your variables inside of functions, so that they get allocated on the stack when the function is called, and so you can call it recursively and each instance gets it's own variables.

So when it's so easy to make functions reentrant in C, why isn't the library written that way?


I would really like to use C, it's so much easier to just type "int x" than to do the equivelant in assembly language, but between these worthless library functions and code which one ANSI C compiler accepts but another ANSI C compiler rejects, C seems to be even less portable than assembly language. At least in assembly I can write a program and have it work on other people's x86 Linux systems, but if I try to do the same in C it won't work because their compiler will find a problem with the code that my compiler said was just fine, and vice versa.


I guess now I understand why things are the way they are. I can download assembly programs that other people have written for Linux and they assemble and work just fine, but when I download other people's C code, 95% of the time it won't compile without warnings, and 50% of the time it won't compile at all. I know most of them are using x86 Linux systems just like me, so I don't understand what the problem is, except that C seems to be the least portable language ever created.
.