Stack Scrubbing (GNAT Reference Manual)

### 18.2 Stack Scrubbing ¶

GNAT can generate code to zero-out stack frames used by subprograms.

It can be activated with the `Machine_Attribute` pragma, on specific subprograms and variables, or their types. (This attribute always applies to a type, even when it is associated with a subprogram or a variable.)

    function Foo returns Integer;
    pragma Machine_Attribute (Foo, "strub");
    --  Foo and its callers are modified so as to scrub the stack
    --  space used by Foo after it returns.  Shorthand for:
    --  pragma Machine_Attribute (Foo, "strub", "at-calls");

    procedure Bar;
    pragma Machine_Attribute (Bar, "strub", "internal");
    --  Bar is turned into a wrapper for its original body,
    --  and they scrub the stack used by the original body.

    Var : Integer;
    pragma Machine_Attribute (Var, "strub");
    --  Reading from Var in a subprogram enables stack scrubbing
    --  of the stack space used by the subprogram.  Furthermore, if
    --  Var is declared within a subprogram, this also enables
    --  scrubbing of the stack space used by that subprogram.

Given these declarations, Foo has its type and body modified as follows:

    function Foo (<WaterMark> : in out System.Address) returns Integer
    is
      --  ...
    begin
      <__strub_update> (<WaterMark>);  --  Updates the stack WaterMark.
      --  ...
    end;

whereas its callers are modified from:

    X := Foo;

to:

    declare
      <WaterMark> : System.Address;
    begin
      <__strub_enter> (<WaterMark>);  -- Initialize <WaterMark>.
      X := Foo (<WaterMark>);
      <__strub_leave> (<WaterMark>);  -- Scrubs stack up to <WaterMark>.
    end;

As for Bar, because it is strubbed in internal mode, its callers are not modified. Its definition is modified roughly as follows:

    procedure Bar is
      <WaterMark> : System.Address;
      procedure Strubbed_Bar (<WaterMark> : in out System.Address) is
      begin
        <__strub_update> (<WaterMark>);  --  Updates the stack WaterMark.
        -- original Bar body.
      end Strubbed_Bar;
    begin
      <__strub_enter> (<WaterMark>);  -- Initialize <WaterMark>.
      Strubbed_Bar (<WaterMark>);
      <__strub_leave> (<WaterMark>);  -- Scrubs stack up to <WaterMark>.
    end Bar;

There are also `-fstrub=`choice'` command-line options to control default settings. For usage and more details on the command-line options, on the `strub` attribute, and their use with other programming languages, see Using the GNU Compiler Collection (GCC).

Note that Ada secondary stacks are not scrubbed. The restriction `No_Secondary_Stack` avoids their use, and thus their accidental preservation of data that should be scrubbed.

Attributes `Access` and `Unconstrained_Access` of variables and constants with `strub` enabled require types with `strub` enabled; there is no way to express an access-to-strub type otherwise. `Unchecked_Access` bypasses this constraint, but the resulting access type designates a non-strub type.

    VI : aliased Integer;
    pragma Machine_Attribute (VI, "strub");
    XsVI : access Integer := VI'Access; -- Error.
    UXsVI : access Integer := VI'Unchecked_Access; -- OK,
    --  UXsVI does *not* enable strub in subprograms that
    --  dereference it to obtain the UXsVI.all value.

    type Strub_Int is new Integer;
    pragma Machine_Attribute (Strub_Int, "strub");
    VSI : aliased Strub_Int;
    XsVSI : access Strub_Int := VSI'Access; -- OK,
    --  VSI and XsVSI.all both enable strub in subprograms that
    --  read their values.

Every access-to-subprogram type, renaming, and overriding and overridden dispatching operations that may refer to a subprogram with an attribute-modified interface must be annotated with the same interface-modifying attribute. Access-to-subprogram types can be explicitly converted to different strub modes, as long as they are interface-compatible (i.e., adding or removing `at-calls` is not allowed). For example, a `strub`-`disabled` subprogram can be turned `callable` through such an explicit conversion:

    type TBar is access procedure;

    type TBar_Callable is access procedure;
    pragma Machine_Attribute (TBar_Callable, "strub", "callable");
    --  The attribute modifies the procedure type, rather than the
    --  access type, because of the extra argument after "strub",
    --  only applicable to subprogram types.

    Bar_Callable_Ptr : constant TBar_Callable
               := TBar_Callable (TBar'(Bar'Access));

    procedure Bar_Callable renames Bar_Callable_Ptr.all;
    pragma Machine_Attribute (Bar_Callable, "strub", "callable");

Note that the renaming declaration is expanded to a full subprogram body, it won’t be just an alias. Only if it is inlined will it be as efficient as a call by dereferencing the access-to-subprogram constant Bar_Callable_Ptr.