How can I trap memory exceptions in a speedy manner?

I am currently using sigaction to trap bad memory access errors but it is really slow. What I do is call sigaction at the start of an assembly language routine to enable my signal handler and then in my signal handler I use the address of the bad memory access to determine which error message I want to return to the user. All my assembly language routines use the exact same type of exit and return values to make this possible. It is really slow. Something like 100 times slower. What would be better is if I could set up my handler once and then not have to worry about other things changing my handler, like when I call 3rd party functions in shared libraries or call operating system functions. Is there a better way?

Accepted Reply

I think what you are telling me is there is no standard way other than setting signals to trap a memory access error.

No, that’s not what I said. On Apple platforms there are two standard ways of handling memory access exceptions, UNIX signals and Mach thread/task/host exception handling. As to which of those is faster, I’ve never profiled them.

I’d like to discuss the big picture because, IMO, trapping memory access exceptions is almost always the wrong approach. There are numerous other approaches you can take and I think it would behoove you to explore those before going down this path. For example:

As for why I need to do this, in addition to just being good programming practice, I mean why not have your block move subroutine return an error code instead of just crashing?

IMO that’s terribly programming practice. What is the caller supposed to do with that error? If the program is in such a state that they’re passing bogus values to memcpy, the best thing to do is stop immediately. Any else is just asking for security exploits.

But the main reason is, my application is a Forth based script engine and the user can put in any addresses they want

I would solve this by using a suballocator for Forth memory, at which point you can reject bad addresses at the UI level, before they enter your runtime.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Can you put any constraints on the thread that runs this code? That is, is it always the same thread that runs the code? Or can arbitrary threads run it?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Right now it is used in a single threaded program. But the program comes as a shared library and is reentrant so it is possible multiple programs could be using the same shared library.


Solving this problem is going to be a challenge. I’d like to start with your motivation. Why do you need to trap these accesses? Rather than bounce back and forward here, I’d like to discuss your requirements interactively. So, my recommendation here is that you open a DTS tech support incident and we can take things from there.

Please reference this forums thread so your incident gets routed to me.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
I think what you are telling me is there is no standard way other than setting signals to trap a memory access error. What I am doing, and it works, is using a signal handler to have a memory access error in my low level assembler memory access functions act like a regular error in the return code method of error reporting. What this means is, the handler traps the memory access error and forces the subroutine to exit with a return code indicating a memory access error occurred. What would be nice is if I could give the operating system a list of addresses where a bad memory access could occur, and a list of corresponding addresses of where I want execution to continue at link time. The problem is setting the signal handler and removing it before and after the memory accesses is slow. As for why I need to do this, in addition to just being good programming practice, I mean why not have your block move subroutine return an error code instead of just crashing? But the main reason is, my application is a Forth based script engine and the user can put in any addresses they want and it's very frustrating for me to try to debug my programs when they crash and I don't know why. An error message like, 'bad memory access at address 0x12823' along with a trace of all the nested functions that were in play is much better, which is what I have now.

In short:
What I am doing is I am making bad memory exceptions fit into the return code model of error handling using a signal handler.

But, it's slow. Setting and unsetting the handler around each memory access address is time consuming.

The option of setting the handler once during the initialization will not work because calling operating system functions and third party libraries can override my setting of the handler.

But I think you've answered my question. There is no standard fast and easy way in Mac Os to trap memory exceptions in the manner I'm describing.

I think what you are telling me is there is no standard way other than setting signals to trap a memory access error.

No, that’s not what I said. On Apple platforms there are two standard ways of handling memory access exceptions, UNIX signals and Mach thread/task/host exception handling. As to which of those is faster, I’ve never profiled them.

I’d like to discuss the big picture because, IMO, trapping memory access exceptions is almost always the wrong approach. There are numerous other approaches you can take and I think it would behoove you to explore those before going down this path. For example:

As for why I need to do this, in addition to just being good programming practice, I mean why not have your block move subroutine return an error code instead of just crashing?

IMO that’s terribly programming practice. What is the caller supposed to do with that error? If the program is in such a state that they’re passing bogus values to memcpy, the best thing to do is stop immediately. Any else is just asking for security exploits.

But the main reason is, my application is a Forth based script engine and the user can put in any addresses they want

I would solve this by using a suballocator for Forth memory, at which point you can reject bad addresses at the UI level, before they enter your runtime.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

No, that’s not what I said. On Apple platforms there are two standard ways of handling memory access exceptions, UNIX signals and Mach thread/task/host exception handling. As to which of those is faster, I’ve never profiled them.

I didn't know about the Mach thread/task/host exception handling. It looks like it will work for what I want to do. Thanks!

IMO that’s terribly programming practice. What is the caller supposed to do with that error? If the program is in such a state that they’re passing bogus values to memcpy, the best thing to do is stop immediately. Any else is just asking for security exploits.

