stack - 堆栈上的可执行Ada代码

标签 stack ada scada data-segment safety-critical

我刚刚看了security considerations for railway systems中有关last year's 32C3的演讲。
在第25分钟,演讲者简短地谈论了Ada。他具体说:


典型的Ada实现具有一种称为“((流浪汉/干线/
?)行”。这意味着它将在[the]堆栈上执行代码
对于C程序来说不是很好。还有[...]如果要将Ada代码与C链接
库,其中一种安全机制将无法正常工作。


这是该演讲的相应部分的link(YouTube)。 This是背景中的幻灯片。如您所见,我不确定其中一个词。也许是trampolines



现在我直截了当地问:这句话有什么道理吗?如果是这样,那么谁能详细说明Ada语言的这一神秘功能以及它显然会影响的安全机制?

到目前为止,我一直认为代码位于代码段(也称为“文本”)中,而数据(包括堆栈)位于不同内存位置的数据段中(如this graphic中所述)。阅读memory management in Ada暗示那里应该没有太大的不同。

尽管有许多方法可以绕开这种布局(例如,参见“ C on stack”问题和“ C on heap”答案),但我相信现代操作系统通常会通过executable space protection阻止此类尝试,除非堆栈明确地为made executable 。 -但是,对于嵌入式系统,如果代码未保存在ROM上,可能仍然是一个问题(有人可以澄清吗?)。

最佳答案

他们被称为“蹦床”。尽管我不是GNAT专家,但这是我对它们的用途的理解,因此我的某些理解可能会被误解。

背景:Ada(与C不同)支持嵌套子程序。嵌套子程序能够访问封闭子程序的局部变量。例如:

procedure Outer is
    Some_Variable : Integer;

    procedure Inner is
    begin
        ...
        Some_Variable := Some_Variable + 1;
        ...


由于每个过程都有自己的堆栈框架并保存自己的局部变量,因此Inner必须有一种获取Outer堆栈框架的方法,以便它可以在Some_Variable时访问Outer。调用InnerOuter调用其他一些嵌套的子程序,这些子程序调用Inner。一种典型的实现方式是将隐藏参数传递给Inner,通常称为“静态链接”,该参数指向Outer的堆栈框架。现在,Inner可以使用它来访问Some_Variable

当您使用Inner'Access(一种access procedure类型)时,乐趣就开始了。这可用于将Inner的地址存储在access procedure类型的变量中。其他子程序以后可以使用该变量间接调用该过程。如果使用'Access,则必须在Outer中声明该变量-您不能将过程访问存储在Outer之外的变量中,因为在Outer退出后,有人可以稍后调用它,并且它的局部变量不再存在。 GNAT和其他Ada编译器具有绕过此限制的'Unrestricted_Access属性,因此Outer可以调用一些间接调用Inner的外部子程序。但是使用它时必须非常小心,因为如果在错误的时间调用它,将会造成严重破坏。

无论如何,都会出现问题,因为将Inner'Access存储在变量中并随后用于间接调用Inner时,在调用Inner时必须使用带有静态链接的隐藏参数。那么,间接调用者如何知道要传递的静态链接?

一种解决方案(Irvine Compiler的解决方案,可能还有其他解决方案)是使这种访问类型的变量具有两个值-过程地址和静态链接(因此access procedure是“胖指针”,而不是简单的指针)。然后,除其他参数(如果有)外,对该过程的调用将始终通过静态链接。 [在Irvine Compiler的实现中,如果指针中的静态链接实际上指向全局过程,则该链接将为null,因此该代码知道在这种情况下不传递隐藏参数。]缺点是,当该指针不起作用时,它将不起作用。将过程地址作为回调参数传递给C例程(在像gtk这样的C图形库顶部的Ada库中很常见)。 C库例程不知道如何处理像这样的胖指针。

GNAT使用或一次使用蹦床来解决此问题。基本上,当看到Inner'Unrestricted_Access'时,它将即时生成新代码(“蹦床”)。该蹦床用正确的静态链接调用Inner(链接的值将嵌入在代码中)。访问值将是一个瘦指针,只是一个地址,即蹦床的地址。因此,当C代码调用回调时,它会调用蹦床,然后将隐藏的参数添加到参数列表中并调用Inner

这样可以解决问题,但是在堆栈上生成蹦床时会产生安全问题。

编辑:以当前时态引用GNAT的实现时,我错了。几年前,我上次查看此内容,但我真的不知道GNAT是否仍以这种方式进行操作。 [Simon对此有更好的信息。]顺便说一句,我确实认为可以使用蹦床,但不能将它们放在堆栈中,我认为这可以减少安全性问题。当我上次调查此问题时,如果我没有记错的话,Windows已经开始阻止执行堆栈上的代码,但是它还允许程序请求内存,这些内存可用于动态生成可以执行的代码。

关于stack - 堆栈上的可执行Ada代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34982151/

相关文章:

c# - 服务栈服务器事件

css - 为什么我的媒体查询不起作用?

floating-point - 阿达95 : How to use Float in Exponent?

android - 在运行时从 scada 系统获取关于 android 应用程序的信息

.net - 在 .NET 中创建 SCADA 图

c++ - 有关堆栈大小的警告消息

c++ - 为什么在堆栈仍有元素时跳过 "if stack not empty"条件?

logging - 除了 Alog (http ://www. nongnu.org/alog/) 之外,是否还有 Ada 的日志记录框架或众所周知的设计模式?

Ada 手册 : difference between annotated and consolidated

python - 无法在 Ubuntu 16.04(64 位)上安装 Snap7 库