Lua - 空值判斷的幾種情況


【摘要】 在安全領域,lua編程語言因為其小巧在眾多工具上都作為插件開發語言,常見的有openresty,nmap等。因此筆者將會開辟一個Lua相關的系列文章,主要記錄工作過程中一些領悟或者是一些踩過的坑,希望能夠借此平台幫助到讀者們。

0x00 背景

最近在寫一段nginx+redis的代碼,主要基於openresty,其中使用到了lua-resty-redis庫。我平時寫代碼都比較小心,針對外部輸入的值一般都會進行異常判斷,大概的代碼如下:

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)

在decode這里出現了錯誤提示,但是ok並沒有為空或者nil,不然代碼是走不到這里來。
發現問題后,我們就在前面打印一下ok數據的類型吧,大概的代碼如下:

ngx.log(ngx.ERR, 'ok type: ', type(ok))
if not ok then
    -- TODO
end

這個時候我們得到的結果是userdata,這個東西算是一種復雜結構體,一般都是跨語言產生的,比如ffi.C這些。當時我的思路大概也是這樣,肯定redis存放的數據是二進制的,但是呀,存放什么數據都是我自己控制的,不可能有什么畸形數據,因此這一點也排除了。最后在自己查看中發現,其實就是這個key不存在。

0x01 分析

既然原因找到了,我們就去看看為什么會這樣,主要通過閱讀lua-resty-redis的源碼:

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

從上面的源碼可以看到,在讀取redis服務器返回數據的時候,如果某些格式不正確,比如數據長度的字節小於0這樣的異常情況,函數就會返回null,注意是null不是nil
這個null的定義來自ngx.null,這個東西可以追溯到其官方文檔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.

從上面描述看,ngx.null就是一個代表null的userdata結構,類似一個自定義的類,但是沒有什么具體含義,同時文檔里面也提到了類似的值還有cjson.null,以后小心被坑。

0x02 擴展

同時文檔中還提到了,使用ngx.log對幾個空值進行字符串打印的時候

  • nil會顯示成“nil”,

  • 邏輯值會顯示成“true”或者“false”,

  • ngx.null會被顯示成“null”。

來源:華為雲社區  作者:HuangJacky

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM