微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Ada 中的无约束数组可以安全使用吗?

如何解决Ada 中的无约束数组可以安全使用吗?

我正在阅读 Ada 中的数组类型,发现有趣的是,与 C++ 不同,该语言允许在编译时未知它们的大小。我不确定它们是如何实现的,所以我写了一个小测试:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;

procedure Main is
   type Data is array (1 .. 131_072) of Integer;
   type Vector is array (Positive range <>) of Data;
   Sequence : Vector (1 .. Argument_Count);
begin
   for I in Sequence'Range loop
      Sequence (I) := (others => I);
      Put (Integer'Image (Sequence (I)(1)));
   end loop;
end Main;

然后尝试使用可变长度数组在 C 中复制此代码

#include <stdio.h>

struct data {
    int x[131072];
};

int main(int argc,char** argv) {
    (void)argv;
    struct data sequence[argc - 1];
    for (int i = 0; i < argc - 1; i++) {
        for (int j = 0; j < 131072; j++)
            sequence[i].x[j] = i;
        printf("%i ",sequence[i].x[1]);
    }
}

我用 gnatmake -gnato -fstack-check -gnat2012 -gnata -O3 main.adb -o maingcc -O3 -Wall -Werror -Wextra -pedantic cmain.c -o cmain 编译。运行程序后,当给出 16 个或更多参数时,两者都失败了 - 不同之处在于 cmain 只是段错误,而 main 最终引发“STORAGE_ERROR:堆栈溢出或错误的内存访问”。

由于 VLA 和无约束阵列似乎(至少在表面上)以类似的方式实现,而且前者被广泛认为在几乎所有情况下都不能安全使用,那么使用后者是否安全?

>

解决方法

Ada 中的无约束数组类型本身并不是不安全的;不安全的,或者至少可能导致异常的,是使用未经检查的输入值(例如 Argument_Count)来创建该大小的数组对象。如果您这样做,并且没有异常处理程序,则攻击者可以使您的程序因未处理的异常而中止,如您的示例所示。

请注意,Ada 中以两种方式使用不受约束的数组类型:

  1. 创建动态确定大小的数组对象,如您的示例所示。
  2. 将各种大小的数组作为参数传递给子程序,即使实际数组对象具有静态定义的大小。

第二次使用(参数)当然是完全安全的,参数的实际边界可以像往常一样通过A'First、A'Last、A'Range、A'Length访问。

,

不受约束的数组本质上并不是不安全的。请参阅下面实现数组元素并行添加的示例。

package Parallel_Addition is
   type Data_Array is array(Integer range <>) of Integer;
   type Data_Access is access all Data_Array;
   function Sum(Item : in not null Data_Access) return Integer;
end Parallel_Addition;

请注意,上面的包规范声明了一个不受约束的数组类型。它还声明了对无约束数组类型的访问类型,以便可以动态分配非常大的数组,避免堆栈耗尽问题。

package body Parallel_Addition is

   ---------
   -- Sum --
   ---------

   function Sum (Item : in not null Data_Access) return Integer is
      task type Adder is
         entry Set (Min : Integer; Max : Integer);
         entry Report (Value : out Integer);
      end Adder;

      task body Adder is
         Total : Integer := 0;
         First : Integer;
         Last  : Integer;
      begin
         accept Set (Min : Integer; Max : Integer) do
            First := Min;
            Last  := Max;
         end Set;
         for I in First .. Last loop
            Total := Total + Item (I);
         end loop;
         accept Report (Value : out Integer) do
            Value := Total;
         end Report;
      end Adder;
      A1  : Adder;
      A2  : Adder;
      R1  : Integer;
      R2  : Integer;
      Mid : constant Integer := (Item'Length / 2) + Item'First;
   begin
      A1.Set (Min => Item'First,Max => Mid);
      A2.Set (Min => Mid + 1,Max => Item'Last);
      A1.Report (R1);
      A2.Report (R2);
      return R1 + R2;
   end Sum;

end Parallel_Addition;

在函数 Sum 中使用不受约束的数组实例没有问题。 让我们尝试使用一个非常大的动态分配数组来测试这个包。

with Parallel_Addition; use Parallel_Addition;
with Ada.Text_IO;       use Ada.Text_IO;
with Ada.Calendar;      use Ada.Calendar;

procedure Parallel_Addition_Test is
   The_Data : Data_Access := new Data_Array (1 .. Integer'Last);
   Start    : Time;
   Stop     : Time;
   The_Sum  : Integer;

begin
   The_Data.all := (others => 1);
   Start        := Clock;
   The_Sum      := Sum (The_Data);
   Stop         := Clock;
   Put_Line ("The sum is: " & Integer'Image (The_Sum));
   Put_Line
     ("Addition elapsed time is " &
      Duration'Image (Stop - Start) &
        " seconds.");
   Put_Line
     ("Time per addition operation is " &
        Float'Image(Float(Stop - Start) / Float(The_Data'Length)) &
        " seconds.");
end Parallel_Addition_Test;

在我的 Windows 10 PC 上执行时,我得到以下输出:

The sum is:  2147483647
Addition elapsed time is  5.141288000 seconds.
Time per addition operation is  2.39410E-09 seconds.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。