3

tritwise 操作、向右旋转和疯狂操作无法正常运行并在 Malbolge 编译器/解释器中引发分段错误。

在看到有关 Coding Challenges 和 Code Golf 的惊人答案后,我决定开始在 Malbolge 进行编程,并且还学习了使用硬编程语言进行编程。

当我尝试输出一个固定字符时,我注意到*and p(在 Normalized Malbolge 中)在我尝试使用它们的大部分时间都抛出了分段错误。

我尝试使用互联网查找字符串(在 Google 上)“'Malbolge' 疯狂操作'segfaults'”和“'Malbolge' 向右旋转'segfaults'”。我还尝试在不同的上下文中使用这些命令,发现如果没有输入(这不是我想要的)它就可以工作。

我正在使用由 tio.run 或 Try It Online 托管的在线解释器。

我尝试使用的代码:

归一化马尔堡:/*<

马尔博格:u&a

在线尝试!

归一化马尔堡:/p*<v

马尔博格:u=%`M

在线尝试!

归一化马尔堡:/pp<v

马尔博格:u=<`M

在线尝试!

我希望 、 和 的输出u&a不会u=%`M引发u=<`M任何错误,但实际输出是分段错误。

确切的错误: /srv/wrappers/malbolge: line 3: 21992 Segmentation fault (core dumped) /opt/malbolge/malbolge .code.tio < .input.tio其中 21992 可以是任何数字(很可能是数千到一万)

4

1 回答 1

3

我一直在调试器中单步执行 Malbolge 解释器,以诊断这里发生了什么。我想说“恭喜,您在 Malbolge 解释器中发现了一个错误”,但鉴于规范和解释器在其他方面不匹配(通常将权威版本作为解释器),并且这是 Malbolge,据我所知,这是预期的行为。(好吧,它实际上可能不是预期的行为,但是其他许多功能也不是被视为重要的编程技术。)

Malbolge 将所有内容存储在一个大数组中,包括代码和数据。命令旨在在运行后自行修改(“加密”,在 Malbolge 术语中),但解释器并没有完全正确地实现它:它实际上做的是运行一个命令,然后查看由代码指针并对其进行加密。这就是为什么跳转会加密跳转目标之前的指令,而不是跳转指令本身。

如果您正在运行的命令超出 33 到 126(含)范围,则该命令不会运行(实际上,在我拥有的 Malbolge 解释器版本中,代码和数据指针也不会增加,看起来像这将不可避免地导致无限循环;也许还有其他版本可以解决该问题)。这是一项重要的检查,因为加密例程只是通过索引查找表来工作;33 到 126 范围之外的值最终会从数组之前或之后读取一些任意字节的内存。

不幸的是,由于代码和数据一起存储在一个大数组中,因此命令最终可能会在运行时修改自身:它可能在运行前处于 33 到 126 的范围内(从而导致安全检查成功),但在运行之后运行时,它超出范围,然后加密将最终执行查找表的越界索引。Malbolge 解释器是用 C 语言编写的,它对于越界读取具有未定义的行为,但对于非常长的越界读取,可能会出现分段错误(但不能保证)行为。

让我们看看代码发生了什么u&a

Command   A     C     D   memory
  start   0     0     0   117,    38, 97, 29432, 98, 29431, 98, 29432, 97, ...
      / input   0     0   117,    38, 97, 29432, 98, 29431, 98, 29432, 97, ...
encrypt input   1     1   111,    38, 97, 29432, 98, 29431, 98, 29432, 97, ...
      * 39378   1     1   111, 39378, 97, 29432, 98, 29431, 98, 29432, 97, ...
encrypt 39378   2     2   111,   ???, 97, 29432, 98, 29431, 98, 29432, 97, ...

如您所见,您实际上并没有旋转加载到的输入AD旋转操作从(而不是)指向的地址读取A,因此您将旋转 38(*内存位置 1 中命令的内存表示形式)以生成 39378。该值同时存储到A内存位置 1 中。不幸的是,内存位置 1 是当前正在执行的命令,因此当需要对其进行加密时,解释器会越界读取查找表(试图在仅涵盖从 33 到 126 的范围),如果幸运的话,这将产生分段错误。

这种行为很可能出现在“简单”的 Malbolge 程序中,因为 C 和 D 以相同的值开始并以相同的速率增加。如果您希望旋转指令影响当前运行的命令以外的其他内容,则必须以某种方式使它们不同步。最简单的方法通常是一个j命令(注意:使用生成的数据指针可能不是特别容易,但至少它可能在代码指针之外的其他地方)。

顺便说一句,与您给出的示例程序相比,实际旋转用户输入需要更多的努力。您必须首先将其存储到内存中,并且唯一能够根据 A 的值写入内存的操作是p,这要求相关单元格已经具有适当的值(为避免丢失信息,这需要是 29524 ,您必须通过 Malbolge 的算术产生一个值,因为它不能作为原始程序的一部分输入,即使这样,您最终也会在输入值中将 0 个 trits 与 1 个 trits 交换)。然后您必须将数据指针发送回您写入输入的单元格,以便您可以在其上运行*以旋转它。

于 2019-08-14T02:54:41.850 回答