Lua バグ

Luaの各リリースで見つかったバグのリストを、4.0以降で掲載しています。

各エントリには、バグを示す最小限の例と、利用可能な場合はパッチまたは解決策が含まれています。より大きな修正は、LuaのGitHubリポジトリに掲載されています。これは不定期にミラーリングされます。

Luaのすべてのリリースでは、特に記載がない限り、以前のリリースにリストされているすべてのバグが修正されています。新しいリリースで見つかった一部のバグは、古いリリースに存在していた可能性があります。

バグを報告したい場合は、まずこちらをお読みください

Lua 5.4.6 1 · 2 · 3 · 4 · 5 · 6 · 7

  1. 'l_strcmp' での読み取りオーバーフロー。
    2023年6月9日にXmilia Hermitにより報告されました。5.0(少なくとも)から存在していました。githubで修正されました。

    -- You must have the locale 'km_KH.UTF-8' installed
    assert(os.setlocale("km_KH.UTF-8"))
    local A = "\u{17A4}"
    local B = "\u{17A2}\u{17B6}"
    print(string.rep(B, 100000) .. "\0" <= string.rep(A, 100000) .. "\0")
    
  2. カウントフックが中断した場合、コールフックが2回呼び出される可能性があります。
    2023年7月20日にG.k Rayにより報告されました。5.4.0(少なくとも)から存在していました。githubで修正されました。

    /* The following program prints "Call hook: Lua" twice, while it
       should print that only once.  */
    
    #include <stdio.h>
    #include "lua.h"
    #include "lauxlib.h"
    
    static void hook (lua_State *L, lua_Debug *ar) {
      if (ar->event == LUA_HOOKCALL) {  /* call hook? */
        lua_getinfo(L, "S", ar);
        printf("Call hook: %s\n", ar->what);
      }
      else  /* count hook */
        lua_yield(L, 0);
    }
    
    int main (void) {
      lua_State *L = luaL_newstate();
      lua_State *l = lua_newthread(L);
      luaL_loadstring(l, "(function(x) end)()");
      lua_sethook(l, hook, LUA_MASKCOUNT | LUA_MASKCALL, 1);
      int nres;
      while (lua_resume(l, NULL, 0, &nres) == LUA_YIELD) { }
      lua_close(L);
    }
    
  3. 関数呼び出しの行番号が間違っています。
    2023年8月20日にThadeu de Paulaにより報告されました。5.2から存在していました。githubで修正されました。

    -- error should be at line 3
    foo
    (
    0
    )
    --> lua: temp:2: attempt to call a nil value (global 'foo')
    
  4. 'getobjname'の再帰がスタックオーバーフローを引き起こす可能性があります。
    2023年10月30日にSergey Bronnikovにより報告されました。5.4.0から存在していました。githubで修正されました。

    -- This code takes forever to run, and eventually it crashes
    local a = {"local d = 1; d = d * _G"}
    for i = 2, 1e7 do a[i] = "._G" end
    a = table.concat(a)
    a = load(a)
    print(a())
    
  5. '_ENV'と呼ばれるフィールドのエラーメッセージが間違っています。
    2023年10月31日にRobertoにより報告されました。5.4.0から存在していました。githubで修正されました。

    a = {_ENV = {}}
    print(a._ENV.x + 1)
    --> attempt to perform arithmetic on a nil value (global 'x')
    
  6. 文字列連結でのバッファオーバーフロー。
    2023年12月16日にSergey Bronnikovにより報告されました。5.3.2から存在していました。githubで修正されました。

    -- seg.faults in 32-bit machines
    local a = 'Name'
    for b = -1000, 0 do
      a = a .. '____________' .. a .. '____________' .. a .. a .. '____________' 
    end
    
  7. フック内での中断は、関数を間違った命令で停止させます。
    2023年11月22日に无名字により報告されました。5.3.0(少なくとも)から存在していました。githubで修正されました。

    *** yieldhook.c:
    #include "lua.h"
    #include "lauxlib.h"
    #include "string.h"
    
    static void hookf (lua_State *L, lua_Debug *ar) {
        if (ar->currentline == 6)
          lua_yield(L, 0);
    }
    
    static int db_sethook (lua_State *L) {
      lua_sethook(L, hookf, LUA_MASKLINE, 0);
      return 0;
    }
    
    int luaopen_yieldhook(lua_State *L) {
          static luaL_Reg l[] = {
                  { "sethook", db_sethook },
                  { NULL, NULL },
          };
          luaL_checkversion(L);
          luaL_newlib(L, l);
      return 1;
    }
    
    *** test.lua:
    local yieldhook = require 'yieldhook'
    local function test()
        local a1 = 1
        local a2 = 2
        local a3 = 3
        local a4 = 4
        return a1 + a2 + a3 + a4
    end
    
    local function run()
        yieldhook.sethook(coroutine.running())
        return test()
    end
    
    local co = coroutine.create(run)
    coroutine.resume(co)
    local name
    local i = 1
    for i = 1, 20 do
        local name, value =  debug.getlocal(co, 0, i)
        if name == nil then break end
        i = i + 1
        print(name, value)
    end
    
    result:
    a1      1
    a2      2
    --a3    3   is lost
    

Lua 5.4.5 1

  1. 'lua_resetthread'のシグネチャを変更すると、ABIが破損しました。
    2023年4月29日にAndrew Gierthにより報告されました。5.4.6で修正されました。githubで修正されました。

