Executable Ada code on the stack

1.2k views Asked by At

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

3

There are 3 answers

1
AudioBubble On

A presentation in 2003 on Ada for secure applications (D. Wheeler, SigAda 2003) supports this on page 7 : (quote)

How do Ada and security match poorly?
...
Ada implementations typically need to execute code on the stack("trampolines", e.g. for access values to nested subprograms).

In other (C) words, for function pointers, where the subprograms are nested within other subprograms.

(Speculating : presumably these function pointers are on the stack so they go out of scope when you leave the scope of the outer subprogram)

HOWEVER

A quick search also showed this gcc mailing list message :
[Ada] remove trampolines from front end dated 2007, which refers to enabling Gnat executables to run on systems with DEP (data execution protection), by eliminating precisely this problematic feature.

This is NOT an authoritative answer but it seems that while "typical" Ada implementations do (or did) so, it may not be the case for at least Gnat this side of 2007, thanks to protection systems on newer hardware driving the necessary changes to the compiler.

Or : definitely true at one time, but possibly no longer true today, at least for Gnat.

I would welcome a more in-depth answer from a real expert...

EDIT : Adam's thorough answer states this is still true of Gnat, so my optimism should be tempered until further information.

2
Simon Wright On

FSF GCC 5 generates trampolines under circumstances documented here. This becomes problematic when the trampolines are actually used; in particular, when code takes ’Access or ’Unrestricted_Access of nested subprograms.

You can detect when your code does this by using

pragma Restrictions (No_Implicit_Dynamic_Code);

which needs to be used as a configuration pragma (although you won’t necessarily get warnings at compilation time, see PR 67205). The pragma is documented here.

You used to set configuration pragmas simply by including them in a file gnat.adc. If you’re using gnatmake you can also use the switch -gnatec=foo.adc. gprbuild doesn’t see gnat.adc; instead set global configurations in package Builder in your project file,

package Builder is
   for Global_Configuration_Pragmas use "foo.adc";
end Builder;

Violations end up with compilation errors like

$ gprbuild -P trampoline tramp
gcc -c tramp.adb
tramp.adb:26:12: violation of restriction "No_Implicit_Dynamic_Code" at /Users/simon/cortex-gnat-rts/test-arduino-due/gnat.adc:1
1
ajb On

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.