Nmap/Lua 5.2

From SecWiki
Jump to: navigation, search

New features in Lua 5.2 of particular interest for NSE

  • New hexadecimal escape sequences for strings. Scripts may now use, for example, "\x0a" instead of "\010".
  • Lexical environments with _ENV. Environments are now simply upvalues of a function and you may introduce a new environment into a scope by creating a new local named _ENV with a lifetime that expires at the end of the scope. For example:
local print = print
function foo ()
  a = 5
  do
    local _ENV = {}
    print(a) --> nil
    a = 6
    print(a) --> 6
  end
  print(a) --> 5
end

You may also do things like:

local print = print
function foo (_ENV)
  print(a)
end
foo({a = 5}) --> 5
foo({a = 6}) --> 6

The biggest gotcha you may experience is figuring out that changing the environment for one function may also change the environment of others. For example:

local print = print
_ENV = {foo = 1}
local function helper ()
  print(foo)
end
function foo ()
  _ENV = nil
end
foo()
helper() --> attempt to index upvalue '_ENV' (a nil value)

The old Lua 5.1 environments are completely gone. getfenv and setfenv are gone as a result.

  • Libraries must be required and stored in locals by scripts. The require function no longer puts libraries in the global namespace once required. So instead of:
require "http"

You must do:

local http = require "http"

The days of getting away with forgetting a require are also over. :)

  • Coroutines may yield across most C call boundaries. One of the simplest examples of such an error in 5.1:
$ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> F = coroutine.wrap(function(...)
> >   return pcall(function(...)
>>     return coroutine.yield(...)
>>   end, ...)
 >> end)
> print(F("Hello"))
false   attempt to yield across metamethod/C-call boundary

This changes allows for coroutines in generic for loop iterators which yield outside of the for loop. For example:

-- This example does not handle EOF or errors.
local function lines (socket)
  while true do coroutine.yield(socket:receive_buf("\r?\n", false)) end
end
for line in lines(socket) do
  --> do something
end
  • Lua 5.2 comes with a default bit library called bit32.
  • Lua closures are now cached. The ramifications are best illustrated:
$ lua51
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> for i = 1, 2 do print(function() end) end
function: 0x2104ad0
function: 0x2104fe0
$ lua52
Lua 5.2.0  Copyright (C) 1994-2011 Lua.org, PUC-Rio
> for i = 1, 2 do print(function() end) end
function: 0x1d51070
function: 0x1d51070

Notice the closures are equivalent (and we could confirm equality using ==). An example where the closures change:

$ lua52
Lua 5.2.0  Copyright (C) 1994-2011 Lua.org, PUC-Rio
> for i = 1, 2 do print(function() return i end) end
function: 0x796070
function: 0x796d30

This produces distinct closures because the upvalues are different. Namely, the i local is recreated on each iteration of the loop so each closure references a different i.

It is an optimization to cache closures. This optimization lessens the performance impact of an anonymous function used in a loop. For example:

-- Escape non-graphical characters
function escape (str)
  return (str:gsub("[^%w%s%p]", function (a) return ("\\x%02x"):format(a:byte()) end));
end

In Lua 5.1, if the above escape function is called in a loop, it would create a closure for the call to gsub each time. These become "garbage" because they are only used once. The way to correct this is to pull the function out into a local above the escape function:

-- Lua 5.1 optimization for escape function
do
  local function helper (a)
    return ("\\x%02x"):format(a:byte())
  end
  function escape (str)
    return (str:gsub("[^%w%s%p]", helper));
  end
end

In Lua 5.2, this is largely unnecessary as the closure is cached for future use.

::forever::
goto forever

The goto mechanism was added partially to solve the lack of a continue statement and nested break.

for i = 1, 10 do
  if i % 2 == 0 then goto cont end
  ::cont::
end
for i = 1, 10 do
  for j  = 1, 10 do
     for k = 1, 10 do
      if i + j + k == 99 then goto done end
    end
  end
end
::done::
  • __pairs and __ipairs metamethods. You can now define how an object is iterated over using the pairs and ipairs functions.
$ lua52
Lua 5.2.0  Copyright (C) 1994-2011 Lua.org, PUC-Rio
> t = {1, 2, 3, a = 1}
> setmetatable(t, {__ipairs = pairs}) -- make ipairs work like pairs
> for i, v in ipairs(t) do print(i, v) end
1       1
2       2
3       3
a       1