Accepted Reply
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 think what you are telling me is there is no standard way other than setting signals to trap a memory access error.
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:
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.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?
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.But the main reason is, my application is a Forth based script engine and the user can put in any addresses they want
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Replies
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
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"
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.
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 think what you are telling me is there is no standard way other than setting signals to trap a memory access error.
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:
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.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?
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.But the main reason is, my application is a Forth based script engine and the user can put in any addresses they want
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
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!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.
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.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.
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'm not sure I'm understanding your suggestion here. In my Forth the programmer can do something like: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.
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.
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.To help with debugging.
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.
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Yes. That is kind of the whole point.
The key question here is whether you let your Forth engine interact directly with OS-level objects.
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.
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.
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.)