I've just watched a talk on security considerations for railway systems from last year's 32C3. At minute 25 the speaker briefly talks about Ada. Specifically he says:
Typical Ada implementations have a mechanism called "(tramp / trunk / ?) lines". And that means it will execute code on [the] stack which is not very good for C programs. And [...] if you want to link Ada code with C libraries, one of the security mechanisms won't work.
Here is a link (YouTube) to the respective part of the talk. This is the slide in the background. As you see I am unsure about one of the words. Perhaps it's trampolines?
Now my blunt question: Is there any truth in this statement? If so, can anyone elaborate on this mysterious feature of the Ada language and the security mechanism it apparently influences?
Until now I always assumed that code lives in a code segment (aka "text") whereas data (including the stack) is placed in a data segment at a different memory location (as depicted in this graphic). And reading about memory management in Ada suggests it should not be much different there.
While there are ways to circumvent such a layout (see e.g. this "C on stack" question and this "C on heap" answer), I believe modern OSes would commonly prevent such attempts via executable space protection unless the stack is explicitly made executable. - However, for embedded systems it may be still an issue if the code is not kept on ROM (can anyone clarify?).
They're called "trampolines". Here's my understanding of what they're for, although I'm not a GNAT expert, so some of my understanding could be mistaken.
Background: Ada (unlike C) supports nested subprograms. A nested subprogram is able to access the local variables of enclosing subprograms. For example:
procedure Outer is
Some_Variable : Integer;
procedure Inner is
begin
...
Some_Variable := Some_Variable + 1;
...
Since each procedure has its own stack frame that holds its own local variables, there has to be a way for Inner
to get at Outer
's stack frame, so that it can access Some_Variable
, either when Outer
calls Inner
, or Outer
calls some other nested subprograms that call Inner
. A typical implementation is to pass a hidden parameter to Inner
, often called a "static link", that points to Outer
's stack frame. Now Inner
can use that to access Some_Variable
.
The fun starts when you use Inner'Access
, which is an access procedure
type. This can be used to store the address of Inner
in a variable of an access procedure
type. Other subprograms can later use that variable to call the prodcedure indirectly. If you use 'Access
, the variable has to be declared inside Outer
--you can't store the procedure access in a variable outside Outer
, because then someone could call it later, after Outer
has exited and its local variables no longer exist. GNAT and other Ada compilers have an 'Unrestricted_Access
attribute that gets around this restriction, so that Outer
could call some outside subprogram that indirectly calls Inner
. But you have to be very careful when using it, because if you call it at the wrong time, the result would be havoc.
Anyway, the problem arises because when Inner'Access
is stored in a variable and later used to call Inner
indirectly, the hidden parameter with the static link has to be used when calling Inner
. So how does the indirect caller know what static link to pass?
One solution (Irvine Compiler's, and probably others) is to make variables of this access type have two values--the procedure address, and the static link (so an access procedure
is a "fat pointer", not a simple pointer). Then a call to that procedure will always pass the static link, in addition to other parameters (if any). [In Irvine Compiler's implementation, the static link inside the pointer will be null if it's actually pointing to a global procedure, so that the code knows not to pass the hidden parameter in that case.] The drawback is that this doesn't work when passing the procedure address as a callback parameter to a C routine (something very commonly done in Ada libraries that sit on top of C graphics libraries like gtk). The C library routines don't know how to handle fat pointers like this.
GNAT uses, or at one time used, trampolines to get around this. Basically, when it sees Inner'Unrestricted_Access'
, it will generate new code (a "trampoline") on the fly. This trampoline calls Inner
with the correct static link (the value of the link will be embedded in the code). The access value will then be a thin pointer, just one address, which is the address of the trampoline. Thus, when the C code calls the callback, it calls the trampoline, which then adds the hidden parameter to the parameter list and calls Inner
.
This solves the problem, but creates a security issue when the trampoline is generated on the stack.
Edit: I erred when referring to GNAT's implementation in the present tense. I last looked at this a few years ago, and I don't really know whether GNAT still does things this way. [Simon has better information about this.] By the way, I do think it's possible to use trampolines but not put them on the stack, which I think would reduce the security issues. When I last investigated this, if I recall correctly, Windows had started preventing code on the stack from being executed, but it also allowed programs to request memory that could be used to dynamically generate code that could be executed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With