3 ms·
How would you differentiate a function that takes an argument of some kind and handles signals from one that just takes an argument of that kind (e.g. for debug
by alxlaz 9y ago
How would you differentiate a function that takes an argument of some kind and handles signals from one that just takes an argument of that kind (e.g. for debugging, logging, or some other sort of introspection)?
Edit: to be clear -- I'm not asking this just to be snarky :-). Inferring functional information from nothing but semantic information with no functional implication is a very complicated problem that has very error-prone solutions. In C's case, in fact, I think C99 defines signal handlers as taking a single argument, an int. A compiler that doesn't let me call free from a function that takes a single integer argument wouldn't be too useful.
IRL, there are analysis tools that can help catch (a subset of) this sort of problem, but they are environment-specific and don't rely on just the function definition. They either look at the signal handler installation calls (but then they're restricted to information that's known at compile time!), or require some sort of annotation (e.g. via special comments a la Doxygen, or via macros etc.).
And even then, the sort of problems that they catch are specific to each environment. The restriction isn't that you shouldn't call <these functions> under POSIX, the restriction is that you shouldn't call functions that are not async-signal-safe (i.e. they're not re-entrant or they're not atomic with respect to signals). There are a few POSIX functions that are safe to call from signals (see man 7 signal-safety on a Linux box), but that list obviously doesn't cover user-defined functions -- some of which may be OK to call, others not so much.
You can probably determine if a function is async-call-safe with some static analysis but at this point, it's the sort of stuff that really doesn't belong in a compiler anymore...
- alkonaut 9y agoTo be clear: I wasn't suggesting that making everything context aware would be possible in either C or Posix. It would most likely require a complete rewrite of all APIs to begin with - including the C standard library. It would no longer resemble posix after that.
- alxlaz 9y agoIt'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 :-).