Lua 5.4.4 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11

  1. lua.cは、argvに少なくとも1つの要素があることを前提としています。
    2022年1月27日にDoubleFにより報告されました。5.0(少なくとも)から存在していました。githubで修正されました。
  2. _ENVが<const>の場合、Luaが間違ったコードを生成する可能性があります。
    2022年2月3日にКныжов Никитаにより報告されました。5.4.2から存在していました。githubで修正されました。

    -- Lua compiled with assertions on
    local _ENV <const> = 0
    X=0
    
  3. ビット単位演算での定数のコード生成が間違っています。
    2022年3月30日にbmcqにより報告されました。5.4.0から存在していました。githubで修正されました。

    local _ = {"1","2","3","4","5","6","7","8","9","10","11","12",
    "13","14","15","16","17","18","19","20","21","22","23","24","25","26",
    "27","28","29","30","31","32","33","34","35","36","37","38",
    "39","40","41","42","43","44","45","46","47","48","49","50",
    "51","52","53","54","55","56","57","58","59","60","61","62","63","64",
    "65","66","67","68","69","70","71","72","73","74","75","76","77","78",
    "79","80","81","82","83","84","85","86","87","88","89","90","91","92",
    "93","94","95","96","97","98","99","100","101","102","103","104",
    "105","106","107","108","109","110","111","112","113","114",
    "115","116","117","118","119","120","121","122","123","124",
    "125","126","127","128","129","130","131","132","133","134",
    "135","136","137","138","139","140","141","142","143","144",
    "145","146","147","148","149","150","151","152","153","154",
    "155","156","157","158","159","160","161","162","163","164",
    "165","166","167","168","169","170","171","172","173","174",
    "175","176","177","178","179","180","181","182","183","184",
    "185","186","187","188","189","190","191","192","193","194",
    "195","196","197","198","199","200","201","202","203","204",
    "205","206","207","208","209","210","211","212","213","214",
    "215","216","217","218","219","220","221","222","223","224",
    "225","226","227","228","229","230","231","232","233","234",
    "235","236","237","238","239","240","241","242","243","244",
    "245","246","247","248","249","250","251","252","253","254",
    "255","256", }
    -- should print 3, but prints 2
    print (1 | (2 or 3))
    
  4. エラー処理中にCスタックがオーバーフローすると、Luaスタックがオーバーフローします。
    2022年5月13日にJinwei Dongにより報告されました。5.4.2から存在していました。githubで修正されました。

    print(
        xpcall((0),
            function(...)
                local f
                if d[print(print(print(print(t[...]))))] then
                end
            end
        )
    )
    
  5. 'lua_settop'は、'luaF_close'によって無効化されたスタックへのポインタを使用する可能性があります。
    2022年5月23日にjun louisにより報告されました。5.4.3から存在していました。githubで修正されました。

    -- (needs test library)
    
    -- reduce stack size
    collectgarbage(); collectgarbage(); collectgarbage()
    
    -- force a stack reallocation
    local function loop (n)
      if n < 400 then loop(n + 1) end
    end
    
    local o = setmetatable({}, {__close = function () loop(0) end})
    
    local script = [[toclose 2; settop 1; return 1]]
    
    assert(T.testC(script, o) == script)
    
  6. 'break'は、'for'ループ内の変数を適切に閉じない場合があります。
    2022年5月18日にXmilia Hermitにより報告されました。5.4.0から存在していました。githubで修正されました。

    local closed = false
    
    local o1 = setmetatable({}, {__close=function() closed = true end})
    
    local function test()
        for k, v in next, {}, nil, o1 do
          local function f() return k end   -- just to use 'k'
          break
        end
      assert(closed)   -- should be closed here
    end
    
    test()
    
  7. 世代別モードでのフルコレクションの後、GCが次のサイクルの適切なターゲットを設定していません。
    2022年7月15日に最萌 小汐により報告されました。5.4.3から存在していました。githubで修正されました。
  8. 'utf8.codes'は、偽の継続バイトでエラーを発生させません。
    2022年9月18日にChristian Ludwigにより報告されました。5.4.0から存在していました。githubで修正されました。

    -- the following code should raise an error
    for pos, cp in utf8.codes('in\xbfvalid') do
      print(pos, cp)
    end
    
  9. コルーチン.closeの深いネストによるCスタックオーバーフロー。
    2022年10月13日にXmilia Hermitにより報告されました。5.4.0から存在していました。githubで修正されました。

    -- Depending on the size of your C stack, you may need to increase the
    -- limit of the loop.
    local coro
    for i = 1, 1e5 do
      local previous = coro
      coro = coroutine.create(function()
        local cc <close> = setmetatable({}, {__close=function()
          if previous then
            assert(coroutine.close(previous))
          end
        end})
        coroutine.yield()
      end)
      assert(coroutine.resume(coro))
    end
    print(coroutine.close(coro))
    
  10. 算術エラーのエラーメッセージの行が間違っています。
    2023年2月5日にYongheng Chenにより報告されました。5.4.0から存在していました。githubで修正されました。

    local a = 0
    local b = 1
    print(b // a)
    --> lua: temp:1: attempt to divide by zero
    
  11. 破損したバイナリファイルをロードすると、セグメンテーションフォールトが発生する可能性があります。
    2023年3月15日にMaik Betkaにより報告されました。5.2から存在していました。githubで修正されました。

    -- Binary files have different formats in different versions. This
    -- example is specific for Lua 5.4.
    local code =
     "\x1b\x4c\x75\x61\x54\x00\x19\x93\x0d\x0a\x1a\x0a\x04\x08\x08\x78\x56\z
      \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x77\x40\x00\x86\x40\z
      \x74\x65\x6d\x70\x81\x81\x01\x00\x02\x82\x48\x00\x02\x00\xc7\x00\x01\z
      \x00\x80\x80\x80\x82\x00\x00\x80\x81\x82\x78\x80\x82\x81\x86\x40\x74\z
      \x65\x6d\x70"
    
    assert(load(code))
    

Lua 5.4.3 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 11 · 10 · 11 · 12

  1. 「for」ループで閉じる変数へのテールコールが回避されていません。
    2021年3月31日にXmilia Hermitにより報告されました。5.4.3から存在していました。githubで修正されました。

    local function iter ()
      return function () return true end, 0, 0,
             setmetatable({}, {__close = print})
    end
    
    local function tail() print("tail") end
    
    local function foo ()
      for k in iter() do return tail() end
    end
    
    foo()
    
  2. C99の一行コメントはC89と互換性がありません。
    2021年4月9日にOlexa Bilaniukにより報告されました。5.4.3から存在していました。githubで修正されました。

    パッチ

    lvm.c:
    @@ -1156,8 +1156,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
         Instruction i;  /* instruction being executed */
         StkId ra;  /* instruction's A register */
         vmfetch();
    -// low-level line tracing for debugging Lua
    -// printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
    +    #if 0
    +      /* low-level line tracing for debugging Lua */
    +      printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
    +    #endif
         lua_assert(base == ci->func + 1);
         lua_assert(base <= L->top && L->top < L->stack_last);
         /* invalidate top for instructions not expecting it */
    
  3. varargの結果を返すときに呼び出された__closeメタメソッドでの中断により、返される値が混乱します。
    2021年4月7日にXmilia Hermitにより報告されました。5.4.3から存在していました。githubで修正されました。

    -- The final 'print' should print nothing, as the 'print' inside 'test'
    -- returns nothing.
    local function test()
        local x <close> = setmetatable({}, {
            __close = coroutine.yield
        })
        return print("Return")
    end
    
    local c = coroutine.wrap(test)
    c()          -- runs until '__close'
    print(c())   -- runs until end
    
    

    パッチ

    lvm.c:
    @@ -847,10 +847,19 @@ void luaV_finishOp (lua_State *L) {
           luaV_concat(L, total);  /* concat them (may yield again) */
           break;
         }
    -    case OP_CLOSE:  case OP_RETURN: {  /* yielded closing variables */
    +    case OP_CLOSE: {  /* yielded closing variables */
           ci->u.l.savedpc--;  /* repeat instruction to close other vars. */
           break;
         }
    +    case OP_RETURN: {  /* yielded closing variables */
    +      StkId ra = base + GETARG_A(inst);
    +      /* correct top to signal correct number of returns (in case the
    +         return is "in top" */
    +      L->top = ra + ci->u2.nres;
    +      /* repeat instruction to close other vars. and complete the return */
    +      ci->u.l.savedpc--;
    +      break;
    +    }
         default: {
           /* only these other opcodes can yield */
           lua_assert(op == OP_TFORCALL || op == OP_CALL ||
    @@ -1672,6 +1681,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
               n = cast_int(L->top - ra);  /* get what is available */
             savepc(ci);
             if (TESTARG_k(i)) {  /* may there be open upvalues? */
    +          ci->u2.nres = n;  /* save number of returns */
               if (L->top < ci->top)
                 L->top = ci->top;
               luaF_close(L, base, CLOSEKTOP, 1);
    
  4. 関数ステートメント構文では、名前が定数であるかどうかを確認しません。
    2021年6月16日にHalalaluyafail3により報告されました。5.4.0から存在していました。githubで修正されました。

    local x<const> = {}
    function x() end    -- should raise an error
    print(x)
    
  5. 'luaL_tolstring'は負のインデックスで混乱する可能性があります。
    2021年7月22日にXmilia Hermitにより報告されました。5.4.0から存在していました。githubで修正されました。

    -- (must be run in interactive mode)
    -- Both prints should show the same result
    > debug.debug()
    lua_debug> x = setmetatable({}, {__name="TABLE"})
    lua_debug> print(x)
    lua_debug> error(x)
    

    パッチ

    lauxlib.c:
    @@ -881,6 +881,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
     
     
     LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
    +  idx = lua_absindex(L,idx);
       if (luaL_callmeta(L, idx, "__tostring")) {  /* metafield? */
         if (!lua_isstring(L, -1))
           luaL_error(L, "'__tostring' must return a string");
    
  6. マクロ'luaV_shiftr'での否定がオーバーフローする可能性があります。
    2021年7月22日にユーザー673679により報告されました。5.3-alphaから存在していました。githubで修正されました。

    -- Lua compiled in gcc with option '-fsanitize=undefined'
    print(1 >> math.mininteger)
    

    パッチ

    lvm.c:
    @@ -766,7 +766,7 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
     /*
     ** Shift left operation. (Shift right just negates 'y'.)
     */
    -#define luaV_shiftr(x,y)       luaV_shiftl(x,-(y))
    +#define luaV_shiftr(x,y)       luaV_shiftl(x,intop(-, 0, y))
     
     lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
       if (y < 0) {  /* shift right? */
    
  7. 保護されたエラー後に実行を継続するとき、'coroutine.resume'はC呼び出しのカウンターをインクリメントしません。
    2021年10月29日にJihoi Kimにより報告されました。5.4.2から存在していました。githubで修正されました。

    local function func()
      pcall(1)
      coroutine.wrap(func)()
    end
    func()
    
  8. リセット中に変数を閉じている間、コルーチンのステータスが間違っています。
    2021年10月30日にMinSeok Kangにより報告されました。5.4.1から存在していました。githubで修正されました。

    -- Must be run with assertions on, valgrind, or sanitizer
    co = coroutine.wrap(
      function()
        local x <close> = setmetatable(
          {}, {__close = function() pcall(co) end}
        )
        error()
      end
    )
    co()
    
  9. 状態を閉じるときにLuaスタックがアクティブなままです。
    2021年11月30日にKang woosunにより報告されました。5.4.3から存在していました。githubで修正されました。

    -- The following chunk may segfault
    function v (...)
      return os.exit(0, true)
    end
    
    local x <close> = setmetatable({}, {__close = error})
    
    v()
    
  10. ファイナライザはGCを呼び出すことができないようにする必要があります。
    2021年11月29日に김지회により報告されました。5.4.0から存在していました。githubで修正されました。

    function func1 ()
      local f = setmetatable({}, {__gc = function () collectgarbage("step") end})
      collectgarbage("step" , 1)
    end
    
    
    for i = 0,1000 do
      setmetatable({}, {__gc = func1})
    end
    
  11. ファイナライザは無効なスタックで呼び出される可能性があります。
    2021年12月6日にMinseok Kangにより報告されました。5.4.3から存在していました。githubで修正されました。

    local x = {}; for i=1, 2000 do x[i] = i end
    
    local function f()  end
    
    local function g() return f(table.unpack(x)) end
    
    collectgarbage("step")
    setmetatable({}, {__gc = 1})
    
    g()
    
  12. ファイナライザーでos.exitを呼び出すと、ファイナライズの順序が破損する可能性があります。
    2021年12月21日にXmilia Hermitにより報告されました。5.2から存在していました。githubで修正されました。

    -- The following code illustrates the problem. If finalizer 3 calls
    -- a function from a dynamically loaded C module, the C module
    -- will be closed by the time the function is called, generating
    -- a seg. fault.
    
    -- should be called last
    print("creating 1")
    setmetatable({}, {__gc = function () print(1) end})
    
    print("creating 2")
    setmetatable({}, {__gc = function ()
      print("2")
      print("creating 3")
      setmetatable({}, {__gc = function () print(3) end})
      os.exit(1, true)
    end})
    
    

Lua 5.4.2 1 · 2 · 3 · 4

  1. 'table.sort'は部分順序では機能しません。
    2021年1月4日にEgor Skriptunofにより報告されました。5.3から存在していました。githubで修正されました。

    nan = 0/0
    t = {nan, nan, 20, 10}
    table.sort(t)
    print(table.concat(t, ", "))
      --> -nan, 20, -nan, 10
    

    パッチ:マニュアルは誤解を招きやすいです。ソート関数が部分順序を定義することは必要ですが、十分ではありません。

  2. 'debug.getinfo'のパラメータ'what'は'>'で始めることはできません。
    2021年2月1日にXmilia Hermitにより報告されました。5.1から存在していました。githubで修正されました。

    -- with Lua compiled with option LUA_USE_APICHECK
    debug.getinfo(0, ">")
    

    パッチ

    ldblib.c:
    @@ -152,6 +152,7 @@ static int db_getinfo (lua_State *L) {
       lua_State *L1 = getthread(L, &arg);
       const char *options = luaL_optstring(L, arg+2, "flnSrtu");
       checkstack(L, L1, 3);
    +  luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
       if (lua_isfunction(L, arg + 1)) {  /* info about a function? */
         options = lua_pushfstring(L, ">%s", options);  /* add '>' to 'options' */
         lua_pushvalue(L, arg + 1);  /* move function to 'L1' stack */
    
  3. 'string.concat'のエラーメッセージで間違った形式が使用されています。
    2021年2月14日にno-nとAndrew Gierthにより報告されました。5.3.0から存在していました。githubで修正されました。

    -- the following call gives an error message with a wrong index
    table.concat({}, "", math.maxinteger, math.maxinteger)
    

    パッチ

    ltablib.c:
    @@ -146,7 +146,7 @@ static int tmove (lua_State *L) {
     static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
       lua_geti(L, 1, i);
       if (!lua_isstring(L, -1))
    -    luaL_error(L, "invalid value (%s) at index %d in table for 'concat'",
    +    luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
                       luaL_typename(L, -1), i);
       luaL_addvalue(b);
     }
    
  4. 'isinstack'は、未定義の動作を回避できると誤って想定しています。
    2021年2月21日にYongheng Chenにより報告されました。5.3.0から存在していました。githubで修正されました。

    例: このバグは、フラットメモリアーキテクチャではおそらく障害を引き起こさないでしょう。gccオプション'-fsanitize=pointer-subtract' (およびそれが動作するために必要なもの) を付けてLuaをコンパイルし、次のコードを実行することで、強制的に発生させることができます。

    print(setmetatable({}, {__index = 4}).x)
    

Lua 5.4.1 1

  1. トラバース中にテーブルから削除されたキーが 'next' によって受け入れられない場合があります。
    2020年10月11日にXmilia Hermitによって報告されました。5.4.0から存在していました。5.4.2で修正されました。githubで修正されました。

    t = {}
    t["no" .. "ref1"] = 1
    t["no" .. "ref2"] = 2
    
    for k, v in pairs(t) do
        t[k] = nil
        print(k, v)
        collectgarbage("collect")
    end
    

Lua 5.4.0 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12 · 13

  1. 古いファイナライズされたオブジェクトがGCによって訪問されない場合があります。
    2020年7月6日にYongheng Chenによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    -- run this code with some memory checker, such as valgrind
    -- or gcc's option -fsanitize=address
    local A = {}
    A[1] = false     -- create an old anchor for an object
    
    -- obj finalizer
    local function gcf (obj)
      A[1] = obj     -- anchor object
      obj = nil      -- remove it from the stack
      collectgarbage("step", 0)   -- do a young collection
      print(getmetatable(A[1]).x)   -- metatable was collected!
    end
    
    collectgarbage()   -- make A old
    local obj = {}     -- create a new object
    collectgarbage("step", 0)   -- make it a survival
    setmetatable(obj, {__gc = gcf})   -- create its metatable
    obj = nil   -- clear object
    collectgarbage("step", 0)   -- will call obj's finalizer
    

    パッチ

    lgc.c:
    @@ -1140,7 +1140,7 @@ static void finishgencycle (lua_State *L, global_State *g) {
     static void youngcollection (lua_State *L, global_State *g) {
       GCObject **psurvival;  /* to point to first non-dead survival object */
       lua_assert(g->gcstate == GCSpropagate);
    -  markold(g, g->survival, g->reallyold);
    +  markold(g, g->allgc, g->reallyold);
       markold(g, g->finobj, g->finobjrold);
       atomic(L);
     
    
  2. コルーチンに入る際のスタック制限の計算が間違っています。
    2020年7月6日にYongheng Chenによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    local lim = 1000
    local function stack (n)
      if n > 0 then return stack(n - 1) + 1
      else coroutine.wrap(function ()
             stack(lim)
           end)()
      end
    end
    
    print(xpcall(stack, function () return "ok" end, lim))
    

    パッチ

    ldo.c:
    @@ -674,7 +674,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
       if (from == NULL)
         L->nCcalls = CSTACKTHREAD;
       else  /* correct 'nCcalls' for this thread */
    -    L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF;
    +    L->nCcalls = getCcalls(from) - L->nci - CSTACKCF;
       if (L->nCcalls <= CSTACKERR)
         return resume_error(L, "C stack overflow", nargs);
       luai_userstateresume(L, nargs);
    
  3. 関数のアップ値をロード中にエラーを処理する際、緊急コレクションがセグメンテーション違反を引き起こす可能性があります。
    2020年6月30日にRobertoによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    -- must compile Lua with option -DHARDMEMTESTS, to force
    -- emergency collections
    local s = string.dump(function ()
      local x, y, z
      return function () return x + y + z end
    end)
    
    for i = 1, #s - 1 do
      assert(not load(string.sub(s, 1, i)))
    end
    

    パッチ

    lundump.c:
    @@ -205,8 +205,9 @@ static void loadUpvalues (LoadState *S, Proto *f) {
       n = loadInt(S);
       f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
       f->sizeupvalues = n;
    -  for (i = 0; i < n; i++) {
    +  for (i = 0; i < n; i++)
         f->upvalues[i].name = NULL;
    +  for (i = 0; i < n; i++) {
         f->upvalues[i].instack = loadByte(S);
         f->upvalues[i].idx = loadByte(S);
         f->upvalues[i].kind = loadByte(S);
    
  4. 'checkstackp' がGCステップを実行し、事前に割り当てられたCallInfoを破壊する可能性があります。
    2020年7月6日にYongheng Chenによって報告されました。5.4.1で修正されました。githubで修正されました。

    例: http://lua-users.org/lists/lua-l/2020-07/msg00053.htmlを参照してください。

    パッチ

    ldo.c:
    @@ -466,13 +466,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
           f = fvalue(s2v(func));
          Cfunc: {
           int n;  /* number of returns */
    -      CallInfo *ci = next_ci(L);
    +      CallInfo *ci;
           checkstackp(L, LUA_MINSTACK, func);  /* ensure minimum stack size */
    +      L->ci = ci = next_ci(L);
           ci->nresults = nresults;
           ci->callstatus = CIST_C;
           ci->top = L->top + LUA_MINSTACK;
           ci->func = func;
    -      L->ci = ci;
           lua_assert(ci->top <= L->stack_last);
           if (L->hookmask & LUA_MASKCALL) {
             int narg = cast_int(L->top - func) - 1;
    @@ -486,18 +486,18 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
           break;
         }
         case LUA_VLCL: {  /* Lua function */
    -      CallInfo *ci = next_ci(L);
    +      CallInfo *ci;
           Proto *p = clLvalue(s2v(func))->p;
           int narg = cast_int(L->top - func) - 1;  /* number of real arguments */
           int nfixparams = p->numparams;
           int fsize = p->maxstacksize;  /* frame size */
           checkstackp(L, fsize, func);
    +      L->ci = ci = next_ci(L);
           ci->nresults = nresults;
           ci->u.l.savedpc = p->code;  /* starting point */
           ci->callstatus = 0;
           ci->top = func + 1 + fsize;
           ci->func = func;
    -      L->ci = ci;
           for (; narg < nfixparams; narg++)
             setnilvalue(s2v(L->top++));  /* complete missing arguments */
           lua_assert(ci->top <= L->stack_last);
    
  5. スタックのリサイズ後のGCが再度縮小する可能性があります。
    2020年7月6日にYongheng Chenによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    例: http://lua-users.org/lists/lua-l/2020-07/msg00054.htmlを参照してください。

    パッチ

    ldo.h:
    @@ -44,7 +44,7 @@
     
     /* macro to check stack size and GC */
     #define checkstackGC(L,fsize)  \
    -       luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L))
    +       luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
     
     
     /* type of protected functions, to be ran by 'runprotected' */
    
  6. ファイナライザのエラーには、エラーメッセージを生成するための有効な 'pc' が必要です。
    2020年7月6日にRui Zhongによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    例: http://lua-users.org/lists/lua-l/2020-07/msg00078.html

    パッチ

    lvm.c:
    @@ -1104,7 +1104,7 @@ void luaV_finishOp (lua_State *L) {
     
     
     #define checkGC(L,c)  \
    -	{ luaC_condGC(L, L->top = (c),  /* limit of live values */ \
    +	{ luaC_condGC(L, (savepc(L), L->top = (c)), \
                              updatetrap(ci)); \
                luai_threadyield(L); }
     
    @@ -1792,8 +1792,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
             vmbreak;
           }
           vmcase(OP_VARARGPREP) {
    -        luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p);
    -        updatetrap(ci);
    +        ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
             if (trap) {
               luaD_hookcall(L, ci);
               L->oldpc = pc + 1;  /* next opcode will be seen as a "new" line */
    
  7. 'popen' は無効なモードで呼び出されるとクラッシュする可能性があります。
    2020年7月6日にViacheslav Usovによって報告されました。(少なくとも) 5.1から存在していました。5.4.1で修正されました。githubで修正されました。

    例: (システム依存)

    パッチ

    liolib.c:
    @@ -279,6 +279,8 @@ static int io_popen (lua_State *L) {
       const char *filename = luaL_checkstring(L, 1);
       const char *mode = luaL_optstring(L, 2, "r");
       LStream *p = newprefile(L);
    +  luaL_argcheck(L, ((mode[0] == 'r' || mode[0] == 'w') && mode[1] == '\0'),
    +                   2, "invalid mode");
       p->f = l_popen(L, filename, mode);
       p->closef = &io_pclose;
       return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
    
  8. 関数に戻るときにフィールド 'L->oldpc' が常に更新されるわけではありません。
    2020年7月9日にYongheng Chenによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    -- run this code under valgrind. (Error depends on details of dynamic
    -- addresses.)
    function foo ()
      local f = load[[io.write('+');
                      for i = 1, 10000 do local a = {}; debug.sethook(nil) end
                      io.write'-']]
    
      local u = setmetatable({},
        {__gc = assert(load[[debug.sethook(print, "l");
                      error('err');
                      ]])})
    
      u = nil
      f()
    end
    
    for i = 1, 200 do
      foo()
    end
    

    パッチ

    lgc.c:
    @@ -856,6 +856,8 @@ static void GCTM (lua_State *L) {
         if (unlikely(status != LUA_OK)) {  /* error while running __gc? */
           luaE_warnerror(L, "__gc metamethod");
           L->top--;  /* pops error object */
    +      if (isLua(L->ci))
    +        L->oldpc = L->ci->u.l.savedpc;  /* update 'oldpc' */
         }
       }
     }
    
  9. Cスタックオーバーフロー (再び)。
    2020年7月15日にYongheng Chenによって報告されました。5.4.1で修正されました。githubで修正されました。

    function errfunc ()
      return 10 + xpcall(nil, errfunc)
    end
    
    coroutine.resume(coroutine.create(function() xpcall(nil, errfunc) end))
    
  10. バリアは、ジェネレーショナルモードであっても、スイープフェーズ中はアクティブにできません。
    2020年7月15日にYongheng Chenによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    -- The following chunk, under a memory checker like valgrind,
    -- produces a memory access violation.
    local old = {10}
    collectgarbage()   -- make 'old' old
    local co = coroutine.create(
      function ()
        local x = nil
        local f = function ()
                    return x[1]
                  end
        x = coroutine.yield(f)
        coroutine.yield()
      end
    )
    local _, f = coroutine.resume(co)     -- create closure over x in thread
    collectgarbage("step", 0)   -- make upvalue a survival
    old[1] = {"hello"}    -- 'old' go to grayagain as 'touched1'
    coroutine.resume(co, {123})     -- its value will be new
    co = nil
    -- next minor collection hits the barrier between upvalue and its
    -- conent while sweeping the thread. This will mix the lists 'gray'
    -- and 'grayagain' and will remove 'old' from 'grayagain'.
    collectgarbage("step", 0)
    assert(f() == 123 and old[1][1] == "hello")   -- still ok
    collectgarbage("step", 0)   -- run the collector once more
    -- now, as 'old' was not in 'grayagain', 'old[1]' was deleted
    assert(f() == 123 and old[1][1] == "hello")
    
  11. getlocal/setlocalでの否定オーバーフロー。
    2020年7月24日にYongheng Chenによって報告されました。5.2から存在していました。5.4.1で修正されました。githubで修正されました。

    print(debug.getlocal(1, 2^31))
  12. ストリップされた関数のラインフックでのデバッグ情報へのアクセス。
    2020年7月24日にYongheng Chenによって報告されました。5.4.0から存在していました。5.4.1で修正されました。githubで修正されました。

    local function foo ()
       local a = 1
       local b = 2
       local c = 3
    end
    
    local s = load(string.dump(foo, true))
    local line
    debug.sethook(function (e, l) line = l end, "l"); s(); debug.sethook(nil)
    print(line)
    
  13. バイナリファイルのロード中に、その内容が読み込まれている間、長い文字列が収集される可能性があります。
    2020年8月15日にPayo Nelによって報告されました。5.3から存在していました。5.4.1で修正されました。githubで修正されました。

    -- run this code under some memory checker
    local function myload (s)
      return load(function ()
        if s == "" then return nil
        else
          local c = string.sub(s, 1, 1)
          s = string.sub(s, 2)
          collectgarbage()
          return c
        end
      end)
    end
    
    local y = string.dump(function ()
      return '01234567890123456789012345678901234567890123456789'
    end)
    y = myload(y)
    assert(y() == '01234567890123456789012345678901234567890123456789')
    

Lua 5.3.6

これはLua 5.3の最後のリリースでした。後で報告されたバグは、おそらくLua 5.4で修正されています。

Lua 5.3.5 1 · 2 · 3 · 4

  1. FreeBSDレシピで 'LUA_USE_READLINE' が2回定義されています。
    2018年7月13日にRussell Haleyによって報告されました。

    パッチ

    src/Makefile:
    @@ -104,2 +104,2 @@
     freebsd:
    -	$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"
    +	$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"
    
  2. Makefileでリビジョン番号が更新されていません。
    2018年8月8日にmillyによって報告されました。

    パッチ

    Makefile:
    @@ -49 +49 @@
    -R= $V.4
    +R= $V.5
    
  3. 多数の '=' を持つ長いブラケットは、一部の内部バッファ演算をオーバーフローさせます。
    2018年12月12日にMarcoによって報告されました。5.1から存在していました。

    local eqs = string.rep("=", 0x3ffffffe)
    local code = "return [" .. eqs .. "[a]" .. eqs .. "]"
    print(#assert(load(code))())
    
  4. アップ値を自分自身と結合すると、use-after-freeクラッシュが発生する可能性があります。
    2019年1月10日にFady Othmanによって報告されました。5.3から存在していました。

    -- the next code may crash the machine
    f=load(function() end)
    interesting={}
    interesting[0]=string.rep("A",512)
    debug.upvaluejoin(f,1,f,1)
    

    パッチ

    lapi.c:
    @@ -1289,6 +1289,8 @@
    LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
       LClosure *f1;
       UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
       UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
    +  if (*up1 == *up2)
    +    return;
       luaC_upvdeccount(L, *up1);
       *up1 = *up2;
       (*up1)->refcount++;
    

Lua 5.3.4 1 · 2 · 3 · 4 · 5 · 6 · 7

  1. 'if' 内のラベルに続く 'goto' に対して間違ったコードが生成されます。
    2017年4月13日に云风によって報告されました。5.2から存在していました。

    -- should print 32323232..., but prints only '3'
    if true then
      goto LBL
      ::loop::
      print(2)
      ::LBL::
      print(3)
      goto loop
    end
    

    パッチ

    lparser.c:
    @@ -1392,7 +1392,7 @@
         luaK_goiffalse(ls->fs, &v);  /* will jump to label if condition is true */
         enterblock(fs, &bl, 0);  /* must enter block before 'goto' */
         gotostat(ls, v.t);  /* handle goto/break */
    -    skipnoopstat(ls);  /* skip other no-op statements */
    +    while (testnext(ls, ';')) {}  /* skip semicolons */
         if (block_follow(ls, 0)) {  /* 'goto' is the entire block? */
           leaveblock(fs);
           return;  /* and that is it */
    
  2. Luaは2^30を超える要素を持つシーケンスを構築する際にクラッシュします。
    2017年5月11日にViacheslav Usovによって報告されました。

    -- crashes if machine has enough memory
    local t = {}
    for i = 1, 0x7fffffff do
      t[i] = i
    end
    

    パッチ

    ltable.c:
    @@ -223,7 +223,9 @@
       unsigned int na = 0;  /* number of elements to go to array part */
       unsigned int optimal = 0;  /* optimal size for array part */
       /* loop while keys can fill more than half of total size */
    -  for (i = 0, twotoi = 1; *pna > twotoi / 2; i++, twotoi *= 2) {
    +  for (i = 0, twotoi = 1;
    +       twotoi > 0 && *pna > twotoi / 2;
    +       i++, twotoi *= 2) {
         if (nums[i] > 0) {
           a += nums[i];
           if (a > twotoi/2) {  /* more than half elements present? */
    
  3. テーブル長の計算が、2^31を超える要素を持つシーケンスでオーバーフローします。
    2017年5月12日にViacheslav Usovによって報告されました。

    -- on a machine with enough memory
    local t = {}
    for i = 1, 2147483681 do
      t[i] = i
    end
    print(#t)
    

    パッチ

    ltable.h:
    @@ -56,3 +56,3 @@
     LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
    -LUAI_FUNC int luaH_getn (Table *t);
    +LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
     
    ltable.c:
    @@ -614,4 +614,4 @@
     
    -static int unbound_search (Table *t, unsigned int j) {
    -  unsigned int i = j;  /* i is zero or a present index */
    +static lua_Unsigned unbound_search (Table *t, lua_Unsigned j) {
    +  lua_Unsigned i = j;  /* i is zero or a present index */
       j++;
    @@ -620,3 +620,3 @@
         i = j;
    -    if (j > cast(unsigned int, MAX_INT)/2) {  /* overflow? */
    +    if (j > l_castS2U(LUA_MAXINTEGER) / 2) {  /* overflow? */
           /* table was built with bad purposes: resort to linear search */
    @@ -630,3 +630,3 @@
       while (j - i > 1) {
    -    unsigned int m = (i+j)/2;
    +    lua_Unsigned m = (i+j)/2;
         if (ttisnil(luaH_getint(t, m))) j = m;
    @@ -642,3 +642,3 @@
     */
    -int luaH_getn (Table *t) {
    +lua_Unsigned luaH_getn (Table *t) {
       unsigned int j = t->sizearray;
    
  4. Luaはエラーメッセージを作成する際にGCをチェックしません。
    2017年7月6日にViacheslav Usovによって報告されました。5.3.2から存在していました。

    function test()
      bob.joe.larry = 23
    end
    
    -- memory will grow steadly
    for i = 1, math.huge do
      pcall(test)
      if i % 100000 == 0 then
        io.write(collectgarbage'count'*1024, "\n")
      end
    end
    

    パッチ

    ldebug.c:
    @@ -653,6 +653,7 @@
       CallInfo *ci = L->ci;
       const char *msg;
       va_list argp;
    +  luaC_checkGC(L);  /* error message uses memory */
       va_start(argp, fmt);
       msg = luaO_pushvfstring(L, fmt, argp);  /* format message */
       va_end(argp);
    
  5. nil値を持つデッドキーが弱テーブルに残る可能性があります。
    2017年8月15日に云风 Cloud Wuによって報告されました。5.2から存在していました。

    -- The following chunk, under a memory checker like valgrind, produces a memory access violation.
    
    local a = setmetatable({}, {__mode = 'kv'})
    
    a['ABCDEFGHIJKLMNOPQRSTUVWXYZ' .. 'abcdefghijklmnopqrstuvwxyz'] = {}
    a[next(a)] = nil
    collectgarbage()
    print(a['BCDEFGHIJKLMNOPQRSTUVWXYZ' .. 'abcdefghijklmnopqrstuvwxyz'])
    

    パッチ

    lgc.c:
    @@ -643,8 +643,9 @@
         for (n = gnode(h, 0); n < limit; n++) {
           if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) {
             setnilvalue(gval(n));  /* remove value ... */
    -        removeentry(n);  /* and remove entry from table */
           }
    +      if (ttisnil(gval(n)))  /* is entry empty? */
    +        removeentry(n);  /* remove entry from table */
         }
       }
     }
    
  6. lua_pushcclosure は、n がゼロの場合、ガベージコレクタを呼び出すべきではありません。
    2017年12月5日にAndrew Gierthによって報告されました。5.3.3から存在していました。

    パッチ

    lapi.c:
    @@ -533,6 +533,7 @@
       lua_lock(L);
       if (n == 0) {
         setfvalue(L->top, fn);
    +    api_incr_top(L);
       }
       else {
         CClosure *cl;
    @@ -546,9 +547,9 @@
           /* does not need barrier because closure is white */
         }
         setclCvalue(L, L->top, cl);
    +    api_incr_top(L);
    +    luaC_checkGC(L);
       }
    -  api_incr_top(L);
    -  luaC_checkGC(L);
       lua_unlock(L);
     }
    
  7. テーブルのリサイズ時のメモリ割り当てエラーは、テーブルを矛盾した状態のままにする可能性があります。
    2017年12月8日にRobertoによって報告されました。5.0から存在していました。

    local a = {x = 1, y = 1, z = 1}
    a[1] = 10   -- goes to the hash part (which has 4 slots)
    print(a[1])   --> 10
    
    -- assume that the 2nd memory allocation from now fails
    pcall(rawset, a, 2, 20)   -- forces a rehash
    
    -- a[1] now exists both in the array part (because the array part
    -- grew) and in the hash part (because the allocation of the hash
    -- part failed, keeping it as it was).
    -- This makes the following traversal goes forever...
    for k,v in pairs(a) do print(k,v) end
    

    パッチ

    ltable.c:
    @@ -332,17 +332,34 @@
     }
     
     
    +typedef struct {
    +  Table *t;
    +  unsigned int nhsize;
    +} AuxsetnodeT;
    +
    +
    +static void auxsetnode (lua_State *L, void *ud) {
    +  AuxsetnodeT *asn = cast(AuxsetnodeT *, ud);
    +  setnodevector(L, asn->t, asn->nhsize);
    +}
    +
    +
     void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
                                               unsigned int nhsize) {
       unsigned int i;
       int j;
    +  AuxsetnodeT asn;
       unsigned int oldasize = t->sizearray;
       int oldhsize = allocsizenode(t);
       Node *nold = t->node;  /* save old hash ... */
       if (nasize > oldasize)  /* array part must grow? */
         setarrayvector(L, t, nasize);
       /* create new hash part with appropriate size */
    -  setnodevector(L, t, nhsize);
    +  asn.t = t; asn.nhsize = nhsize;
    +  if (luaD_rawrunprotected(L, auxsetnode, &asn) != LUA_OK) {  /* mem. error? */
    +    setarrayvector(L, t, oldasize);  /* array back to its original size */
    +    luaD_throw(L, LUA_ERRMEM);  /* rethrow memory error */
    +  }
       if (nasize < oldasize) {  /* array part must shrink? */
         t->sizearray = nasize;
         /* re-insert elements from vanishing slice */
    

Lua 5.3.3 1 · 2 · 3 · 4

  1. 'for'ループで4つ以上の式を持つ式リストは、インタプリタをクラッシュさせる可能性があります。
    2016年6月17日にMarco Schöplによって報告されました。5.2から存在していました。5.3.4で修正されました。

    -- the next loop will probably crash the interpreter
    repeat until load "for _ in _,_,_,_ do local function _() end"
    

    パッチ

    lparser.c:
    @@ -323,6 +323,8 @@
           luaK_nil(fs, reg, extra);
         }
       }
    +  if (nexps > nvars)
    +    ls->fs->freereg -= nexps - nvars;  /* remove extra values */
     }
     
     
    @@ -1160,11 +1162,8 @@
         int nexps;
         checknext(ls, '=');
         nexps = explist(ls, &e);
    -    if (nexps != nvars) {
    +    if (nexps != nvars)
           adjust_assign(ls, nvars, nexps, &e);
    -      if (nexps > nvars)
    -        ls->fs->freereg -= nexps - nvars;  /* remove extra values */
    -    }
         else {
           luaK_setoneret(ls->fs, &e);  /* close last expression */
           luaK_storevar(ls->fs, &lh->v, &e);
    
  2. os.dateの書式をチェックすると、書式文字列を読み飛ばす可能性があります。
    2016年7月10日にNagaev Borisによって報告されました。5.3.3から存在していました。5.3.4で修正されました。

    例: このバグは、通常のコンパイラでは発生しないようです。差が見つかった後にメモリの読み取りを続ける「インターセプター」'memcmp'関数が必要です。

    パッチ

    loslib.c:
    263c263,264
    <   for (option = LUA_STRFTIMEOPTIONS; *option != '\0'; option += oplen) {
    ---
    >   int convlen = (int)strlen(conv);
    >   for (option = LUA_STRFTIMEOPTIONS; *option != '\0' && oplen <= convlen; option += oplen) {
    
  3. Luaは、定数が多すぎる関数で間違ったコードを生成する可能性があります。
    2016年7月17日にMarco Schöplによって報告されました。5.3.3から存在していました。5.3.4で修正されました。

    例: http://lua-users.org/lists/lua-l/2016-07/msg00303.html を参照してください。

    パッチ

    lcode.c:
    @@ -1018,8 +1018,8 @@
     */
     static void codebinexpval (FuncState *fs, OpCode op,
                                expdesc *e1, expdesc *e2, int line) {
    -  int rk1 = luaK_exp2RK(fs, e1);  /* both operands are "RK" */
    -  int rk2 = luaK_exp2RK(fs, e2);
    +  int rk2 = luaK_exp2RK(fs, e2);  /* both operands are "RK" */
    +  int rk1 = luaK_exp2RK(fs, e1);
       freeexps(fs, e1, e2);
       e1->u.info = luaK_codeABC(fs, op, 0, rk1, rk2);  /* generate opcode */
       e1->k = VRELOCABLE;  /* all those operations are relocatable */
    
  4. コルーチンが一時停止されていないコルーチンを再開しようとすると、エラーを検出する前に、いくつかの混乱を引き起こす可能性があります (Cアサーションを壊す)。
    2016年7月20日にMarco Schöplによって報告されました。5.3.4で修正されました。

    -- with C assertions on
    A = coroutine.running()
    B = coroutine.create(function() coroutine.resume(A) end)
    coroutine.resume(B)
    -- or
    A = coroutine.wrap(function() pcall(A, _) end)
    A()
    

Lua 5.3.2 1 · 2 · 3

  1. メタテーブルは、__newindexに自己参照がある場合、自身の割り当て解除されたフィールドにアクセスする可能性があります。
    2016年1月1日にactboy168によって報告されました。5.3.2から存在していました。5.3.3で修正されました。

    local mt = {}
    mt.__newindex = mt
    local t = setmetatable({}, mt)
    t[1] = 1     -- will segfault on some machines
    

    パッチ

    lvm.c:
    @@ -190,18 +190,19 @@
       for (loop = 0; loop < MAXTAGLOOP; loop++) {
         const TValue *tm;
         if (oldval != NULL) {
    -      lua_assert(ttistable(t) && ttisnil(oldval));
    +      Table *h = hvalue(t);  /* save 't' table */
    +      lua_assert(ttisnil(oldval));
           /* must check the metamethod */
    -      if ((tm = fasttm(L, hvalue(t)->metatable, TM_NEWINDEX)) == NULL &&
    +      if ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL &&
              /* no metamethod; is there a previous entry in the table? */
              (oldval != luaO_nilobject ||
              /* no previous entry; must create one. (The next test is
                 always true; we only need the assignment.) */
    -         (oldval = luaH_newkey(L, hvalue(t), key), 1))) {
    +         (oldval = luaH_newkey(L, h, key), 1))) {
             /* no metamethod and (now) there is an entry with given key */
             setobj2t(L, cast(TValue *, oldval), val);
    -        invalidateTMcache(hvalue(t));
    -        luaC_barrierback(L, hvalue(t), val);
    +        invalidateTMcache(h);
    +        luaC_barrierback(L, h, val);
             return;
           }
           /* else will try the metamethod */
    
  2. ローカル定義の間のラベルがそれらの初期化を混同する可能性があります。
    2016年3月1日にKarel Tumaによって報告されました。5.2から存在していました。5.3.3で修正されました。

    do
      local k = 0
      local x
      ::foo::
      local y       -- should be reset to nil after goto, but it is not
      assert(not y)
      y = true
      k = k + 1
      if k < 2 then goto foo end
    end
    

    パッチ

    lparser.c:
    @@ -1226,7 +1226,7 @@
       checkrepeated(fs, ll, label);  /* check for repeated labels */
       checknext(ls, TK_DBCOLON);  /* skip double colon */
       /* create new entry for this label */
    -  l = newlabelentry(ls, ll, label, line, fs->pc);
    +  l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs));
       skipnoopstat(ls);  /* skip other no-op statements */
       if (block_follow(ls, 0)) {  /* label is last no-op statement in the block? */
         /* assume that locals are already out of scope */
    
  3. gmatch イテレータは、それを作成したコルーチンとは異なるコルーチンから呼び出された場合に失敗します。
    2016年3月18日にNagaev Borisによって報告されました。5.3.2から存在していました。5.3.3で修正されました。

    local f = string.gmatch("1 2 3 4 5", "%d+")
    print(f())     --> 1
    co = coroutine.wrap(f)
    print(co())    --> ??? (should be 2)
    

    パッチ

    lstrlib.c:
    @@ -688,6 +688,7 @@
     static int gmatch_aux (lua_State *L) {
       GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3));
       const char *src;
    +  gm->ms.L = L;
       for (src = gm->src; src <= gm->ms.src_end; src++) {
         const char *e;
         reprepstate(&gm->ms);
    

Lua 5.3.1 1

  1. io.lines はオプションの最大数をチェックしません。
    2015年7月10日にPatrick Donnellによって報告されました。5.3.0から存在していました。5.3.2で修正されました。

    -- can crash in some machines
    t ={}; for i = 1, 253 do t[i] = 1 end
    io.lines("someexistingfile", table.unpack(t))()
    

    パッチ

    liolib.c:
    @@ -318,8 +318,15 @@
     static int io_readline (lua_State *L);
     
     
    +/*
    +** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
    +** in the limit for upvalues of a closure)
    +*/
    +#define MAXARGLINE	250
    +
     static void aux_lines (lua_State *L, int toclose) {
       int n = lua_gettop(L) - 1;  /* number of arguments to read */
    +  luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
       lua_pushinteger(L, n);  /* number of arguments to read */
       lua_pushboolean(L, toclose);  /* close/not close file when finished */
       lua_rotate(L, 2, 2);  /* move 'n' and 'toclose' to their positions */
    

Lua 5.3.0 1 · 2 · 3 · 4

  1. string.format("%f") はバッファオーバーフローを引き起こす可能性があります (「lua_Number」がlong doubleの場合のみ)。
    2015年1月13日にRobertoによって報告されました。5.3から存在していました。5.3.1で修正されました。

    string.format("%.99f", 1e4000)    -- when floats are long double
    

    パッチ

    lstrlib.c:
    @@ -800,3 +800,4 @@
     /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
    -#define MAX_ITEM	512
    +#define MAX_ITEM  \
    +  (sizeof(lua_Number) <= 4 ? 150 : sizeof(lua_Number) <= 8 ? 450 : 5050)
     
    
  2. フックで一時停止されたコルーチンで debug.getlocal を使用すると、インタプリタがクラッシュする可能性があります。
    2015年2月11日に云风によって報告されました。5.2から存在していました。5.3.1で修正されました。

    例: http://lua-users.org/lists/lua-l/2015-02/msg00146.html を参照してください。

    パッチ

    ldebug.c:
    @@ -49,4 +49,14 @@
     
     
    +static void swapextra (lua_State *L) {
    +  if (L->status == LUA_YIELD) {
    +    CallInfo *ci = L->ci;  /* get function that yielded */
    +    StkId temp = ci->func;  /* exchange its 'func' and 'extra' values */
    +    ci->func = restorestack(L, ci->extra);
    +    ci->extra = savestack(L, temp);
    +  }
    +}
    +
    +
     /*
     ** this function can be called asynchronous (e.g. during a signal)
    @@ -145,4 +155,5 @@
       const char *name;
       lua_lock(L);
    +  swapextra(L);
       if (ar == NULL) {  /* information about non-active function? */
         if (!isLfunction(L->top - 1))  /* not a Lua function? */
    @@ -159,4 +170,5 @@
         }
       }
    +  swapextra(L);
       lua_unlock(L);
       return name;
    @@ -166,10 +178,13 @@
     LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
       StkId pos = 0;  /* to avoid warnings */
    -  const char *name = findlocal(L, ar->i_ci, n, &pos);
    +  const char *name;
       lua_lock(L);
    +  swapextra(L);
    +  name = findlocal(L, ar->i_ci, n, &pos);
       if (name) {
         setobjs2s(L, pos, L->top - 1);
         L->top--;  /* pop value */
       }
    +  swapextra(L);
       lua_unlock(L);
       return name;
    @@ -271,4 +286,5 @@
       StkId func;
       lua_lock(L);
    +  swapextra(L);
       if (*what == '>') {
         ci = NULL;
    @@ -289,4 +305,5 @@
         api_incr_top(L);
       }
    +  swapextra(L);
       if (strchr(what, 'L'))
         collectvalidlines(L, cl);
    
  3. 一時停止された__leメタメソッドが誤った結果を与える可能性があります。
    2015年4月7日にEric Zhongによって報告されました。5.2から存在していました。5.3.1で修正されました。

    mt = {__le = function (a,b) coroutine.yield("yield"); return a.x <= b.x end}
    t1 = setmetatable({x=1}, mt)
    t2 = {x=2}
    co = coroutine.wrap(function (a,b) return t2 <= t1 end)
    co()
    print(co())   --> true  (should be false)
    

    パッチ

    lstate.h:
    @@ -94,6 +94,7 @@
     #define CIST_YPCALL	(1<<4)	/* call is a yieldable protected call */
     #define CIST_TAIL	(1<<5)	/* call was tail called */
     #define CIST_HOOKYIELD	(1<<6)	/* last hook called yielded */
    +#define CIST_LEQ	(1<<7)  /* using __lt for __le */
     
     #define isLua(ci)	((ci)->callstatus & CIST_LUA)
     
    lvm.c:
    @@ -292,9 +292,14 @@
         return l_strcmp(tsvalue(l), tsvalue(r)) <= 0;
       else if ((res = luaT_callorderTM(L, l, r, TM_LE)) >= 0)  /* first try 'le' */
         return res;
    -  else if ((res = luaT_callorderTM(L, r, l, TM_LT)) < 0)  /* else try 'lt' */
    -    luaG_ordererror(L, l, r);
    -  return !res;
    +  else {  /* try 'lt': */
    +    L->ci->callstatus |= CIST_LEQ;  /* mark it is doing 'lt' for 'le' */
    +    res = luaT_callorderTM(L, r, l, TM_LT);
    +    L->ci->callstatus ^= CIST_LEQ;  /* clear mark */
    +    if (res < 0)
    +      luaG_ordererror(L, l, r);
    +    return !res;  /* result is negated */
    +  }
     }
     
     
    @@ -553,11 +558,11 @@
         case OP_LE: case OP_LT: case OP_EQ: {
           int res = !l_isfalse(L->top - 1);
           L->top--;
    -      /* metamethod should not be called when operand is K */
    -      lua_assert(!ISK(GETARG_B(inst)));
    -      if (op == OP_LE &&  /* "<=" using "<" instead? */
    -          ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE)))
    -        res = !res;  /* invert result */
    +      if (ci->callstatus & CIST_LEQ) {  /* "<=" using "<" instead? */
    +        lua_assert(op == OP_LE);
    +        ci->callstatus ^= CIST_LEQ;  /* clear mark */
    +        res = !res;  /* negate result */
    +      }
           lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
           if (res != GETARG_A(inst))  /* condition failed? */
             ci->u.l.savedpc++;  /* skip jump instruction */
    
  4. 関数が戻るとき、リターンフックはアクティブなローカル変数の正しい値を認識できない場合があります。
    2015年5月19日にPhilipp JandaとPeng Yiによって報告されました。5.0から存在していました。5.3.1で修正されました。

    例: http://lua-users.org/lists/lua-l/2015-05/msg00376.html を参照してください。

Lua 5.2.4

これはLua 5.2の最後のリリースでした。後で報告されたバグは、おそらくLua 5.3で修正されています。

Lua 5.2.3 1 · 2 · 3

  1. コンパイラは、table.unpack でのオーバーフローチェックを最適化によって削除する可能性があります。
    2014年3月30日にPaige DePolによって報告されました。5.1から存在していました。5.3.0と5.2.4で修正されました。

    unpack({}, 0, 2^31 - 1) -- crashes on some platforms with some compiler options
    

    パッチ

    ltablib.c:
    @@ -134,13 +135,14 @@
     
     
     static int unpack (lua_State *L) {
    -  int i, e, n;
    +  int i, e;
    +  unsigned int n;
       luaL_checktype(L, 1, LUA_TTABLE);
       i = luaL_optint(L, 2, 1);
       e = luaL_opt(L, luaL_checkint, 3, luaL_len(L, 1));
       if (i > e) return 0;  /* empty range */
    -  n = e - i + 1;  /* number of elements */
    -  if (n <= 0 || !lua_checkstack(L, n))  /* n <= 0 means arith. overflow */
    +  n = (unsigned int)e - (unsigned int)i;  /* number of elements minus 1 */
    +  if (n > (INT_MAX - 10) || !lua_checkstack(L, ++n))
         return luaL_error(L, "too many results to unpack");
       lua_rawgeti(L, 1, i);  /* push arg[i] (avoiding overflow problems) */
       while (i++ < e)  /* push arg[i + 1...e] */
    
  2. エフェメロンテーブルが誤ってストロングキーを持つエントリを収集する可能性があります。
    2014年8月22日にJörg Richterによって報告されました。5.2.0から存在していました。5.3.0と5.2.4で修正されました。

    例: このバグは、インクリメンタルコレクタとプログラム間の特定のイベントのインターリーブに依存するため、非常に再現が難しいです。

    パッチ

    lgc.c:
    @@ -403,7 +403,7 @@
           reallymarkobject(g, gcvalue(gval(n)));  /* mark it now */
         }
       }
    -  if (prop)
    +  if (g->gcstate != GCSatomic || prop)
         linktable(h, &g->ephemeron);  /* have to propagate again */
       else if (hasclears)  /* does table have white keys? */
         linktable(h, &g->allweak);  /* may have to clean white keys */
    
  3. 行が多すぎるチャンクはLuaをクラッシュさせる可能性があります。
    2014年11月14日にRobertoによって報告されました。少なくとも5.1から存在していました。5.3.0と5.2.4で修正されました。

    例: バグの原因は初期化されていない変数の使用であるため、確実に再現することはできません。

    local s = string.rep("\n", 2^24)
    print(load(function () return s end))
    

    パッチ

    llex.c:
    @@ -153,5 +153,5 @@
         next(ls);  /* skip `\n\r' or `\r\n' */
       if (++ls->linenumber >= MAX_INT)
    -    luaX_syntaxerror(ls, "chunk has too many lines");
    +    lexerror(ls, "chunk has too many lines", 0);
     }
     
    

Lua 5.2.2 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8

  1. 引数が少ない状態で呼び出された、固定パラメータが多い可変長引数関数のスタックオーバーフロー。
    2013年4月17日に云风によって報告されました。5.1から存在していました。5.2.3で修正されました。

    function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
               p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
               p21, p22, p23, p24, p25, p26, p27, p28, p29, p30,
               p31, p32, p33, p34, p35, p36, p37, p38, p39, p40,
               p41, p42, p43, p44, p45, p46, p48, p49, p50, ...)
      local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14
    end
    
    f()   -- crashes on some machines
    

    パッチ

    ldo.c:
    @@ -324,7 +324,7 @@
         case LUA_TLCL: {  /* Lua function: prepare its call */
           StkId base;
           Proto *p = clLvalue(func)->p;
    -      luaD_checkstack(L, p->maxstacksize);
    +      luaD_checkstack(L, p->maxstacksize + p->numparams);
           func = restorestack(L, funcr);
           n = cast_int(L->top - func) - 1;  /* number of real arguments */
           for (; n < p->numparams; n++)
    
  2. ガベージコレクタが再帰ループで過度に頻繁にトリガーする可能性があります。
    2013年4月25日にRobertoによって報告されました。5.2.2から存在していました。5.2.3で修正されました。

    function f() f() end
    f()   -- it takes too long before a "stack overflow" error
    

    パッチ

    lgc.c:
    @@ -495,2 +495,3 @@
     static lu_mem traversestack (global_State *g, lua_State *th) {
    +  int n = 0;
       StkId o = th->stack;
    @@ -505,3 +506,9 @@
       }
    -  return sizeof(lua_State) + sizeof(TValue) * th->stacksize;
    +  else {  /* count call infos to compute size */
    +    CallInfo *ci;
    +    for (ci = &th->base_ci; ci != th->ci; ci = ci->next)
    +      n++;
    +  }
    +  return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
    +         sizeof(CallInfo) * n;
     }
    
  3. 連結エラーを報告するときの誤ったアサート (Luaがデバッグモードでコンパイルされている場合にのみ現れます)。
    2013年5月5日にRobertoによって報告されました。5.2.0から存在していました。5.2.3で修正されました。

    -- only with Lua compiled in debug mode
    print({} .. 2)
    

    パッチ

    ldebug.c:
    @@ -519,5 +519,5 @@
     l_noret luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
       if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
    -  lua_assert(!ttisstring(p1) && !ttisnumber(p2));
    +  lua_assert(!ttisstring(p1) && !ttisnumber(p1));
       luaG_typeerror(L, p1, "concatenate");
     }
    
  4. 一部のショートカット式での誤ったエラーメッセージ。
    2013年5月10日にEgor Skriptunoffによって報告されました。5.0から存在していました。5.2.3で修正されました。

    a,b,c = true,true,true
    (a and b or c)('', '')
    --> stdin:1: attempt to call a boolean value (global 'c')
    --  it should be global 'b' instead of 'c'
    

    パッチ

    ldebug.c:
    @@ -327,12 +327,20 @@
     }
     
     
    +static int filterpc (int pc, int jmptarget) {
    +  if (pc < jmptarget)  /* is code conditional (inside a jump)? */
    +    return -1;  /* cannot know who sets that register */
    +  else return pc;  /* current position sets that register */
    +}
    +
    +
     /*
     ** try to find last instruction before 'lastpc' that modified register 'reg'
     */
     static int findsetreg (Proto *p, int lastpc, int reg) {
       int pc;
       int setreg = -1;  /* keep last instruction that changed 'reg' */
    +  int jmptarget = 0;  /* any code before this address is conditional */
       for (pc = 0; pc < lastpc; pc++) {
         Instruction i = p->code[pc];
         OpCode op = GET_OPCODE(i);
    @@ -341,33 +349,38 @@
           case OP_LOADNIL: {
             int b = GETARG_B(i);
             if (a <= reg && reg <= a + b)  /* set registers from 'a' to 'a+b' */
    -          setreg = pc;
    +          setreg = filterpc(pc, jmptarget);
             break;
           }
           case OP_TFORCALL: {
    -        if (reg >= a + 2) setreg = pc;  /* affect all regs above its base */
    +        if (reg >= a + 2)  /* affect all regs above its base */
    +          setreg = filterpc(pc, jmptarget);
             break;
           }
           case OP_CALL:
           case OP_TAILCALL: {
    -        if (reg >= a) setreg = pc;  /* affect all registers above base */
    +        if (reg >= a)  /* affect all registers above base */
    +          setreg = filterpc(pc, jmptarget);
             break;
           }
           case OP_JMP: {
             int b = GETARG_sBx(i);
             int dest = pc + 1 + b;
             /* jump is forward and do not skip `lastpc'? */
    -        if (pc < dest && dest <= lastpc)
    -          pc += b;  /* do the jump */
    +        if (pc < dest && dest <= lastpc) {
    +          if (dest > jmptarget)
    +            jmptarget = dest;  /* update 'jmptarget' */
    +        }
             break;
           }
           case OP_TEST: {
    -        if (reg == a) setreg = pc;  /* jumped code can change 'a' */
    +        if (reg == a)  /* jumped code can change 'a' */
    +          setreg = filterpc(pc, jmptarget);
             break;
           }
           default:
             if (testAMode(op) && reg == a)  /* any instruction that set A */
    -          setreg = pc;
    +          setreg = filterpc(pc, jmptarget);
             break;
         }
       }
    
  5. luacリストが長い文字列で詰まります。
    2013年7月3日にAshwin Hirschiによって報告されました。5.2.1から存在していました。5.2.3で修正されました。

    -- When you call 'luac -l' over this chunk, it chokes the output
    s="Lorem ipsum dolor sit amet, consectetur, "
    

    パッチ

    luac.c:
    @@ -251,7 +251,7 @@
     static void PrintConstant(const Proto* f, int i)
     {
      const TValue* o=&f->k[i];
    - switch (ttype(o))
    + switch (ttypenv(o))
      {
       case LUA_TNIL:
            printf("nil");
    
  6. GCはパーサー中に使用中の長い文字列を収集する可能性があります。
    2013年8月30日にRobertoによって報告されました。5.2.0から存在していました。5.2.3で修正されました。

    例: このバグは、長い (40文字を超える) 識別子を持つソースコードを解析するときに、GCが非常に特定の方法で実行されることに依存するため、発生 (および再現) することが非常に困難です。

    パッチ

    ltable.h:
    @@ -18,4 +18,8 @@
     #define invalidateTMcache(t)	((t)->flags = 0)
     
    +/* returns the key, given the value of a table entry */
    +#define keyfromval(v) \
    +  (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val))))
    +
     
     LUAI_FUNC const TValue *luaH_getint (Table *t, int key);
    
    llex.c:
    @@ -134,4 +134,7 @@
         luaC_checkGC(L);
       }
    +  else {  /* string already present */
    +    ts = rawtsvalue(keyfromval(o));  /* re-use value previously stored */
    +  }
       L->top--;  /* remove string from stack */
       return ts;
    
  7. マクロ luai_userstateclose の呼び出しは、__gc メソッドの呼び出しの後でのみ行う必要があります。
    2013年9月2日にJean-Luc Jumpertzによって報告されました。5.2.3で修正されました。

    パッチ

    lstate.c:
    @@ -194,2 +194,4 @@
       g->gcrunning = 1;  /* allow gc */
    +  g->version = lua_version(NULL);
    +  luai_userstateopen(L);
     }
    @@ -224,2 +226,4 @@
       luaC_freeallobjects(L);  /* collect all objects */
    +  if (g->version)  /* closing a fully built state? */
    +    luai_userstateclose(L);
       luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
    @@ -289,3 +293,3 @@
       g->panic = NULL;
    -  g->version = lua_version(NULL);
    +  g->version = NULL;
       g->gcstate = GCSpause;
    @@ -308,4 +312,2 @@
       }
    -  else
    -    luai_userstateopen(L);
       return L;
    @@ -317,3 +319,2 @@
       lua_lock(L);
    -  luai_userstateclose(L);
       close_state(L);
    
  8. 実行中のコルーチンを再開すると、yieldできなくなります。
    2013年10月28日にFlorian Nückeによって報告されました。5.2.0から存在していました。5.2.3で修正されました。

    -- should print 'true'
    print(coroutine.resume(coroutine.create(function()
      coroutine.resume(coroutine.running())
      coroutine.yield()
    end)))
    

    パッチ

    ldo.c:
    @@ -536,2 +536,3 @@
       int status;
    +  int oldnny = L->nny;  /* save 'nny' */
       lua_lock(L);
    @@ -557,3 +558,3 @@
       }
    -  L->nny = 1;  /* do not allow yields */
    +  L->nny = oldnny;  /* restore 'nny' */
       L->nCcalls--;
    

Lua 5.2.1 1 · 2 · 3 · 4

  1. 一部のパターンが、再帰によりCスタックをオーバーフローさせる可能性があります。
    2012年7月8日にTim Starlingによって報告されました。2.5から存在していました。5.2.2で修正されました。

    print(string.find(string.rep("a", 2^20), string.rep(".?", 2^20)))
    
  2. コルーチン内部では、pcallが以前のエラー関数を復元しない可能性があります。
    2012年6月12日にAlexander Gavrilovによって報告されました。5.2.0から存在していました。5.2.2で修正されました。

    function errfunc(x)
      return 'errfunc'
    end
    
    function test(do_yield)
      print(do_yield and "yielding" or "not yielding")
      pcall(function() -- this pcall sets errfunc back to none
        if do_yield then
          coroutine.yield() -- stops errfunc from being restored
        end
      end)
      error('fail!')
    end
    
    coro = coroutine.wrap(function()
      print(xpcall(test, errfunc, false))
      print(xpcall(test, errfunc, true))
      print(xpcall(test, errfunc, false))
    end)
    
    coro()
    --> not yielding
    --> false	errfunc
    --> yielding
    coro()
    --> false	temp:12: fail!       <<<< should be 'errfunc' too
    --> not yielding
    --> false	errfunc
    

    パッチ

    ldo.c:
    @@ -403,7 +403,11 @@
       int n;
       lua_assert(ci->u.c.k != NULL);  /* must have a continuation */
       lua_assert(L->nny == 0);
    -  /* finish 'lua_callk' */
    +  if (ci->callstatus & CIST_YPCALL) {  /* was inside a pcall? */
    +    ci->callstatus &= ~CIST_YPCALL;  /* finish 'lua_pcall' */
    +    L->errfunc = ci->u.c.old_errfunc;
    +  }
    +  /* finish 'lua_callk'/'lua_pcall' */
       adjustresults(L, ci->nresults);
       /* call continuation function */
       if (!(ci->callstatus & CIST_STAT))  /* no call status? */
    
  3. 関数呼び出しにおけるガベージコレクタのチェックが、すべてのパスをカバーしていません。
    2012年8月15日にRobertoによって報告されました。5.2.1から存在していました。5.2.2で修正されました。

    例: http://lua-users.org/lists/lua-l/2012-08/msg00149.htmlを参照してください。

    パッチ

    ldo.c:
    @@ -311,6 +311,7 @@
           ci->top = L->top + LUA_MINSTACK;
           lua_assert(ci->top <= L->stack_last);
           ci->callstatus = 0;
    +      luaC_checkGC(L);  /* stack grow uses memory */
           if (L->hookmask & LUA_MASKCALL)
             luaD_hook(L, LUA_HOOKCALL, -1);
           lua_unlock(L);
    @@ -338,6 +339,7 @@
           ci->u.l.savedpc = p->code;  /* starting point */
           ci->callstatus = CIST_LUA;
           L->top = ci->top;
    +      luaC_checkGC(L);  /* stack grow uses memory */
           if (L->hookmask & LUA_MASKCALL)
             callhook(L, ci);
           return 0;
    @@ -393,7 +395,6 @@
         luaV_execute(L);  /* call it */
       if (!allowyield) L->nny--;
       L->nCcalls--;
    -  luaC_checkGC(L);
     }
    
  4. アップバリューを持たないバイナリチャンクに環境が与えられた場合、loadloadfileが間違った結果を返します。
    2012年11月28日にVladimir Strakhによって報告されました。5.2.0から存在していました。5.2.2で修正されました。

    f = load(string.dump(function () return 1 end), nil, "b", {})
    print(type(f))   --> table	(should be function)
    

    パッチ

    lbaselib.c:
    @@ -244,5 +244,11 @@
     
    -static int load_aux (lua_State *L, int status) {
    -  if (status == LUA_OK)
    +static int load_aux (lua_State *L, int status, int envidx) {
    +  if (status == LUA_OK) {
    +    if (envidx != 0) {  /* 'env' parameter? */
    +      lua_pushvalue(L, envidx);  /* environment for loaded function */
    +      if (!lua_setupvalue(L, -2, 1))  /* set it as 1st upvalue */
    +        lua_pop(L, 1);  /* remove 'env' if not used by previous call */
    +    }
         return 1;
    +  }
       else {
    @@ -258,9 +264,5 @@
       const char *mode = luaL_optstring(L, 2, NULL);
    -  int env = !lua_isnone(L, 3);  /* 'env' parameter? */
    +  int env = (!lua_isnone(L, 3) ? 3 : 0);  /* 'env' index or 0 if no 'env' */
       int status = luaL_loadfilex(L, fname, mode);
    -  if (status == LUA_OK && env) {  /* 'env' parameter? */
    -    lua_pushvalue(L, 3);
    -    lua_setupvalue(L, -2, 1);  /* set it as 1st upvalue of loaded chunk */
    -  }
    -  return load_aux(L, status);
    +  return load_aux(L, status, env);
     }
    @@ -309,5 +311,5 @@
       size_t l;
    -  int top = lua_gettop(L);
       const char *s = lua_tolstring(L, 1, &l);
       const char *mode = luaL_optstring(L, 3, "bt");
    +  int env = (!lua_isnone(L, 4) ? 4 : 0);  /* 'env' index or 0 if no 'env' */
       if (s != NULL) {  /* loading a string? */
    @@ -322,7 +324,3 @@
       }
    -  if (status == LUA_OK && top >= 4) {  /* is there an 'env' argument */
    -    lua_pushvalue(L, 4);  /* environment for loaded function */
    -    lua_setupvalue(L, -2, 1);  /* set it as 1st upvalue */
    -  }
    -  return load_aux(L, status);
    +  return load_aux(L, status, env);
     }
    

Lua 5.2.0 1 · 2 · 3 · 4 · 5 · 6

  1. コルーチンのLuaフックを作成する際のメモリの浪費。
    2012年1月16日にArseny Vakhrushevによって報告されました。5.1から存在していました。5.2.1で修正されました。

    collectgarbage(); print(collectgarbage'count' * 1024)
    
    for i = 1, 100 do
      local co = coroutine.create(function () end)
      local x = {}
      for j=1,1000 do x[j] = j end
      debug.sethook(co, function () return x end, 'l')
    end
    
    collectgarbage(); print(collectgarbage'count' * 1024)
    -- value should back to near the original level
    

    パッチ

    ldblib.c:
    @@ -253,14 +253,15 @@
     }
     
     
    -#define gethooktable(L)	luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY);
    +#define gethooktable(L)	luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)
     
     
     static void hookf (lua_State *L, lua_Debug *ar) {
       static const char *const hooknames[] =
         {"call", "return", "line", "count", "tail call"};
       gethooktable(L);
    -  lua_rawgetp(L, -1, L);
    +  lua_pushthread(L);
    +  lua_rawget(L, -2);
       if (lua_isfunction(L, -1)) {
         lua_pushstring(L, hooknames[(int)ar->event]);
         if (ar->currentline >= 0)
    @@ -306,10 +307,15 @@
         count = luaL_optint(L, arg+3, 0);
         func = hookf; mask = makemask(smask, count);
       }
    -  gethooktable(L);
    +  if (gethooktable(L) == 0) {  /* creating hook table? */
    +    lua_pushstring(L, "k");
    +    lua_setfield(L, -2, "__mode");  /** hooktable.__mode = "k" */
    +    lua_pushvalue(L, -1);
    +    lua_setmetatable(L, -2);  /* setmetatable(hooktable) = hooktable */
    +  }
    +  lua_pushthread(L1); lua_xmove(L1, L, 1);
       lua_pushvalue(L, arg+1);
    -  lua_rawsetp(L, -2, L1);  /* set new hook */
    -  lua_pop(L, 1);  /* remove hook table */
    +  lua_rawset(L, -3);  /* set new hook */
       lua_sethook(L1, func, mask, count);  /* set hooks */
       return 0;
     }
    @@ -325,7 +331,8 @@
         lua_pushliteral(L, "external hook");
       else {
         gethooktable(L);
    -    lua_rawgetp(L, -1, L1);   /* get hook */
    +    lua_pushthread(L1); lua_xmove(L1, L, 1);
    +    lua_rawget(L, -2);   /* get hook */
         lua_remove(L, -2);  /* remove hook table */
       }
       lua_pushstring(L, unmakemask(mask, buff));
    
  2. 字句解析が、算術演算子と16進数の組み合わせで混乱します。
    2012年1月17日にAlexandra Barrosによって報告されました。5.2.0から存在していました。5.2.1で修正されました。

    print(0xE+1)
    

    パッチ

    llex.c:
    @@ -223,12 +223,19 @@
    
     /* LUA_NUMBER */
     static void read_numeral (LexState *ls, SemInfo *seminfo) {
    +  const char *expo = "Ee";
    +  int first = ls->current;
       lua_assert(lisdigit(ls->current));
    -  do {
    -    save_and_next(ls);
    -    if (check_next(ls, "EePp"))  /* exponent part? */
    +  save_and_next(ls);
    +  if (first == '0' && check_next(ls, "Xx"))  /* hexadecimal? */
    +    expo = "Pp";
    +  for (;;) {
    +    if (check_next(ls, expo))  /* exponent part? */
           check_next(ls, "+-");  /* optional exponent sign */
    -  } while (lislalnum(ls->current) || ls->current == '.');
    +    if (lisxdigit(ls->current) || ls->current == '.')
    +      save_and_next(ls);
    +    else  break;
    +  }
       save(ls, '\0');
       buffreplace(ls, '.', ls->decpoint);  /* follow locale for decimal point */
       if (!buff2d(ls->buff, &seminfo->r))  /* format error? */
    
  3. ファイナライザが、ライブラリがアンロードされた後に動的ライブラリから関数を呼び出す可能性があります。
    2012年4月8日にJosh Habermanによって報告されました。5.1から存在していました。5.2.1で修正されました。

    local u = setmetatable({}, {__gc = function () foo() end})
    local m = require 'mod'   -- 'mod' may be any dynamic library written in C
    foo = m.foo     -- 'foo' may be any function from 'mod'
    -- end program; it crashes
    

    パッチ

    loadlib.c:
    95c95
    < #define LIBPREFIX	"LOADLIB: "
    ---
    > #define CLIBS		"_CLIBS"
    251,266c251,256
    < 
    < static void **ll_register (lua_State *L, const char *path) {
    <   void **plib;
    <   lua_pushfstring(L, "%s%s", LIBPREFIX, path);
    <   lua_gettable(L, LUA_REGISTRYINDEX);  /* check library in registry? */
    <   if (!lua_isnil(L, -1))  /* is there an entry? */
    <     plib = (void **)lua_touserdata(L, -1);
    <   else {  /* no entry yet; create one */
    <     lua_pop(L, 1);  /* remove result from gettable */
    <     plib = (void **)lua_newuserdata(L, sizeof(const void *));
    <     *plib = NULL;
    <     luaL_setmetatable(L, "_LOADLIB");
    <     lua_pushfstring(L, "%s%s", LIBPREFIX, path);
    <     lua_pushvalue(L, -2);
    <     lua_settable(L, LUA_REGISTRYINDEX);
    <   }
    ---
    > static void *ll_checkclib (lua_State *L, const char *path) {
    >   void *plib;
    >   lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
    >   lua_getfield(L, -1, path);
    >   plib = lua_touserdata(L, -1);  /* plib = CLIBS[path] */
    >   lua_pop(L, 2);  /* pop CLIBS table and 'plib' */
    270a261,270
    > static void ll_addtoclib (lua_State *L, const char *path, void *plib) {
    >   lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
    >   lua_pushlightuserdata(L, plib);
    >   lua_pushvalue(L, -1);
    >   lua_setfield(L, -3, path);  /* CLIBS[path] = plib */
    >   lua_rawseti(L, -2, luaL_len(L, -2) + 1);  /* CLIBS[#CLIBS + 1] = plib */
    >   lua_pop(L, 1);  /* pop CLIBS table */
    > }
    > 
    > 
    272,273c272,273
    < ** __gc tag method: calls library's `ll_unloadlib' function with the lib
    < ** handle
    ---
    > ** __gc tag method for CLIBS table: calls 'll_unloadlib' for all lib
    > ** handles in list CLIBS
    276,278c276,281
    <   void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
    <   if (*lib) ll_unloadlib(*lib);
    <   *lib = NULL;  /* mark library as closed */
    ---
    >   int n = luaL_len(L, 1);
    >   for (; n >= 1; n--) {  /* for each handle, in reverse order */
    >     lua_rawgeti(L, 1, n);  /* get handle CLIBS[n] */
    >     ll_unloadlib(lua_touserdata(L, -1));
    >     lua_pop(L, 1);  /* pop handle */
    >   }
    284,286c287,292
    <   void **reg = ll_register(L, path);
    <   if (*reg == NULL) *reg = ll_load(L, path, *sym == '*');
    <   if (*reg == NULL) return ERRLIB;  /* unable to load library */
    ---
    >   void *reg = ll_checkclib(L, path);  /* check loaded C libraries */
    >   if (reg == NULL) {  /* must load library? */
    >     reg = ll_load(L, path, *sym == '*');
    >     if (reg == NULL) return ERRLIB;  /* unable to load library */
    >     ll_addtoclib(L, path, reg);
    >   }
    292c298
    <     lua_CFunction f = ll_sym(L, *reg, sym);
    ---
    >     lua_CFunction f = ll_sym(L, reg, sym);
    675,676c681,683
    <   /* create new type _LOADLIB */
    <   luaL_newmetatable(L, "_LOADLIB");
    ---
    >   /* create table CLIBS to keep track of loaded C libraries */
    >   luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS);
    >   lua_createtable(L, 0, 1);  /* metatable for CLIBS */
    678a686
    >   lua_setmetatable(L, -2);
    
  4. コルーチンにおけるnCcallsの誤った処理。
    2012年4月18日にAlexander Gavrilovによって報告されました。5.2.0から存在していました。5.2.1で修正されました。

    coroutine.wrap(function()
      print(pcall(pcall,pcall,pcall,pcall,pcall,error,3))
    end)()
    

    パッチ

    ldo.c:
    @@ -402,8 +402,6 @@
       int n;
       lua_assert(ci->u.c.k != NULL);  /* must have a continuation */
       lua_assert(L->nny == 0);
    -  /* finish 'luaD_call' */
    -  L->nCcalls--;
       /* finish 'lua_callk' */
       adjustresults(L, ci->nresults);
       /* call continuation function */
    @@ -513,7 +511,6 @@
             api_checknelems(L, n);
             firstArg = L->top - n;  /* yield results come from continuation */
           }
    -      L->nCcalls--;  /* finish 'luaD_call' */
           luaD_poscall(L, firstArg);  /* finish 'luaD_precall' */
         }
         unroll(L, NULL);
    
  5. 内部のLua値がデバッグAPIを介してエスケープする可能性があります。
    2012年4月20日にDan Tullによって報告されました。5.1から存在していました。5.2.1で修正されました。

    local firsttime = true
    local function foo ()
      if firsttime then
        firsttime = false
        return "a = 1" 
      else
        for i = 1, 10 do
          print(debug.getlocal(2, i))
        end
      end
    end
    
    print(load(foo))   -- prints some lines and then crashes
    
  6. デバッグフックからのyield時の問題。
    2012年6月5日にErik Casselによって報告されました。5.2.0から存在していました。5.2.1で修正されました。

    例:Cで、単にyieldする行フックを設定し、任意のLua関数を呼び出します。yieldの無限ループが発生します。

Lua 5.1.5 1 · 2

これがLua 5.1の最後のリリースでした。後で報告されたバグは、ほぼ確実にLua 5.2で修正されています。

  1. src/Makefileのコメントが移植可能ではありません。
    2012年3月9日にLorenzo Donatiによって報告されました。5.1.5から存在していました。5.2.0で修正されました。

    例:この不具合は、一部のmingwシステムでLuaをコンパイルする際に発生します。他のシステムには影響しないようです。

    パッチ

    src/Makefile:
    @@ -50,3 +50,3 @@
     $(LUA_A): $(CORE_O) $(LIB_O)
    -	$(AR) $@ $(CORE_O) $(LIB_O)	# DLL needs all object files
    +	$(AR) $@ $(CORE_O) $(LIB_O)
     	$(RANLIB) $@
    
  2. ファイルをロードする際、Luaは入力の終わりを返した後でもリーダー関数を再度呼び出す可能性があります。
    2013年6月5日にChris Howieによって報告されました。5.1から存在していました。5.2.0で修正されました。

    load(function () print("called"); return nil end)
    --> called
    --> called             (should be called only once!)
    

    パッチ

    lzio.h:
    @@ -59,6 +59,7 @@
       lua_Reader reader;
       void* data;			/* additional data */
       lua_State *L;			/* Lua state (for reader) */
    +  int eoz;			/* true if reader has no more data */
     };
    
    
    lzio.c:
    @@ -22,10 +22,14 @@
       size_t size;
       lua_State *L = z->L;
       const char *buff;
    +  if (z->eoz) return EOZ;
       lua_unlock(L);
       buff = z->reader(L, z->data, &size);
       lua_lock(L);
    -  if (buff == NULL || size == 0) return EOZ;
    +  if (buff == NULL || size == 0) {
    +    z->eoz = 1;  /* avoid calling reader function next time */
    +    return EOZ;
    +  }
       z->n = size - 1;
       z->p = buff;
       return char2int(*(z->p++));
    @@ -51,6 +55,7 @@
       z->data = data;
       z->n = 0;
       z->p = NULL;
    +  z->eoz = 0;
     }
    

Lua 5.1.4 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11

  1. 悪意を持って作成されたプリコンパイル済みコードがLuaをクラッシュさせる可能性があります。
    2008年9月1日にPeter Cawleyによって報告されました。

    解決策:信頼できないソースからのプリコンパイル済みコードの実行を避けるため、ストリームの最初のバイトがエスケープ文字(10進数の27)である場合はエラーを発生させます。

  2. 可変長引数のスマートな使用は、過剰な数の引数を返し、C関数のスタックをオーバーフローさせる関数を作成する可能性があります。
    2008年12月10日にPatrick Donnellyによって報告されました。5.1.5で修正されました。

    function lunpack(i, ...)
      if i == 0 then
        return ...
      else
        return lunpack(i-1, 1, ...)
      end
    end
    
    現在、Cが大きなnでlunpack(n)を呼び出すと、スタックに過剰な値が残り、スタックインデックスが混乱する可能性があります。
  3. 一部の特定のブール式の誤ったコード生成。(9も参照してください)
    2009年4月15日にBrian Kelleyによって報告されました。5.0から存在していました。5.1.5で修正されました。

    print(((1 or false) and true) or false)   --> 1, but should be 'true'
    

    パッチ:(部分的な解決策; 9も参照してください)

    lcode.c:
    @@ -544,15 +544,18 @@
           pc = NO_JUMP;  /* always true; do nothing */
           break;
         }
    -    case VFALSE: {
    -      pc = luaK_jump(fs);  /* always jump */
    -      break;
    -    }
         case VJMP: {
           invertjump(fs, e);
           pc = e->u.s.info;
           break;
         }
    +    case VFALSE: {
    +      if (!hasjumps(e)) {
    +        pc = luaK_jump(fs);  /* always jump */
    +        break;
    +      }
    +      /* else go through */
    +    }
         default: {
           pc = jumponcond(fs, e, 0);
           break;
    @@ -572,14 +575,17 @@
           pc = NO_JUMP;  /* always false; do nothing */
           break;
         }
    -    case VTRUE: {
    -      pc = luaK_jump(fs);  /* always jump */
    -      break;
    -    }
         case VJMP: {
           pc = e->u.s.info;
           break;
         }
    +    case VTRUE: {
    +      if (!hasjumps(e)) {
    +        pc = luaK_jump(fs);  /* always jump */
    +        break;
    +      }
    +      /* else go through */
    +    }
         default: {
           pc = jumponcond(fs, e, 1);
           break;
    
  4. luaV_settableがテーブルへの参照を無効にし、それを再利用しようとする可能性があります。
    2009年6月27日にMark Feldmanによって報告されました。5.0から存在していました。5.1.5で修正されました。

    grandparent = {}
    grandparent.__newindex = function(s,_,_) print(s) end
    
    parent = {}
    parent.__newindex = parent
    setmetatable(parent, grandparent)
    
    child = setmetatable({}, parent)
    child.foo = 10      --> (crash on some machines)
    

    パッチ

    lvm.c:
    @@ -133,6 +133,7 @@
     
     void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
       int loop;
    +  TValue temp;
       for (loop = 0; loop < MAXTAGLOOP; loop++) {
         const TValue *tm;
         if (ttistable(t)) {  /* `t' is a table? */
    @@ -152,7 +153,9 @@
           callTM(L, tm, t, key, val);
           return;
         }
    -    t = tm;  /* else repeat with `tm' */ 
    +    /* else repeat with `tm' */
    +    setobj(L, &temp, tm);  /* avoid pointing inside table (may rehash) */
    +    t = &temp;
       }
       luaG_runerror(L, "loop in settable");
     }
    
  5. debug.getfenvは引数があるかどうかをチェックしません。
    2009年7月30日にPatrick Donnellyによって報告されました。5.1から存在していました。5.1.5で修正されました。

    debug.getfenv()   -- should raise an error
    

    パッチ

    ldblib.c:
    @@ -45,6 +45,7 @@
     
     
     static int db_getfenv (lua_State *L) {
    +  luaL_checkany(L, 1);
       lua_getfenv(L, 1);
       return 1;
     }
    
  6. GCが解析中にスタックし、文字列テーブルの適切なサイズ変更を回避し、リストが過度に大きくなり、パフォーマンスが低下する可能性があります。
    2009年11月10日にSean Connerによって報告されました。5.1から存在していました。5.1.5で修正されました。

    例: http://lua-users.org/lists/lua-l/2009-11/msg00463.htmlを参照してください。

    パッチ

    llex.c:
    @@ -118,8 +118,10 @@
       lua_State *L = ls->L;
       TString *ts = luaS_newlstr(L, str, l);
       TValue *o = luaH_setstr(L, ls->fs->h, ts);  /* entry for `str' */
    -  if (ttisnil(o))
    +  if (ttisnil(o)) {
         setbvalue(o, 1);  /* make sure `str' will not be collected */
    +    luaC_checkGC(L);
    +  }
       return ts;
     }
     
    
  7. string.formatは、引数が不足しており、書式文字列が長すぎる場合、バッファを引数として取得する可能性があります。
    2010年4月12日にRobertoによって報告されました。5.0から存在していました。5.1.5で修正されました。

    x = string.rep("x", 10000) .. "%d"
    print(string.format(x))    -- gives wrong error message
    

    パッチ

    lstrlib.c:
    @@ -754,6 +754,7 @@
     
     
     static int str_format (lua_State *L) {
    +  int top = lua_gettop(L);
       int arg = 1;
       size_t sfl;
       const char *strfrmt = luaL_checklstring(L, arg, &sfl);
    @@ -768,7 +769,8 @@
         else { /* format item */
           char form[MAX_FORMAT];  /* to store the format (`%...') */
           char buff[MAX_ITEM];  /* to store the formatted item */
    -      arg++;
    +      if (++arg > top)
    +        luaL_argerror(L, arg, "no value");
           strfrmt = scanformat(L, strfrmt, form);
           switch (*strfrmt++) {
             case 'c': {
    
  8. io.read("*n","*n")は、2回目の読み込みに失敗した場合、ガベージを返す可能性があります。
    2010年4月12日にRobertoによって報告されました。5.0から存在していました。5.1.5で修正されました。

    print(io.read("*n", "*n"))   --<< enter "10   hi"
    --> file (0x884420)	nil
    

    パッチ

    liolib.c:
    @@ -276,7 +276,10 @@
         lua_pushnumber(L, d);
         return 1;
       }
    -  else return 0;  /* read fails */
    +  else {
    +    lua_pushnil(L);  /* "result" to be removed */
    +    return 0;  /* read fails */
    +  }
     }
     
     
    
  9. 一部の特定のブール式の誤ったコード生成。
    2011年1月20日にThierry Van Elsuweによって報告されました。5.0から存在していました。5.1.5で修正されました。

    print((('hi' or true) and true) or true)
    --> hi     (should be true)
    print(((nil and nil) or false) and true)
    --> nil    (should be false)
    

    パッチ:(3のパッチの後に適用する必要があります)

    lcode.c:
    @@ -549,13 +549,6 @@
           pc = e->u.s.info;
           break;
         }
    -    case VFALSE: {
    -      if (!hasjumps(e)) {
    -        pc = luaK_jump(fs);  /* always jump */
    -        break;
    -      }
    -      /* else go through */
    -    }
         default: {
           pc = jumponcond(fs, e, 0);
           break;
    @@ -579,13 +572,6 @@
           pc = e->u.s.info;
           break;
         }
    -    case VTRUE: {
    -      if (!hasjumps(e)) {
    -        pc = luaK_jump(fs);  /* always jump */
    -        break;
    -      }
    -      /* else go through */
    -    }
         default: {
           pc = jumponcond(fs, e, 1);
           break;
    
  10. メタテーブルがそれ自身のメタテーブルである場合、Newindexメタメソッドが機能しない可能性があります。
    2011年8月9日にCuero Bugotによって報告されました。5.1から存在していました。5.1.5で修正されました。

    meta={}
    setmetatable(meta, meta)
    meta.__newindex = function(t, key, value) print("set") end
    o = setmetatable({}, meta)
    o.x = 10    -- should print 'set'
    

    パッチ

    lvm.c:
    @@ -142,6 +142,7 @@
           if (!ttisnil(oldval) ||  /* result is no nil? */
               (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
             setobj2t(L, oldval, val);
    +        h->flags = 0;
             luaC_barriert(L, h, val);
             return;
           }
    
  11. パーサーがプロトタイプを構築中に収集する可能性があります。
    2011年10月13日にIngo van Lilによって報告されました。5.1.4から存在していました(パッチ5.1.4-6が原因)。5.1.5で修正されました。

    パッチ

    lparser.c:
    @@ -374,9 +374,9 @@
       lua_assert(luaG_checkcode(f));
       lua_assert(fs->bl == NULL);
       ls->fs = fs->prev;
    -  L->top -= 2;  /* remove table and prototype from the stack */
       /* last token read was anchored in defunct function; must reanchor it */
       if (fs) anchor_token(ls);
    +  L->top -= 2;  /* remove table and prototype from the stack */
     }
     
     
    

Lua 5.1.3 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12

  1. LUAI_MAXCSTACK-LUA_REGISTRYINDEXより小さくなければなりません。
    2008年2月11日にPatrick Donnellyによって報告されました。5.1.3から存在していました。5.1.4で修正されました。

    j = 1e4
    co = coroutine.create(function()
           t = {}
           for i = 1, j do t[i] = i end
           return unpack(t)
    end)
    print(coroutine.resume(co))
    

    パッチ

    luaconf.h:
    443c443,444
    < ** functions to consume unlimited stack space.
    ---
    > ** functions to consume unlimited stack space. (must be smaller than
    > ** -LUA_REGISTRYINDEX)
    445,446c446
    < #define LUAI_MCS_AUX  ((int)(INT_MAX / (4*sizeof(LUA_NUMBER))))
    < #define LUAI_MAXCSTACK        (LUAI_MCS_AUX > SHRT_MAX ? SHRT_MAX : LUAI_MCS_AUX)
    ---
    > #define LUAI_MAXCSTACK        8000
    
  2. coroutine.resumeがスタックサイズを保証せずに要素をプッシュします。
    2008年2月11日に報告されました。5.0から存在していました。5.1.4で修正されました。

    例:このバグは内部アサーションなしでは検出できません。

    パッチ

    lbaselib.c:
    @@ -526,7 +526,7 @@
       status = lua_resume(co, narg);
       if (status == 0 || status == LUA_YIELD) {
         int nres = lua_gettop(co);
    -    if (!lua_checkstack(L, nres))
    +    if (!lua_checkstack(L, nres + 1))
           luaL_error(L, "too many results to resume");
         lua_xmove(co, L, nres);  /* move yielded values */
         return nres;
    
  3. lua_checkstackは大きな'size'に対して算術オーバーフローを起こす可能性があります。
    2008年2月12日にPatrick Donnellyによって報告されました。5.0から存在していました。5.1.4で修正されました。

    print(unpack({1,2,3}, 0, 2^31-3))
    

    パッチ

    lapi.c:
    @@ -93,15 +93,14 @@
     
     
     LUA_API int lua_checkstack (lua_State *L, int size) {
    -  int res;
    +  int res = 1;
       lua_lock(L);
    -  if ((L->top - L->base + size) > LUAI_MAXCSTACK)
    +  if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)
         res = 0;  /* stack overflow */
    -  else {
    +  else if (size > 0) {
         luaD_checkstack(L, size);
         if (L->ci->top < L->top + size)
           L->ci->top = L->top + size;
    -    res = 1;
       }
       lua_unlock(L);
       return res;
    
  4. 最大インデックスを持つunpackは、算術オーバーフローによりクラッシュする可能性があります。
    2008年2月12日にPatrick Donnellyによって報告されました。5.1から存在していました。5.1.4で修正されました。

    print(unpack({1,2,3}, 2^31-1, 2^31-1))
    

    パッチ

    lbaselib.c:
    @@ -344,10 +344,12 @@
       luaL_checktype(L, 1, LUA_TTABLE);
       i = luaL_optint(L, 2, 1);
       e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
    +  if (i > e) return 0;  /* empty range */
       n = e - i + 1;  /* number of elements */
    -  if (n <= 0) return 0;  /* empty range */
    -  luaL_checkstack(L, n, "table too big to unpack");
    -  for (; i<=e; i++)  /* push arg[i...e] */
    +  if (n <= 0 || !lua_checkstack(L, n))  /* n <= 0 means arith. overflow */
    +    return luaL_error(L, "too many results to unpack");
    +  lua_rawgeti(L, 1, i);  /* push arg[i] (avoiding overflow problems) */
    +  while (i++ < e)  /* push arg[i + 1...e] */
         lua_rawgeti(L, 1, i);
       return n;
     }
    
  5. 悪意を持って作成されたプリコンパイル済みコードがLuaをクラッシュさせる可能性があります。
    2008年3月24日にPeter Cawleyによって報告されました。5.0から存在していました。5.1.4で修正されました。

    a = string.dump(function()return;end)
    a = a:gsub(string.char(30,37,122,128), string.char(34,0,0), 1)
    loadstring(a)()
    

    パッチ

    ldebug.c:
    @@ -275,12 +275,12 @@
     
     static int precheck (const Proto *pt) {
       check(pt->maxstacksize <= MAXSTACK);
    -  lua_assert(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
    -  lua_assert(!(pt->is_vararg & VARARG_NEEDSARG) ||
    +  check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
    +  check(!(pt->is_vararg & VARARG_NEEDSARG) ||
                   (pt->is_vararg & VARARG_HASARG));
       check(pt->sizeupvalues <= pt->nups);
       check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);
    -  check(GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
    +  check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
       return 1;
     }
     
    @@ -363,7 +363,11 @@
         }
         switch (op) {
           case OP_LOADBOOL: {
    -        check(c == 0 || pc+2 < pt->sizecode);  /* check its jump */
    +        if (c == 1) {  /* does it jump? */
    +          check(pc+2 < pt->sizecode);  /* check its jump */
    +          check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||
    +                GETARG_C(pt->code[pc+1]) != 0);
    +        }
             break;
           }
           case OP_LOADNIL: {
    @@ -428,7 +432,10 @@
           }
           case OP_SETLIST: {
             if (b > 0) checkreg(pt, a + b);
    -        if (c == 0) pc++;
    +        if (c == 0) {
    +          pc++;
    +          check(pc < pt->sizecode - 1);
    +        }
             break;
           }
           case OP_CLOSURE: {
    
  6. 悪意を持って作成されたプリコンパイル済みコードがCスタックを吹き飛ばす可能性があります。
    2008年3月25日にGreg Falconによって報告されました。5.0から存在していました。5.1.4で修正されました。

    function crash(depth)
      local init = '\27\76\117\97\81\0\1\4\4\4\8\0\7\0\0\0\61\115\116' ..
                   '\100\105\110\0\1\0\0\0\1\0\0\0\0\0\0\2\2\0\0\0\36' ..
                   '\0\0\0\30\0\128\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0' ..
                   '\1\0\0\0\0\0\0\2'
      local mid = '\1\0\0\0\30\0\128\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0'
      local fin = '\0\0\0\0\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0' ..
                  '\0\0\97\0\1\0\0\0\1\0\0\0\0\0\0\0'
      local lch = '\2\0\0\0\36\0\0\0\30\0\128\0\0\0\0\0\1\0\0\0\0\0\0' ..
                  '\0\1\0\0\0\1\0\0\0\0\0\0\2'
      local rch = '\0\0\0\0\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0' ..
                  '\0\0\97\0\1\0\0\0\1'
      for i=1,depth do lch,rch = lch..lch,rch..rch end
      loadstring(init .. lch .. mid .. rch .. fin)
    end
    for i=1,25 do print(i); crash(i) end
    

    パッチ

    lundump.c:
    @@ -161,7 +160,9 @@
     
     static Proto* LoadFunction(LoadState* S, TString* p)
     {
    - Proto* f=luaF_newproto(S->L);
    + Proto* f;
    + if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep");
    + f=luaF_newproto(S->L);
      setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
      f->source=LoadString(S); if (f->source==NULL) f->source=p;
      f->linedefined=LoadInt(S);
    @@ -175,6 +176,7 @@
      LoadDebug(S,f);
      IF (!luaG_checkcode(f), "bad code");
      S->L->top--;
    + S->L->nCcalls--;
      return f;
     }
    
  7. コードバリデーターが(悪意を持って作成された)正しいコードを拒否する可能性があります。
    2008年3月26日にGreg Falconによって報告されました。5.0から存在していました。5.1.4で修正されました。

    z={}
    for i=1,27290 do z[i]='1,' end
    z = 'if 1+1==2 then local a={' .. table.concat(z) .. '} end'
    func = loadstring(z)
    print(loadstring(string.dump(func)))
    

    パッチ

    ldebug.c:
    @@ -346,9 +346,18 @@
               int dest = pc+1+b;
               check(0 <= dest && dest < pt->sizecode);
               if (dest > 0) {
    -            /* cannot jump to a setlist count */
    -            Instruction d = pt->code[dest-1];
    -            check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0));
    +            int j;
    +            /* check that it does not jump to a setlist count; this
    +               is tricky, because the count from a previous setlist may
    +               have the same value of an invalid setlist; so, we must
    +               go all the way back to the first of them (if any) */
    +            for (j = 0; j < dest; j++) {
    +              Instruction d = pt->code[dest-1-j];
    +              if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;
    +            }
    +            /* if 'j' is even, previous value is not a setlist (even if
    +               it looks like one) */
    +            check((j&1) == 0);
               }
             }
             break;
    
  8. 悪意を持って作成されたプリコンパイル済みコードが、無効なブール値をLuaコードに注入する可能性があります。
    2008年3月27日にGreg Falconによって報告されました。5.0から存在していました。5.1.4で修正されました。

    maybe = string.dump(function() return ({[true]=true})[true] end)
    maybe = maybe:gsub('\1\1','\1\2')
    maybe = loadstring(maybe)()
    assert(type(maybe) == "boolean" and maybe ~= true and maybe ~= false)
    

    パッチ

    lundump.c:
    @@ -115,7 +115,7 @@
            setnilvalue(o);
            break;
        case LUA_TBOOLEAN:
    -       setbvalue(o,LoadChar(S));
    +       setbvalue(o,LoadChar(S)!=0);
            break;
        case LUA_TNUMBER:
            setnvalue(o,LoadNumber(S));
    
  9. string.byteは、一部の範囲外の負のインデックスで混乱します。
    2008年6月3日にMike Pallによって報告されました。5.1から存在していました。5.1.4で修正されました。

    print(string.byte("abc", -5))   --> 97   98   99   (should print nothing)
    

    パッチ

    lstrlib.c:
    @@ -35,7 +35,8 @@
     
     static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
       /* relative string position: negative means back from end */
    -  return (pos>=0) ? pos : (ptrdiff_t)len+pos+1;
    +  if (pos < 0) pos += (ptrdiff_t)len + 1;
    +  return (pos >= 0) ? pos : 0;
     }
     
     
    
  10. ユーザーが要求したGCステップが永遠にループする可能性があります。
    2008年7月1日にMakoto Hamanakaによって報告されました。5.1から存在していました。5.1.4で修正されました。

    collectgarbage("setpause", 100) -- small value
    collectgarbage("setstepmul", 2000) -- large value
    collectgarbage("step",0)
    

    パッチ

    lapi.c:
    @@ -929,10 +929,13 @@
             g->GCthreshold = g->totalbytes - a;
           else
             g->GCthreshold = 0;
    -      while (g->GCthreshold <= g->totalbytes)
    +      while (g->GCthreshold <= g->totalbytes) {
             luaC_step(L);
    -      if (g->gcstate == GCSpause)  /* end of cycle? */
    -        res = 1;  /* signal it */
    +        if (g->gcstate == GCSpause) {  /* end of cycle? */
    +          res = 1;  /* signal it */
    +          break;
    +        }
    +      }
           break;
         }
         case LUA_GCSETPAUSE: {
    
  11. moduleがC関数の環境を変更する可能性があります。
    2008年7月16日にPeter Cawleyによって報告されました。5.1から存在していました。5.1.4で修正されました。

    pcall(module, "xuxu")
    assert(debug.getfenv(pcall) == xuxu)
    

    パッチ

    loadlib.c:
    @@ -506,8 +506,11 @@
     
     static void setfenv (lua_State *L) {
       lua_Debug ar;
    -  lua_getstack(L, 1, &ar);
    -  lua_getinfo(L, "f", &ar);
    +  if (lua_getstack(L, 1, &ar) == 0 ||
    +      lua_getinfo(L, "f", &ar) == 0 ||  /* get calling function */
    +      lua_iscfunction(L, -1))
    +    luaL_error(L, "function " LUA_QL("module")
    +                  " not called from a Lua function");
       lua_pushvalue(L, -2);
       lua_setfenv(L, -2);
       lua_pop(L, 1);
    
  12. 内部マクロsvalueが間違っています。
    2008年8月4日にMartijn van Buulによって報告されました。5.1から存在していました。5.1.4で修正されました。

    /* in luaconf.h */
    #define LUAI_USER_ALIGNMENT_T   union { char b[32]; }
    

    パッチ

    lobject.h:
    @@ -210,3 +210,3 @@
     #define getstr(ts)	cast(const char *, (ts) + 1)
    -#define svalue(o)       getstr(tsvalue(o))
    +#define svalue(o)       getstr(rawtsvalue(o))
     
    

Lua 5.1.2 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12 · 13

  1. Luaが標準ファイルを閉じ、その後Cで使用される可能性があります。
    2007年4月17日にDavid Manuraによって報告されました。5.1.3で修正されました。
  2. -nil-true、および-falseに対して生成されたコードが間違っています。
    2007年4月29日にDavid ManuraとRici Lakeによって報告されました。5.1から存在していました。5.1.3で修正されました。

    print(-nil)
    

    パッチ

    lcode.c:
    @@ -699,7 +699,7 @@
       e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
       switch (op) {
         case OPR_MINUS: {
    -      if (e->k == VK)
    +      if (!isnumeral(e))
             luaK_exp2anyreg(fs, e);  /* cannot operate on non-numeric constants */
           codearith(fs, OP_UNM, e, &e2);
           break;
    
  3. カウントフックが設定されていないのに呼び出される可能性があります。
    2007年5月にMike Pallによって報告されました。5.1.3で修正されました。

    パッチ

    lvm.c:
    @@ -61,11 +61,9 @@
       lu_byte mask = L->hookmask;
       const Instruction *oldpc = L->savedpc;
       L->savedpc = pc;
    -  if (mask > LUA_MASKLINE) {  /* instruction-hook set? */
    -    if (L->hookcount == 0) {
    -      resethookcount(L);
    -      luaD_callhook(L, LUA_HOOKCOUNT, -1);
    -    }
    +  if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
    +    resethookcount(L);
    +    luaD_callhook(L, LUA_HOOKCOUNT, -1);
       }
       if (mask & LUA_MASKLINE) {
         Proto *p = ci_func(L->ci)->l.p;
    
  4. 再帰的なコルーチンがCスタックをオーバーフローする可能性があります。

    a = function(a) coroutine.wrap(a)(a) end
    a(a)
    

    パッチ:'nCcalls'カウンターは、すべてのスレッドで共有する必要があります。(つまり、'lua_State'ではなく、'global_State'構造体で宣言する必要があります。)

  5. 一部の連結における誤ったエラーメッセージ。
    2007年5月にAlex Daviesによって報告されました。5.1.2から存在していました。5.1.3で修正されました。

    a = nil; a = (1)..a
    

    パッチ

    ldebug.c:
    @@ -563,8 +563,8 @@
    
    
     void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
    -  if (ttisstring(p1)) p1 = p2;
    -  lua_assert(!ttisstring(p1));
    +  if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
    +  lua_assert(!ttisstring(p1) && !ttisnumber(p1));
       luaG_typeerror(L, p1, "concatenate");
     }
    
    
  6. 非常に小さな数値はすべてハッシュ関数で衝突します。(これはパフォーマンスの問題のみを引き起こします。動作は正しいです。)。
    2007年4月18日に報告されました。Lua 5.0から存在していました。5.1.3で修正されました。

    パッチ

    ltable.c:
    87,88c87,88
    <   n += 1;  /* normalize number (avoid -0) */
    <   lua_assert(sizeof(a) <= sizeof(n));
    ---
    >   if (luai_numeq(n, 0))  /* avoid problems with -0 */
    >     return gnode(t, 0);
    
  7. 割り当てに多すぎる変数が含まれていると、Cスタックオーバーフローが発生する可能性があります。
    2007年7月31日にMike Pallによって報告されました。5.0から存在していました。5.1.3で修正されました。

    $ ulimit -s 1024       # Reduce C stack to 1MB for quicker results
    $ lua -e 'local s = "a,"; for i=1,18 do s = s..s end print(loadstring("local a;"..s.."a=nil", ""))'
    

    パッチ

    lparser.c:
    @@ -938,6 +938,8 @@
         primaryexp(ls, &nv.v);
         if (nv.v.k == VLOCAL)
           check_conflict(ls, lh, &nv.v);
    +    luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,
    +                    "variable names");
         assignment(ls, &nv, nvars+1);
       }
       else {  /* assignment -> `=' explist1 */
    
  8. '-l'オプションを介してロードされたモジュールでのエラーには、トレースバックが表示されません。
    2007年8月25日にDavid Manuraによって報告されました。5.1から存在していました。5.1.3で修正されました。

    lua -ltemp    (assuming temp.lua has an error)
    

    パッチ

    lua.c:
    @@ -144,7 +144,7 @@
     static int dolibrary (lua_State *L, const char *name) {
       lua_getglobal(L, "require");
       lua_pushstring(L, name);
    -  return report(L, lua_pcall(L, 1, 0, 0));
    +  return report(L, docall(L, 1, 1));
     }
    
  9. gsubは、3番目の引数なしで、大きな対象で誤って呼び出された場合、暴走する可能性があります。
    2007年10月26日にFlorian Bergerによって報告されました。5.1から存在していました。5.1.3で修正されました。

    x = string.rep('a', 10000) .. string.rep('b', 10000)
    print(#string.gsub(x, 'b'))
    

    パッチ

    lstrlib.c:
    @@ -631,6 +631,2 @@
         }
    -    default: {
    -      luaL_argerror(L, 3, "string/function/table expected"); 
    -      return;
    -    }
       }
    @@ -650,2 +646,3 @@
       const char *p = luaL_checkstring(L, 2);
    +  int  tr = lua_type(L, 3);
       int max_s = luaL_optint(L, 4, srcl+1);
    @@ -655,2 +652,5 @@
       luaL_Buffer b;
    +  luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
    +                   tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
    +                      "string/function/table expected");
       luaL_buffinit(L, &b);
    
  10. table.removeは、範囲外のインデックスが与えられた場合、テーブルの最後の要素を削除します。
    2007年11月13日にPatrick Donnellyによって報告されました。少なくとも5.0から存在していました。5.1.3で修正されました。

    a = {1,2,3}
    table.remove(a, 4)
    print(a[3])   --> nil   (should be 3)
    

    パッチ

    ltablib.c:
    @@ -118,7 +118,8 @@
     static int tremove (lua_State *L) {
       int e = aux_getn(L, 1);
       int pos = luaL_optint(L, 2, e);
    -  if (e == 0) return 0;  /* table is `empty' */
    +  if (!(1 <= pos && pos <= e))  /* position is outside bounds? */
    +   return 0;  /* nothing to remove */
       luaL_setn(L, 1, e - 1);  /* t.n = n-1 */
       lua_rawgeti(L, 1, pos);  /* result = t[pos] */
       for ( ;pos&lt;e; pos++) {
    
  11. 無効なオブジェクトに対して呼び出された場合、lua_setfenvがクラッシュする可能性があります。
    2007年11月28日にMike Pallによって報告されました。5.1から存在していました。5.1.3で修正されました。

    > debug.setfenv(3, {})
    

    パッチ

    lapi.c:
    @@ -749,7 +749,7 @@
           res = 0;
           break;
       }
    -  luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));
    +  if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));
       L->top--;
       lua_unlock(L);
       return res;
    
  12. スタンドアロンインタープリターは、「メッセージ」がコルーチンである場合、誤ったエラーメッセージを表示します。
    2007年12月17日にPatrick Donnellyによって報告されました。5.1から存在していました。5.1.3で修正されました。

    > error(coroutine.create(function() end))
    

    パッチ

    lua.c:
    @@ -74,6 +74,8 @@
     
     
     static int traceback (lua_State *L) {
    +  if (!lua_isstring(L, 1))  /* 'message' not a string? */
    +    return 1;  /* keep it intact */
       lua_getfield(L, LUA_GLOBALSINDEX, "debug");
       if (!lua_istable(L, -1)) {
         lua_pop(L, 1);
    
    
  13. debug.sethook/gethookがスレッドのスタックをオーバーフローさせる可能性があります。
    2008年1月4日にIvko Stanilovによって報告されました。5.1から存在していました。5.1.3で修正されました。

    a = coroutine.create(function() yield() end)
    coroutine.resume(a)
    debug.sethook(a)      -- may overflow the stack of 'a'
    

    パッチ

    ldblib.c:
    @@ -268,12 +268,11 @@
         count = luaL_optint(L, arg+3, 0);
         func = hookf; mask = makemask(smask, count);
       }
    -  gethooktable(L1);
    -  lua_pushlightuserdata(L1, L1);
    +  gethooktable(L);
    +  lua_pushlightuserdata(L, L1);
       lua_pushvalue(L, arg+1);
    -  lua_xmove(L, L1, 1);
    -  lua_rawset(L1, -3);  /* set new hook */
    -  lua_pop(L1, 1);  /* remove hook table */
    +  lua_rawset(L, -3);  /* set new hook */
    +  lua_pop(L, 1);  /* remove hook table */
       lua_sethook(L1, func, mask, count);  /* set hooks */
       return 0;
     }
    @@ -288,11 +287,10 @@
       if (hook != NULL && hook != hookf)  /* external hook? */
         lua_pushliteral(L, "external hook");
       else {
    -    gethooktable(L1);
    -    lua_pushlightuserdata(L1, L1);
    -    lua_rawget(L1, -2);   /* get hook */
    -    lua_remove(L1, -2);  /* remove hook table */
    -    lua_xmove(L1, L, 1);
    +    gethooktable(L);
    +    lua_pushlightuserdata(L, L1);
    +    lua_rawget(L, -2);   /* get hook */
    +    lua_remove(L, -2);  /* remove hook table */
       }
       lua_pushstring(L, unmakemask(mask, buff));
       lua_pushinteger(L, lua_gethookcount(L1));
    

Lua 5.1.1 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9

  1. リストコンストラクターに誤った制限があります。
    2006年6月5日にNorman Ramseyによって報告されました。Lua 5.1から存在していました。5.1.2で修正されました。

    a = {}
    a[1] = "x={1"
    for i = 2, 2^20 do
      a[i] = 1
    end
    a[#a + 1] = "}"
    s = table.concat(a, ",")
    assert(loadstring(s))()
    print(#x)
    

    パッチ

    lparser.c:
    @@ -489,7 +489,7 @@
    
     static void listfield (LexState *ls, struct ConsControl *cc) {
       expr(ls, &cc->v);
    -  luaY_checklimit(ls->fs, cc->na, MAXARG_Bx, "items in a constructor");
    +  luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
       cc->na++;
       cc->tostore++;
     }
    
  2. 一部のクロージャに関する誤ったメッセージエラー。
    2006年7月8日にShmuel Zeigermanによって報告されました。Lua 5.1から存在していました。5.1.2で修正されました。

    local Var
    local function main()
      NoSuchName (function() Var=0 end)
    end
    main()
    
    エラーメッセージは、アップバリュー'Var'(nil値)を呼び出そうとしましたです。グローバル'NoSuchName'(nil値)を呼び出そうとしましたである必要があります。

    パッチ

    ldebug.c:
    @@ -435,14 +435,16 @@
             break;
           }
           case OP_CLOSURE: {
    -        int nup;
    +        int nup, j;
             check(b < pt->sizep);
             nup = pt->p[b]->nups;
             check(pc + nup < pt->sizecode);
    -        for (; nup>0; nup--) {
    -          OpCode op1 = GET_OPCODE(pt->code[pc+nup]);
    +        for (j = 1; j <= nup; j++) {
    +          OpCode op1 = GET_OPCODE(pt->code[pc + j]);
               check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
             }
    +        if (reg != NO_REG)  /* tracing? */
    +          pc += nup;  /* do not 'execute' these pseudo-instructions */
             break;
           }
           case OP_VARARG: {
    
  3. string.format("%")が文字列を越えて読み取る可能性があります。
    2006年9月にRobertoによって報告されました。少なくとも5.0から存在していました。5.1.2で修正されました。

    print(string.format("%"))
    

    パッチ

    lstrlib.c:
    @@ -723,7 +723,7 @@
    
     static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
       const char *p = strfrmt;
    -  while (strchr(FLAGS, *p)) p++;  /* skip flags */
    +  while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++;  /* skip flags */
       if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
         luaL_error(L, "invalid format (repeated flags)");
       if (isdigit(uchar(*p))) p++;  /* skip width */
    
  4. os.dateは、結果が空の文字列の場合にエラーをスローします。
    2006年9月15日にNick Gammonによって報告されました。4.0から存在していました。5.1.2で修正されました。

    print(os.date(""))
    

    パッチ

    loslib.c:
    @@ -148,7 +148,18 @@
       else {
    -    char b[256];
    -    if (strftime(b, sizeof(b), s, stm))
    -      lua_pushstring(L, b);
    -    else
    -      return luaL_error(L, LUA_QL("date") " format too long");
    +    char cc[3];
    +    luaL_Buffer b;
    +    cc[0] = '%'; cc[2] = '\0';
    +    luaL_buffinit(L, &b);
    +    for (; *s; s++) {
    +      if (*s != '%' || *(s + 1) == '\0')  /* no conversion specifier? */
    +        luaL_addchar(&b, *s);
    +      else {
    +        size_t reslen;
    +        char buff[200];  /* should be big enough for any conversion result */
    +        cc[1] = *(++s);
    +        reslen = strftime(buff, sizeof(buff), cc, stm);
    +        luaL_addlstring(&b, buff, reslen);
    +      }
    +    }
    +    luaL_pushresult(&b);
       }
    
  5. setfenvは無効な最初の引数を受け入れます。
    2007年2月8日にDoug Rogersによって報告されました。5.0から存在していました。5.1.2で修正されました。

    setfenv(nil, {})   -- should throw an error
    

    パッチ

    lbaselib.c:
    @@ -116,3 +116,3 @@
    
    -static void getfunc (lua_State *L) {
    +static void getfunc (lua_State *L, int opt) {
       if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);
    @@ -120,3 +120,3 @@
         lua_Debug ar;
    -    int level = luaL_optint(L, 1, 1);
    +    int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);
         luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
    @@ -133,3 +133,3 @@
     static int luaB_getfenv (lua_State *L) {
    -  getfunc(L);
    +  getfunc(L, 1);
       if (lua_iscfunction(L, -1))  /* is a C function? */
    @@ -144,3 +144,3 @@
       luaL_checktype(L, 2, LUA_TTABLE);
    -  getfunc(L);
    +  getfunc(L, 0);
       lua_pushvalue(L, 2);
    
  6. 特定の状況下で、算術式のコード生成が間違っている。
    Thierry Grellier が 2007年1月19日に報告。5.1 から存在。5.1.2 で修正。

    -- use a large number of names (almost 256)
    v1=1; v2=1; v3=1; v4=1; v5=1; v6=1; v7=1; v8=1; v9=1;
    v10=1; v11=1; v12=1; v13=1; v14=1; v15=1; v16=1; v17=1;
    v18=1; v19=1; v20=1; v21=1; v22=1; v23=1; v24=1; v25=1;
    v26=1; v27=1; v28=1; v29=1; v30=1; v31=1; v32=1; v33=1;
    v34=1; v35=1; v36=1; v37=1; v38=1; v39=1; v40=1; v41=1;
    v42=1; v43=1; v44=1; v45=1; v46=1; v47=1; v48=1; v49=1;
    v50=1; v51=1; v52=1; v53=1; v54=1; v55=1; v56=1; v57=1;
    v58=1; v59=1; v60=1; v61=1; v62=1; v63=1; v64=1; v65=1;
    v66=1; v67=1; v68=1; v69=1; v70=1; v71=1; v72=1; v73=1;
    v74=1; v75=1; v76=1; v77=1; v78=1; v79=1; v80=1; v81=1;
    v82=1; v83=1; v84=1; v85=1; v86=1; v87=1; v88=1; v89=1;
    v90=1; v91=1; v92=1; v93=1; v94=1; v95=1; v96=1; v97=1;
    v98=1; v99=1; v100=1; v101=1; v102=1; v103=1; v104=1; v105=1;
    v106=1; v107=1; v108=1; v109=1; v110=1; v111=1; v112=1; v113=1;
    v114=1; v115=1; v116=1; v117=1; v118=1; v119=1; v120=1; v121=1;
    v122=1; v123=1; v124=1; v125=1; v126=1; v127=1; v128=1; v129=1;
    v130=1; v131=1; v132=1; v133=1; v134=1; v135=1; v136=1; v137=1;
    v138=1; v139=1; v140=1; v141=1; v142=1; v143=1; v144=1; v145=1;
    v146=1; v147=1; v148=1; v149=1; v150=1; v151=1; v152=1; v153=1;
    v154=1; v155=1; v156=1; v157=1; v158=1; v159=1; v160=1; v161=1;
    v162=1; v163=1; v164=1; v165=1; v166=1; v167=1; v168=1; v169=1;
    v170=1; v171=1; v172=1; v173=1; v174=1; v175=1; v176=1; v177=1;
    v178=1; v179=1; v180=1; v181=1; v182=1; v183=1; v184=1; v185=1;
    v186=1; v187=1; v188=1; v189=1; v190=1; v191=1; v192=1; v193=1;
    v194=1; v195=1; v196=1; v197=1; v198=1; v199=1; v200=1; v201=1;
    v202=1; v203=1; v204=1; v205=1; v206=1; v207=1; v208=1; v209=1;
    v210=1; v211=1; v212=1; v213=1; v214=1; v215=1; v216=1; v217=1;
    v218=1; v219=1; v220=1; v221=1; v222=1; v223=1; v224=1; v225=1;
    v226=1; v227=1; v228=1; v229=1; v230=1; v231=1; v232=1; v233=1;
    v234=1; v235=1; v236=1; v237=1; v238=1; v239=1; v240=1; v241=1;
    v242=1; v243=1; v244=1; v245=1; v246=1; v247=1; v248=1; v249=1;
    v250=1;
    v251={k1 = 1};
    v252=1;
    print(2 * v251.k1, v251.k1 * 2);   -- 2 2, OK
    v253=1;
    print(2 * v251.k1, v251.k1 * 2);   -- 1 2, ???
    

    パッチ

    lcode.c:
    @@ -657,10 +657,16 @@
       if (constfolding(op, e1, e2))
         return;
       else {
    -    int o1 = luaK_exp2RK(fs, e1);
         int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
    -    freeexp(fs, e2);
    -    freeexp(fs, e1);
    +    int o1 = luaK_exp2RK(fs, e1);
    +    if (o1 > o2) {
    +      freeexp(fs, e1);
    +      freeexp(fs, e2);
    +    }
    +    else {
    +      freeexp(fs, e2);
    +      freeexp(fs, e1);
    +    }
         e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
         e1->k = VRELOCABLE;
       }
    @@ -718,10 +724,15 @@
           luaK_exp2nextreg(fs, v);  /* operand must be on the `stack' */
           break;
         }
    -    default: {
    +    case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
    +    case OPR_MOD: case OPR_POW: {
           if (!isnumeral(v)) luaK_exp2RK(fs, v);
           break;
         }
    +    default: {
    +      luaK_exp2RK(fs, v);
    +      break;
    +    }
       }
     }
    
  7. パラメータへの nil の代入が最適化で削除される可能性がある。
    Thomas Lauer が 2007年3月21日に報告。5.1 から存在。5.1.2 で修正。

    function f (a)
      a=nil
      return a
    end
    
    print(f("test"))
    

    パッチ

    lcode.c:
    @@ -35,16 +35,20 @@
     void luaK_nil (FuncState *fs, int from, int n) {
       Instruction *previous;
       if (fs->pc > fs->lasttarget) {  /* no jumps to current position? */
    -    if (fs->pc == 0)  /* function start? */
    -      return;  /* positions are already clean */
    -    previous = &fs->f->code[fs->pc-1];
    -    if (GET_OPCODE(*previous) == OP_LOADNIL) {
    -      int pfrom = GETARG_A(*previous);
    -      int pto = GETARG_B(*previous);
    -      if (pfrom <= from && from <= pto+1) {  /* can connect both? */
    -        if (from+n-1 > pto)
    -          SETARG_B(*previous, from+n-1);
    -        return;
    +    if (fs->pc == 0) {  /* function start? */
    +      if (from >= fs->nactvar)
    +        return;  /* positions are already clean */
    +    }
    +    else {
    +      previous = &fs->f->code[fs->pc-1];
    +      if (GET_OPCODE(*previous) == OP_LOADNIL) {
    +        int pfrom = GETARG_A(*previous);
    +        int pto = GETARG_B(*previous);
    +        if (pfrom <= from && from <= pto+1) {  /* can connect both? */
    +          if (from+n-1 > pto)
    +            SETARG_B(*previous, from+n-1);
    +          return;
    +        }
           }
         }
       }
    
  8. 連結メタメソッドが数値を文字列に変換する。
    Paul Winwood が 2006年12月24日に報告。5.0 から存在。5.1.2 で修正。

    a = {}
    setmetatable(a, {__concat = function (a,b) print(type(a), type(b)) end})
    a = 4 .. a
    

    パッチ

    lvm.c:
    @@ -281,10 +281,12 @@
       do {
         StkId top = L->base + last + 1;
         int n = 2;  /* number of elements handled in this pass (at least 2) */
    -    if (!tostring(L, top-2) || !tostring(L, top-1)) {
    +    if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
           if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
             luaG_concaterror(L, top-2, top-1);
    -    } else if (tsvalue(top-1)->len > 0) {  /* if len=0, do nothing */
    +    } else if (tsvalue(top-1)->len == 0)  /* second op is empty? */
    +      (void)tostring(L, top - 2);  /* result is first op (as string) */
    +    else {
           /* at least two string values; get as many as possible */
           size_t tl = tsvalue(top-1)->len;
           char *buffer;
    
  9. loadlib.c はライブラリであり、Lua の内部(lobject.h 経由)にアクセスすべきではない。
    Jérôme Vuarand が 2007年3月25日に報告。少なくとも 5.0 から存在。5.1.2 で修正。

    例:このバグは外部の動作に影響を与えません。

    パッチ:loadlib.c で、luaO_pushfstring の出現箇所をすべて lua_pushfstring に変更してください。

Lua 5.1 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10

  1. 16ビットマシンでは、右オペランドが数値定数の and/or 式が奇妙な値になる可能性がある。
    Andreas Stenius が 2006年3月15日に報告。5.1.1 で修正。

    print(false or 0)   -- on 16-bit machines
    

    パッチ

    lcode.c:
    @@ -731,17 +731,15 @@
         case OPR_AND: {
           lua_assert(e1->t == NO_JUMP);  /* list must be closed */
           luaK_dischargevars(fs, e2);
    -      luaK_concat(fs, &e1->f, e2->f);
    -      e1->k = e2->k; e1->u.s.info = e2->u.s.info;
    -      e1->u.s.aux = e2->u.s.aux; e1->t = e2->t;
    +      luaK_concat(fs, &e2->f, e1->f);
    +      *e1 = *e2;
           break;
         }
         case OPR_OR: {
           lua_assert(e1->f == NO_JUMP);  /* list must be closed */
           luaK_dischargevars(fs, e2);
    -      luaK_concat(fs, &e1->t, e2->t);
    -      e1->k = e2->k; e1->u.s.info = e2->u.s.info;
    -      e1->u.s.aux = e2->u.s.aux; e1->f = e2->f;
    +      luaK_concat(fs, &e2->t, e1->t);
    +      *e1 = *e2;
           break;
         }
    
  2. luaL_checkudata が誤ったエラーメッセージを生成する可能性がある。
    Greg Falcon が 2006年3月21日に報告。5.1.1 で修正。

    getmetatable(io.stdin).__gc()
    --> bad argument #1 to '__gc' (FILE* expected, got table)
    

    パッチ

    lauxlib.c:
    @@ -123,11 +123,17 @@
    
     LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
       void *p = lua_touserdata(L, ud);
    -  lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */
    -  if (p == NULL || !lua_getmetatable(L, ud) || !lua_rawequal(L, -1, -2))
    -    luaL_typerror(L, ud, tname);
    -  lua_pop(L, 2);  /* remove both metatables */
    -  return p;
    +  if (p != NULL) {  /* value is a userdata? */
    +    if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */
    +      lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */
    +      if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */
    +        lua_pop(L, 2);  /* remove both metatables */
    +        return p;
    +      }
    +    }
    +  }
    +  luaL_typerror(L, ud, tname);  /* else error */
    +  return NULL;  /* to avoid warnings */
     }
    
  3. Lua と DirectX の両方を使用する Windows アプリケーションで、予期しない動作が発生する可能性がある。これは Lua のバグではありません! 問題は、DirectX が Lua が依存する ABI に違反していることです。

    パッチ:最も簡単な解決策は、D3DCREATE_FPU_PRESERVE フラグを使用して DirectX を使用することです。それ以外の場合は、luaconf.h 内の lua_number2int の定義を以下のように変更できます

    #define lua_number2int(i,d)   __asm fld d   __asm fistp i
    
  4. string.formatE の '%q' オプションが '\r' を正しく処理しない。
    FleetCommand が 2006年4月1日に報告。5.1.1 で修正。

    local s = "a string with \r and \n and \r\n and \n\r"
    local c = string.format("return %q", s)
    assert(assert(loadstring(c))() == s)
    

    パッチ

    lstrlib.c:
    @@ -703,6 +703,10 @@
             luaL_addchar(b, *s);
             break;
           }
    +      case '\r': {
    +        luaL_addlstring(b, "\\r", 2);
    +        break;
    +      }
           case '\0': {
             luaL_addlstring(b, "\\000", 4);
             break;
    
  5. luaL_dofileluaL_dostring は、チャンクによって返されるすべての値を返す必要がある。
    mos が 2006年4月11日に報告。5.1.1 で修正。

    パッチ

    lauxlib.h:
    @@ -108,9 +108,11 @@
    
     #define luaL_typename(L,i)     lua_typename(L, lua_type(L,(i)))
    
    -#define luaL_dofile(L, fn)     (luaL_loadfile(L, fn) || lua_pcall(L, 0, 0, 0))
    +#define luaL_dofile(L, fn) \
    +       (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
    
    -#define luaL_dostring(L, s)    (luaL_loadstring(L, s) || lua_pcall(L, 0, 0, 0))
    +#define luaL_dostring(L, s) \
    +       (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
    
     #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
    
  6. ガベージコレクタがファイナライザを十分に補正しない。
    Roberto が 2006年5月に報告。5.1.1 で修正。

    パッチ

    lgc.c:
    @@ -322,4 +322,6 @@
    
    -static void propagateall (global_State *g) {
    -  while (g->gray) propagatemark(g);
    +static size_t propagateall (global_State *g) {
    +  size_t m = 0;
    +  while (g->gray) m += propagatemark(g);
    +  return m;
     }
    @@ -542,3 +544,3 @@
       marktmu(g);  /* mark `preserved' userdata */
    -  propagateall(g);  /* remark, to propagate `preserveness' */
    +  udsize += propagateall(g);  /* remark, to propagate `preserveness' */
       cleartable(g->weak);  /* remove collected objects from weak tables */
    @@ -592,2 +594,4 @@
             GCTM(L);
    +        if (g->estimate > GCFINALIZECOST)
    +          g->estimate -= GCFINALIZECOST;
    
  7. デバッグフックがコルーチンと混在すると間違った動作をする可能性がある。
    Ivko Stanilov が 2006年6月3日に報告。5.1.1 で修正。

    co = coroutine.create(function () coroutine.yield() end)
    debug.sethook(co, function() end, "lr")
    coroutine.resume(co)
    coroutine.resume(co)
    

    パッチ

    ldo.c:
    @@ -389,6 +389,7 @@
           return;
       }
       else {  /* resuming from previous yield */
    +    L->status = 0;
         if (!f_isLua(ci)) {  /* `common' yield? */
           /* finish interrupted execution of `OP_CALL' */
           lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
    @@ -399,7 +400,6 @@
         else  /* yielded inside a hook: just continue its execution */
           L->base = L->ci->base;
       }
    -  L->status = 0;
       luaV_execute(L, cast_int(L->ci - L->base_ci));
     }
    
  8. リストコンストラクタの制限が間違っている。
  9. クロージャに関係する一部のケースで、エラーメッセージが間違っている。
  10. 特定の状況下で、算術式のコード生成が間違っている。

Lua 5.0.3

これは Lua 5.0 の最後のリリースです。後で報告されたバグは、ほとんどの場合 Lua 5.1 で修正されています。

Lua 5.0.2 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8

  1. 文字列連結が算術オーバーフローを引き起こし、バッファオーバーフローにつながる可能性がある。
    Rici Lake が 2004年5月20日に報告。5.1 および 5.0.3 で修正。

    longs = string.rep("\0", 2^25)
    function catter(i)
        return assert(loadstring(
          string.format("return function(a) return a%s end",
                         string.rep("..a", i-1))))()
    end
    rep129 = catter(129)
    rep129(longs)
    

    パッチ

    lvm.c:
    @@ -321,15 +321,15 @@
             luaG_concaterror(L, top-2, top-1);
         } else if (tsvalue(top-1)->tsv.len > 0) {  /* if len=0, do nothing */
           /* at least two string values; get as many as possible */
    -      lu_mem tl = cast(lu_mem, tsvalue(top-1)->tsv.len) +
    -                  cast(lu_mem, tsvalue(top-2)->tsv.len);
    +      size_t tl = tsvalue(top-1)->tsv.len;
           char *buffer;
           int i;
    -      while (n < total && tostring(L, top-n-1)) {  /* collect total length */
    -        tl += tsvalue(top-n-1)->tsv.len;
    -        n++;
    +      /* collect total length */
    +      for (n = 1; n < total && tostring(L, top-n-1); n++) {
    +        size_t l = tsvalue(top-n-1)->tsv.len;
    +        if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow");
    +        tl += l;
           }
    -      if (tl > MAX_SIZET) luaG_runerror(L, "string size overflow");
           buffer = luaZ_openspace(L, &G(L)->buff, tl);
           tl = 0;
           for (i=n; i>0; i--) {  /* concat all strings */
    
  2. lua_getupvaluelua_setupvalue が、小さすぎるインデックスをチェックしない。
    Mike Pall が 2004年6月6日に報告。5.1 および 5.0.3 で修正。

    debug.getupvalue(function() end, 0)
    

    パッチ

    lapi.c
    941c941
    <     if (n > f->c.nupvalues) return NULL;
    ---
    >     if (!(1 <= n && n <= f->c.nupvalues)) return NULL;
    947c947
    <     if (n > p->sizeupvalues) return NULL;
    ---
    >     if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
    
  3. 中断されたスレッドのオープンアップバリューに保持されている値が、誤って収集される可能性がある。
    Spencer Schumann が 2004年12月31日に報告。5.1 および 5.0.3 で修正。

    local thread_id = 0
    local threads = {}
    
    function fn(thread)
        thread_id = thread_id + 1
        threads[thread_id] = function()
                                 thread = nil
                             end
        coroutine.yield()
    end
    
    while true do
        local thread = coroutine.create(fn)
        coroutine.resume(thread, thread)
    end
    

    パッチ

    lgc.c:
    221,224c221,222
    <       if (!u->marked) {
    <         markobject(st, &u->value);
    <         u->marked = 1;
    <       }
    ---
    >       markobject(st, u->v);
    >       u->marked = 1;
    
  4. rawsetrawget が余分な引数を無視しない。
    Romulo Bahiense が 2005年3月11日に報告。5.1 および 5.0.3 で修正。

    a = {}
    rawset(a, 1, 2, 3)
    print(a[1], a[2])    -- should be 2 and nil
    

    パッチ

    lbaselib.c:
    175a176
    >   lua_settop(L, 2);
    183a185
    >   lua_settop(L, 3);
    
  5. 一度のコレクションで生き残った弱テーブルは、二度と収集されない。
    Chromix が 2006年1月2日に報告。5.1 および 5.0.3 で修正。

    a = {}
    print(gcinfo())
    for i = 1, 10000 do
      a[i] = setmetatable({}, {__mode = "v"})
    end
    collectgarbage()
    a = nil
    collectgarbage()
    print(gcinfo())
    

    パッチ

    lgc.c
    @@ -366,7 +366,7 @@
       GCObject *curr;
       int count = 0;  /* number of collected items */
       while ((curr = *p) != NULL) {
    -    if (curr->gch.marked > limit) {
    +    if ((curr->gch.marked & ~(KEYWEAK | VALUEWEAK)) > limit) {
           unmark(curr);
           p = &curr->gch.next;
         }
    
  6. ガベージコレクタがファイナライザを十分に補正しない。
  7. 一部の "not not" 式がブール値を返さない可能性がある。
    Aaron Brown が 2005年6月30日に報告。4.0 から存在。5.0.3 で修正。

    -- should print false, but prints nil
    print(not not (nil and 4))
    
  8. 一部のマシンでは、(io.popen で作成された)"パイプ付きファイル" を閉じると Lua がクラッシュする可能性がある。
    Mike Pall が 2005年5月23日に報告。5.0 から存在。5.0.3 で修正。

    f = io.popen("ls")
    f:close()
    

Lua 5.0 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12 · 13 · 14 · 15 · 16 · 17

  1. lua_closethread はマニュアルにのみ存在する。
    Nguyen Binh が 2003年4月28日に報告。5.0.2 で修正。

    解決策:マニュアルが間違っています。スレッドはガベージコレクションの対象です。

  2. 実行中のコルーチンを再開しようとすると Lua がクラッシュする。
    Alex Bilyk が 2003年5月9日に報告。5.0.2 で修正。

    function co_func (current_co)
       coroutine.resume(co)
    end
    co = coroutine.create(co_func)
    coroutine.resume(co)
    coroutine.resume(co)     --> crash
    

    パッチ

    ldo.c:
    325,326c325
    <     if (nargs >= L->top - L->base)
    <       luaG_runerror(L, "cannot resume dead coroutine");
    ---
    >     lua_assert(nargs < L->top - L->base);
    329c328,329
    <   else if (ci->state & CI_YIELD) {  /* inside a yield? */
    ---
    >   else {  /* inside a yield */
    >     lua_assert(ci->state & CI_YIELD);
    344,345d343
    <   else
    <     luaG_runerror(L, "cannot resume non-suspended coroutine");
    351a350,358
    > static int resume_error (lua_State *L, const char *msg) {
    >   L->top = L->ci->base;
    >   setsvalue2s(L->top, luaS_new(L, msg));
    >   incr_top(L);
    >   lua_unlock(L);
    >   return LUA_ERRRUN;
    > }
    >
    >
    355a363,368
    >   if (L->ci == L->base_ci) {
    >     if (nargs >= L->top - L->base)
    >       return resume_error(L, "cannot resume dead coroutine");
    >   }
    >   else if (!(L->ci->state & CI_YIELD))  /* not inside a yield? */
    >     return resume_error(L, "cannot resume non-suspended coroutine");
    
  3. ファイルなしで file:close を呼び出すことができない(クラッシュが発生する)。
    Tuomo Valkonen が 2003年5月27日に報告。5.0.2 で修正。

    > io.stdin.close()    -- correct call should be io.stdin:close()
    

    パッチ

    liolib.c:
    161c161
    <   if (lua_isnone(L, 1)) {
    ---
    >   if (lua_isnone(L, 1) && lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) {
    
  4. C 関数が現在のトップよりも大きいスタックを持つ可能性がある。
    Alex Bilyk が 2003年6月9日に報告。5.0.2 で修正。

    例:lua.c を変更して、lua_assert が定義された状態で Lua を再コンパイルする必要があります。

    lua.c:
    381a382
    >   lua_checkstack(l, 1000);
    

    パッチ

    lgc.c:
    247c247
    <     if (!(ci->state & CI_C) && lim < ci->top)
    ---
    >     if (lim < ci->top)
    
  5. コルーチンが中断されると、'pc' アドレスが無効になる。
    Nick Trout が 2003年7月7日に報告。5.0.2 で修正。

    function g(x)
        coroutine.yield(x)
    end
    
    function f (i)
      debug.sethook(print, "l")
      for j=1,1000 do
        g(i+j)
      end
    end
    
    co = coroutine.wrap(f)
    co(10)
    pcall(co)
    pcall(co)
    

    パッチ

    lvm.c:
    402d401
    <   L->ci->u.l.pc = &pc;
    405a405
    >   L->ci->u.l.pc = &pc;
    676,678c676
    <           lua_assert(ci->u.l.pc == &pc &&
    <                      ttisfunction(ci->base - 1) &&
    <                      (ci->state & CI_SAVEDPC));
    ---
    >           lua_assert(ttisfunction(ci->base - 1) && (ci->state & CI_SAVEDPC));
    
  6. 収集されるユーザーデータが新しい GC しきい値にカウントされ、メモリ消費量が増加する。
    Roberto が 2003年7月25日に報告。5.0.2 で修正。

    a = newproxy(true)
    getmetatable(a).__gc = function () end
    for i=1,10000000 do
      newproxy(a)
      if math.mod(i, 10000) == 0 then print(gcinfo()) end
    end
    

    パッチ

    lgc.h:
    18c18
    < void luaC_separateudata (lua_State *L);
    ---
    > size_t luaC_separateudata (lua_State *L);
    
    lgc.c:
    113c113,114
    < void luaC_separateudata (lua_State *L) {
    ---
    > size_t luaC_separateudata (lua_State *L) {
    >   size_t deadmem = 0;
    127a129
    >       deadmem += sizeudata(gcotou(curr)->uv.len);
    136a139
    >   return deadmem;
    390c393
    < static void checkSizes (lua_State *L) {
    ---
    > static void checkSizes (lua_State *L, size_t deadmem) {
    400c403
    <   G(L)->GCthreshold = 2*G(L)->nblocks;  /* new threshold */
    ---
    >   G(L)->GCthreshold = 2*G(L)->nblocks - deadmem;  /* new threshold */
    454c457,458
    < static void mark (lua_State *L) {
    ---
    > static size_t mark (lua_State *L) {
    >   size_t deadmem;
    467c471
    <   luaC_separateudata(L);  /* separate userdata to be preserved */
    ---
    >   deadmem = luaC_separateudata(L);  /* separate userdata to be preserved */
    475a480
    >   return deadmem;
    480c485
    <   mark(L);
    ---
    >   size_t deadmem = mark(L);
    482c487
    <   checkSizes(L);
    ---
    >   checkSizes(L, deadmem);
    
  7. IBM AS400 (OS400) の sizeof(void *)==16 であり、'%p' は 'printf' で最大 60 文字を生成し、tostring でバッファオーバーフローを引き起こす可能性がある。
    David Burgess が 2003年8月25日に報告。5.0.2 で修正。

    print{}  -- on an AS400 machine
    

    パッチ

    liolib.c:
    178c178
    <   char buff[32];
    ---
    >   char buff[128];
    
    lbaselib.c:
    327c327
    <   char buff[64];
    ---
    >   char buff[128];
    
  8. 構文 local function がスタックサイズを増やさない。
    Rici Lake が 2003年9月26日に報告。5.0.2 で修正。

    -- must run this with precompiled code
    local a,b,c
    local function d () end
    

    パッチ

    lparser.c:
    1143a1144
    >   FuncState *fs = ls->fs;
    1145c1146,1147
    <   init_exp(&v, VLOCAL, ls->fs->freereg++);
    ---
    >   init_exp(&v, VLOCAL, fs->freereg);
    >   luaK_reserveregs(fs, 1);
    1148c1150,1152
    <   luaK_storevar(ls->fs, &v, &b);
    ---
    >   luaK_storevar(fs, &v, &b);
    >   /* debug information will only see the variable after this point! */
    >   getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;
    
  9. カウントフックが設定されていない場合に呼び出される可能性がある。
    Andreas Stenius が 2003年10月6日に報告。5.0.2 で修正。


    以下のようにフックを設定してください。カウントフックを設定せずに正のカウントを使用することは奇妙ですが、間違っているわけではありません。

    lua_sethook(L, my_hook, LUA_MASKLINE | LUA_MASKRET, 1);
    

    パッチ

    lvm.c:
    69c69
    <   if (mask > LUA_MASKLINE) {  /* instruction-hook set? */
    ---
    >   if (mask & LUA_MASKCOUNT) {  /* instruction-hook set? */
    
  10. dofile が引数なしで呼び出された場合、1つの戻り値を無視する。
    Frederico Abraham が 2004年1月15日に報告。5.0.2 で修正。

    a,b = dofile()   --< here you enter `return 1,2,3 <eof>'
    print(a,b)   --> 2   3   (should be 1 and 2)
    

    パッチ

    lbaselib.c:
    313a314
    >   int n = lua_gettop(L);
    317c318
    <   return lua_gettop(L) - 1;
    ---
    >   return lua_gettop(L) - n;
    
  11. 文字列連結が算術オーバーフローを引き起こし、バッファオーバーフローにつながる可能性がある。
  12. lua_getupvaluelua_setupvalue が、小さすぎるインデックスをチェックしない。
  13. rawsetrawget が余分な引数を無視しない。
  14. 一度のコレクションで生き残った弱テーブルは、二度と収集されない。
  15. ガベージコレクタがファイナライザを十分に補正しない。
  16. 一部の "not not" 式がブール値を返さない可能性がある。
  17. 一部のマシンでは、(io.popen で作成された) "パイプ付きファイル" を閉じると Lua がクラッシュする可能性がある。

Lua 4.0

Lua 4.0 のほとんどのバグは、Lua 4.0.1 以降のバージョンで修正されています。

  1. パーサーが 'return' の後の ';' を受け入れなかった。
    lhf が 2000年11月29日に報告。4.0.1 で修正。
  2. 'read' が失敗した場合、nil を返す必要がある(値を返さないのではなく)。
    Carlos Cassino が 2000年12月22日に報告。5.0 で修正。
  3. lua_pushuserdata(L,NULL) が機能しない。
    Edgar Toernig が 2001年2月1日に報告。4.0.1 で修正。
  4. while 1 dostring[[print('hello\n')]] end がメモリを再利用しない。
    Andrew Paton が 2001年2月2日に報告。4.0.1 で修正。
  5. C の ESC (プリコンパイルされたコードを開始する) は \27 ではなく \33 です。
    Edgar Toernig が 2001年2月6日に報告。4.0.1 で修正。
  6. '%a' のエラーメッセージが間違った行番号を示していた。
    Leonardo Constantino が 2001年7月10日に報告。4.0.1 で修正。
  7. rawget/rawset が余分な引数を受け取るとクラッシュする。
    Eric Mauger が 2001年12月21日に報告。4.0.1 で修正。
  8. 行フックが間違った 'ar' を取得する。
    Daniel Sinclair が 2002年6月19日に報告。4.0.1 で修正。
  9. 'protectedparser' が GC を実行し、その後 'filename' (parse_file 内) を収集する可能性がある。
    Alex Bilyk が 2002年6月19日に報告。4.0.1 で修正。
  10. ULONG_MAX>>10int に収まらない可能性がある。
    Jeff Petkau が 2002年11月21日に報告。5.0 で修正。