4 ms·
It's not something that can be solved on an API level. You can rewrite the C standard library all you want, you still won't be able to differentiate between thi
by alxlaz 9y ago
It's not something that can be solved on an API level. You can rewrite the C standard library all you want, you still won't be able to differentiate between this:
void handle_signal(struct signal_handler_arg *arg)
which does signal handling
and this:
void check_signal_info(struct signal_handler_arg *arg)
which validates a struct signal_handler, is called outside a signal handler, and doesn't do any signal handling.
Similarly, you would not be able to verify that this is okay:
void handle_signalstruct signal_handler_arg *arg)
{
...
some_global_state->some_cb(...);
}
If some_cb is populated at runtime, how do you know (at compile-time) that it's been populated with a function that's safe to call inside a signal?
- alkonaut 9y ago> you still won't be able to differentiate between this > which validates a struct signal_handler, is called outside a signal handler, and doesn't do any signal handling. Let's use a pseudo OO syntax: void foo(arg: argtype) { arg.do_thing(); // ok frob(arg); // not possible - if frob might be forbidden anywhere then it's not callable like this } That frob is forbidden isn't because the compiler detected that foo is a signal handler, but because no forbidden functions exist. I'm not sure this would be practical in this context - but it's the only way I can imagine to have a compiler detect that you don't call a particular function from a particular context. Basically, very few global/static functoins would have to exist, and no/very little global state as well.
- alxlaz 9y agoI really don't know what you mean by this: > That frob is forbidden isn't because the compiler detected that foo is a signal handler, but because no forbidden functions exist. or this: > not possible - if frob might be forbidden anywhere then it's not callable like this
- alkonaut 9y agoI was trying to describe how an api would look where the compiler would make sure you could not call a certain function from a certain context. So I was trying to argue that then the api would have to be such that you can't actually import that function to that context. So basically: say that putchar is the forbidden function. Then there exists no global function "putchar". There is no way to just "import" a header and call putchar in our language/api. Instead, everything available to a function is what's passed to it (an interface to available API). So for example for a particular type of context (a handler, say) - a very restricted api is passed in as argument, meaning the code inside that function has very little api surface area to play with.
- alxlaz 9y ago> I was trying to describe how an api would look where the compiler would make sure you could not call a certain function from a certain context. It's not a certain set of functions that you want to forbid, it's a certain behaviour (e.g. being interruptible by signals, or being non-reentrancy). Forbidding functions won't get you anywhere. You can always forbid write(), but you won't be able to forbid functions that dynamically dispatch to write() at runtime, or user-defined functions that aren't reentrant. Edit: i.e. when we say you shouldn't call write(2) from a signal handler, that's not because there's a list somewhere in a POSIX standard with functions that you shouldn't call in a signal handler and write(2) is on it \. That's because the POSIX spec allows write(2) to be interrupted by a signal and that's not okay inside a signal handler. ___ \ There might be one, I haven't looked at a POSIX spec in a long time now -- but either way, the point is that such a list is open :-).
- alkonaut 9y agoI see. Preventing access to classes of behaviour is a harder problem. Reentrancy (single threaded - such as mutual recursion) must be really hard to detect with certainty other than at runtime, for example. In a multi threaded context it's obviously even harder. Classifying functions into more categories like "interruptible", "uninterruptible" seems doable with enough language support. It doesn't look to far from what e.g. Rust and C# does with "unsafe", i.e. a status of functions that bubbles up so that your function may be tainted as unsafe by calling unsafe functions. If the number of classifications of functions is small as is the case with unsafe/safe, or uninterruptible/interruptible then it would seem possible to sort this out via compiler support and keywords for example. So for example a signal handler is then an uninterruptible function. It's a compiler error to call even indirectly, or dynamically dispatch to interruptible functions. For any type of safety, dynamic dispatch obviously needs to be done in a type safe manner, so no pointer arithmetic/function pointer invocation can be allowed. Doing so would make the called code be assumed to be the worst of all categories: unsafe, interruptible etc). This is how unsafe/safe works too. So while it's cumbersome I'm sure it would be possible to achieve at least to some extent, given an expressive enough language and a clever enough compiler.