Lua解包漏洞?

11

我在 Lua 的 unpack 函数中遇到了奇怪的行为

table1 = {true, nil, true, false, nil, true, nil}
table2 = {true, false, nil, false, nil, true, nil}

a1,b1,c1,d1,e1,f1,g1 = unpack( table1 )
print ("table1:",a1,b1,c1,d1,e1,f1,g1)

a2,b2,c2,d2,e2,f2,g2 = unpack( table2 )
print ("table2:",a2,b2,c2,d2,e2,f2,g2)

输出:

table1: true    nil true    false   nil nil nil
table2: true    false   nil nil nil nil nil
第二个解包提供的参数是直到第一个 "nil" 值为止。我可以理解这一点。 第一个表提供了四个参数,其中一个在中间是 "nil"。它有四个不是 "nil" 的参数,但它们不是显示出来的那个参数。
有人能解释一下吗? 这是在 codepad.org 和 lua 5.1 上尝试的。
2个回答

17
问题可以通过指定起始和结束索引到unpack()并使用table.maxn()作为结束索引来简单解决:
table1 = {true, nil, true, false, nil, true, nil}
a1,b1,c1,d1,e1,f1,g1 = unpack( table1, 1, table.maxn(table1) ) print ("table1:",a1,b1,c1,d1,e1,f1,g1) -->table1: true nil true false nil true nil
两个表处理方式的差异真正原因在于确定表数组部分长度的逻辑。 luaB_unpack()函数使用luaL_getn(),后者以lua_objlen()定义,后者对于表调用luaH_getn()luaH_getn()查看数组的最后一个位置,如果它是nil,则在表中执行二进制搜索以找到边界(“t [i]非空且t [i + 1]为空”)。二分搜索数组结尾的原因是table1table2处理方式不同。
只有当数组中的最后一个条目为nil时,才会出现此问题。
《Lua编程》(第16页)得知(您应该购买这本书):当数组中有空洞(即其中包含nil元素)时,长度运算符可以将这些nil元素中的任意一个作为结束标记。因此,您应避免在可能包含空洞的数组上使用长度运算符。 unpack()使用长度运算符lua_objlen(),后者“可能将任何[这些]nil元素中的任意一个”作为数组的结尾。

谢谢,你救了我的一天。这是否意味着table.maxn()遍历整个分配给表的大小? - Geggamojja
1
table.maxn()函数“返回给定表中最大的正数索引”,请参见http://www.lua.org/manual/5.1/manual.html#pdf-table.maxn。 - gwell
table.maxn在这里真的保证返回正确的值(即表文字中对象的数量)吗? - u0b34a0f6ae
@kazer.se table.maxn将返回6,这是表格table1中具有非空条目的最大索引。第7个位置上的nil不会被table.maxn计算;但是,在这种情况下,它完美地工作。unpack将返回6个元素,分配给第7个变量的变量将收到nil作为“列表随着需要延长了尽可能多的nil”用于分配。 - gwell

3

2.2 - 值和类型

[...] table 类型实现了关联数组,也就是说,这种数组不仅可以用数字索引,还可以用任何值(除了 nil)进行索引。表可以是异构的;也就是说,它们可以包含所有类型的值(除了 nil)。[...]

nil 赋给一个条目会中断表的枚举,并且您的变量将无法正确初始化。

以下是一个简单的示例,演示了一个有问题的行为:

table1 = {true, false, nil, false, nil, true, nil}
for k,v in ipairs(table1) do
  print(k, v)
end

输出:

1   true
2   false
>Exit code: 0

更具体地说,我怀疑它忽略了他指定的nil值。 - RCIX
不,它没有 RCIX,在这种情况下,我想表2也会返回4个值。 - Geggamojja
for循环停止的唯一原因是迭代器在第3个位置返回了nil。表格仍然被正确初始化,因为assert(table1[1]==true and table1[2]==false and table1[3]==nil and table1[4]==false and table1[5]==nil and table1[6]==true and table1[7]==nil)没有断言。 - gwell
1
@gwell,我说过nil可以中断枚举,但我并没有说元素会停止存在。 - Nick Dandoulakis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接