It's not for the end user, it's for the programmer. This is to support the Forth playground style of debugging which allows programmers to make mistakes. For me I've found that staying in the playground is preferable to having my script engine exit.

IMO that’s terribly programming practice. What is the caller supposed to do with that error? If the program is in such a state that they’re passing bogus values to memcpy, the best thing to do is stop immediately. Any else is just asking for security exploits.

It's for the programmer. To help with debugging.
I've found it really convenient. For example, let's say I forgot to put the script engine into the correct number base, or even more likely, I forgot to drop a return value off of the forth stack after calling a function. What happens is I get a bad memory error at some weird address, along with exactly which function I was in. Then I look at the stack and see the wrong numbers there... and go oh I forgot to do a DROP. If the program just crashes, it takes a lot longer to figure out and fix what was going on.

I would solve this by using a suballocator for Forth memory, at which point you can reject bad addresses at the UI level, before they enter your runtime.

I'm not sure I'm understanding your suggestion here. In my Forth the programmer can do something like:

Code Block
VARIABLE moo
0 0 moo D!


The mistake here is, moo is a 64 bit variable and D! is a 128 bit operation. The programmer has just gone off the end of the buffer. Depending on how close they are to the end of the memory allocated by the operating system, it is possible to get a bad memory exception. I think it would be very complicated and expensive execution time wise to scan the address passed to D! against all the buffers allocated in my system.

On the other hand, I did include a managed buffer system which uses buffer ids and offsets. All operations that use that system do a lot of checks to make sure no bad offsets or buffer ids get used, so bad memory errors are not possible with them.

In any case, I will mark this as solved. The thing I found online about the Mach thread/task/host exception handling looks promising but was from 2013. They said there was an issue with documentation not being available for IOS... hopefully it's been resolved since then.















To help with debugging.

The key question here is whether you let your Forth engine interact directly with OS-level objects. If you do then the only safe option here is to run the developer’s code in a separate process. Remember that memory access exceptions aren’t the only potential outcome for rogue pointer accesses. If the developer’s code accidentally smashes the the memory being used by your runtime, things are going to end badly.

The annoying downside to this approach is that it requires a separate process, which isn’t allowed on iOS )-:

The alternative is to isolate your Forth code from OS objects. So something like:
  • Have the runtime access Forth memory directly, but allocate that memory in a way that let’s you check those accesses efficiently.

  • Have the runtime jump through some sort of checked trampoline in order to access OS objects.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"


The key question here is whether you let your Forth engine interact directly with OS-level objects.

Yes. That is kind of the whole point.


If you do then the only safe option here is to run the developer’s code in a separate process. Remember that memory access exceptions aren’t the only potential outcome for rogue pointer accesses. If the developer’s code accidentally smashes the the memory being used by your runtime, things are going to end badly.

Yes that can happen. I believe the shared library's code is read only after it is loaded. The engine's data memory can get corrupted. The low level buffer access routines in my engine's system checks to see if its management system has been corrupted and returns errors if it happens. In the time I've been working on my engine, which is more than 20 years, it's only happened a couple times. The ones that I remember were due to buffers overruns on a block moves that didn't generate bad memory exceptions and ended up going a little bit into the next buffer.

I don't think running the engine in a separate process is going to work. Forth is an interactive extensible stack based programming language with little to no type checking. Development is done by extending the engine and requires access to the engine's data. Again, if the developer uses my managed buffer system through the access routines, bad memory accesses are impossible. But, the Forth Standard requires allowing direct access. And direct access is faster. Forth is pretty simple and there aren't that many system variables, and I could put those variables in a separate buffer... but... Forth is backwards compatible with 8 and 16 bit systems where there aren't multiple buffers, and expects the engine to start up in a certain way which has everything in one buffer. And adding new variables goes to the same buffer as the existing variables by default... Actually they are coming out with a new standard... maybe that's changed.... be nice if it did. But this would mean the only way to access the engine's data would be through some sort of remote procedure call? Wouldn't that be kind of slow?

Forth uses a data stack, and my implementation is already kind of slow because of all the safety checks I do, like checking for underflows, overflows, etc. So I would put the data stack buffer in a separate process and then use remote calls to push numbers to the data stack, and the developer would write their function which is supposed to use the data on the data stack... and their function would have to use remote calls to manipulate the data... But if their function is a memory access... in order to get the protection, the memory access would have to be done from the developer's process and not the engine's process...

Hmm... I'm guessing sending messages to other processes is involved here... The main issue is that Forth lets developers do whatever... accesses to data are not through accessor functions. Instead addressed are pushed onto the data stack and the developer is allowed to do calculations on those addresses. Then the functions to read and write memory are called which uses the addresses on the data stack. Sometimes the developer will be accessing their own memory, and sometimes they will need to access the engine's memory. There's really no way to tell the difference between the two without completely rewriting the Forth language to include a type flag with the addresses on the data stack... (Someone is doing something like that by the way.. but he's using it to tell the difference between integers and floating point data.)