Abstract In the security field, lua programming language because it is compact in a number of plug-in development tools as a language, a common openresty, nmap and so on. So it will open up a series of articles related to Lua, some of the main recording work process or some insight stepped pit, hoping to take this platform to help readers.
0x00 background
Recently write a nginx + redis code, mainly based openresty, where to lua-resty-redis library. I usually write the code are more careful, the abnormality judgment for the value usually external input, probably the code is as follows:
local redis = require "redis"
local cjson = require "cjson"
--[[省略部分代码]]
local ok, err = redis:get("key")
if not ok then
ngx.log(ngx.ERR, '[ERROR]:', err)
return
end
local data = cjson.decode(ok)
In the decode error occurred here, ok but not empty or nil, otherwise the code is able to get here.
Having identified the problem, we kind of ok print data in front of it, probably the code is as follows:
ngx.log(ngx.ERR, 'ok type: ', type(ok))
if not ok then
-- TODO
end
This time the results we get is userdata, this thing is regarded as a complex structure, it is generally produced by cross-language, such as ffi.C these. I was thinking about, too, certainly redis data is stored in binary, but ah, what data to store all my own control, can not have any abnormal data, so this is also ruled out. Finally, in his view found, in fact, this key does not exist.
0x01 analysis
Since the cause is found, we went to see why this is so, mainly through reading lua-resty-redis source:
local function _read_reply(self, sock)
local line, err = sock:receive()
if not line then
if err == "timeout" and not rawget(self, "_subscribed") then
sock:close()
end
return nil, err
end
local prefix = byte(line)
if prefix == 36 then -- char '$'
-- print("bulk reply")
local size = tonumber(sub(line, 2))
if size < 0 then
return null
end
local data, err = sock:receive(size)
if not data then
if err == "timeout" then
sock:close()
end
return nil, err
end
local dummy, err = sock:receive(2) -- ignore CRLF
if not dummy then
return nil, err
end
return data
elseif prefix == 43 then -- char '+'
-- print("status reply")
return sub(line, 2)
elseif prefix == 42 then -- char '*'
local n = tonumber(sub(line, 2))
-- print("multi-bulk reply: ", n)
if n < 0 then
return null
end
local vals = new_tab(n, 0)
local nvals = 0
for i = 1, n do
local res, err = _read_reply(self, sock)
if res then
nvals = nvals + 1
vals[nvals] = res
elseif res == nil then
return nil, err
else
-- be a valid redis error value
nvals = nvals + 1
vals[nvals] = {false, err}
end
end
return vals
elseif prefix == 58 then -- char ':'
-- print("integer reply")
return tonumber(sub(line, 2))
elseif prefix == 45 then -- char '-'
-- print("error reply: ", n)
return false, sub(line, 2)
else
-- when `line` is an empty string, `prefix` will be equal to nil.
return nil, "unknown prefix: \"" .. tostring(prefix) .. "\""
end
end
Source can be seen from the above, the server returns the data read redis time, if some of the format is not correct, such as byte data length is less than 0 such anomalies, the function returns null, null note is not nil .
Definition of the null from ngx.null, this thing can be traced back to its official documentation lua-nginx-module.
The ngx.null constant is a NULL light userdata usually used to represent nil values in Lua tables etc and is similar to the lua-cjson library’s cjson.null constant.
See from the above description, ngx.null is a representative userdata null structure, similar to a custom class, but there is no specific meaning, while inside the document also mentioned the similar values as well as cjson.null, after being carefully pit.
0x02 Extended
At the same time the document also mentions the use ngx.log several null string when printing
-
nil will be displayed as "nil",
-
It appears as the logic value "true" or "false",
-
ngx.null it will be displayed as "null".
Source: Huawei cloud community Author: HuangJacky