2

我正在寻找为什么在 ruby​​ 中扩展基类不是一个好主意的例子。我需要向一些人展示为什么它是一种需要小心使用的武器。

有什么恐怖故事可以分享吗?

4

3 回答 3

5

大约 2.5 年前,Rubinius发生了一个非常著名的猴子修补程序出错的例子。

这个案例的有趣之处在于,违规代码和受害者都非常明显且非常不寻常。通常,冒犯者是某个 PHP 脚本小子编写的一段代码,他们在他的 1337 元编程 h4X0r Skillz 上喝醉了。而失败模式是一个简单的ArgumentError例外,因为原始方法和monkeypatch 有不同的arity。

然而,在这种情况下,攻击者是 stdlib ( mathn) 中的一个库,故障模式是 Rubinius VM 完全崩溃。

所以发生了什么事?好吧,mathnmonkey 修补Fixnum类并改变Fixnum算术的工作方式。特别是,它改变了几种核心方法的结果类型。例如:

r = 4/3  # => 1
r.class  # => Fixnum

require 'mathn'

r = 4/3  # => (4/3)
r.class  # => Rational

问题当然是在 Rubinius 中,整个 Ruby 编译器、整个 Ruby 内核、大部分 Ruby 核心库、部分 Rubinius VM 和其他部分 Rubinius 基础设施,都是用 Ruby 编写的。当然,所有这些都使用Fixnum算术。

该类Hash是用 Ruby 编写的,它使用Fixnum算术来计算散列桶的大小,计算散列函数等等。Array是用 Ruby 编写的,需要计算元素大小和数组长度。FFI 库是用 Ruby 编写的,需要计算内存地址(!)和结构大小。Rubinius 的许多部分都假设他们可以进行一些Fixnum算术运算,然后将结果作为指针或 传递给某个 C 函数int

而且由于 Ruby 不支持任何类型的选择器命名空间或类装箱或类似的(尽管 Ruby 2.0 计划使用类似的东西),只要一些随机用户代码需要该mathn库,所有这些部分就会爆炸,因为所有突然之间,Fixnum操作的结果不再是 a Fixnum(它基本上与机器相同int,可以这样传递),而是 a Rational(它是一个成熟的 Ruby 对象)。

基本上,会发生什么,一些代码会require 'mathn'(或者你将它输入到 IRb 中),然后虚拟机就会立即死掉。

在这种情况下,解决方案是编译器的安全数学插件:当编译器检测到它正在编译内核或 Rubinius 的其他核心部分时,它会自动将对Fixnum方法的调用重写为对这些方法的私有不可变副本的调用。[注意:我认为在当前版本的 Rubinius 中,问题以不同的方式解决。]

于 2010-08-10T00:16:38.433 回答
3

失败的三重奏;或者,如何为 Ruby 1.8.7 修补 Rails 2.0有一个 Rails 示例(这是一个经过仔细审查的大型项目),由于他们通过猴子补丁String添加方法而导致问题chars

于 2010-08-09T23:56:32.257 回答
1

一个明显的缺陷是名称冲突——如果两个或多个包为行为不同的方法选择相同的名称。

于 2010-08-09T22:59:08.537 回答