Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Executable Ada code on the stack

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?).

like image 698
morido Avatar asked Dec 09 '22 00:12

morido


1 Answers

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.

like image 170
ajb Avatar answered Jan 04 '23 20:01

ajb