You must post issues only here. Questions, ideas must be posted in discussions.
Please answer the following before submitting your issue:
- What version of GopherLua are you using? :
ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6
- What version of Go are you using? : 1.22.3
- What operating system and processor architecture are you using? : Mac M1 ARM 64
- What did you do? : See below
- What did you expect to see? : See below
- What did you see instead? : See below
There is a bug in gopher lua master lastest ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6.
Under certain conditions, the compiler can produce invalid code, resulting in a nil pointer deference panic.
The following code will reproduce the bug:
id = "foo"
function get_def()
return {}
end
function test()
local def = id ~= nil and get_def() or nil
if def ~= nil then
print("def is not nil")
end
end
test()
Invoke with:
> ./glua test3.lua
runtime error: invalid memory address or nil pointer dereference
stack traceback:
test3.lua:9: in function 'test'
test3.lua:14: in main chunk
[G]: ?
The issue is caused by incorrect byte code generation, due to another issue with the LOADNIL merging optimisation (see previously resolved issue #495).
The micro code generated by the above should generate:
[001] GETGLOBAL | 0, 0; R(0) := Gbl[Kst(0)] (line:8)
[002] LOADNIL | 1, 1, 0; R(1) := ... := R(1) := nil (line:8)
[003] EQ | 1, 0, 1; if ((RK(0) == RK(1)) ~= 1) then pc++ (line:8)
[004] JMP | 0, 4; pc+=4 (line:8)
[005] GETGLOBAL | 0, 1; R(0) := Gbl[Kst(1)] (line:8)
[006] CALL | 0, 1, 2; R(0) ... R(0+2-2) := R(0)(R(0+1) ... R(0+1-1)) (line:8)
[007] TESTSET | 0, 0, 1; if (R(0) <=> 1) then R(0) := R(0) else pc++ (line:8)
[008] JMP | 0, 1; pc+=1 (line:8) >>>>v CORRECT JMP TARGET
[009] LOADNIL | 0, 0, 0; R(0) := ... := R(0) := nil (line:8) v
[010] LOADNIL | 1, 1, 0; R(1) := ... := R(1) := nil (line:9) <<<<<
[011] EQ | 1, 0, 1; if ((RK(0) == RK(1)) ~= 1) then pc++ (line:9)
[012] JMP | 0, 3; pc+=3 (line:9)
[013] GETGLOBAL | 1, 2; R(1) := Gbl[Kst(2)] (line:10)
[014] LOADK | 2, 3; R(2) := Kst(3) (line:10)
[015] CALL | 1, 2, 1; R(1) ... R(1+1-2) := R(1)(R(1+1) ... R(1+2-1)) (line:10)
[016] RETURN | 0, 1, 0; return R(0) ... R(0+1-2) (line:12)
; end of function
But instead, it generates:
[001] GETGLOBAL | 0, 0; R(0) := Gbl[Kst(0)] (line:8)
[002] LOADNIL | 1, 1, 0; R(1) := ... := R(1) := nil (line:8)
[003] EQ | 1, 0, 1; if ((RK(0) == RK(1)) ~= 1) then pc++ (line:8)
[004] JMP | 0, 4; pc+=4 (line:8)
[005] GETGLOBAL | 0, 1; R(0) := Gbl[Kst(1)] (line:8)
[006] CALL | 0, 1, 2; R(0) ... R(0+2-2) := R(0)(R(0+1) ... R(0+1-1)) (line:8)
[007] TESTSET | 0, 0, 1; if (R(0) <=> 1) then R(0) := R(0) else pc++ (line:8)
[008] JMP | 0, 1; pc+=1 (line:8) >>>>v INCORRECT JMP TARGET
[009] LOADNIL | 0, 1, 0; R(0) := ... := R(1) := nil (line:8) v
[010] EQ | 1, 0, 1; if ((RK(0) == RK(1)) ~= 1) then pc++ (line:9) <<<<<
[011] JMP | 0, 3; pc+=3 (line:9)
[012] GETGLOBAL | 1, 2; R(1) := Gbl[Kst(2)] (line:10)
[013] LOADK | 2, 3; R(2) := Kst(3) (line:10)
[014] CALL | 1, 2, 1; R(1) ... R(1+1-2) := R(1)(R(1+1) ... R(1+2-1)) (line:10)
[015] RETURN | 0, 1, 0; return R(0) ... R(0+1-2) (line:12)
; end of function
Note, the LOADNIL instructions on lines 9 and 10 have been merged by the optimiser, but this is incorrect. The
2nd LOADNIL instruction was a jump target for the JMP on line 8, and merging it into the prior instruction meant
the JMP missed its target, and that the the LOADNIL for reg 1 was skipped. This resulted in an uninitialised
register value being used in subsequent operations, causing the panic.
Summary
The LOADNIL merging optimisation merges multiple LOADNIL instructions of consecutive registers into a single
instruction, however it does not take into account if any of the LOADNIL instructions are jump targets, and so breaks
the logic.
You must post issues only here. Questions, ideas must be posted in discussions.
Please answer the following before submitting your issue:
ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6There is a bug in gopher lua master lastest
ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6.Under certain conditions, the compiler can produce invalid code, resulting in a nil pointer deference panic.
The following code will reproduce the bug:
Invoke with:
The issue is caused by incorrect byte code generation, due to another issue with the
LOADNILmerging optimisation (see previously resolved issue #495).The micro code generated by the above should generate:
But instead, it generates:
Note, the
LOADNILinstructions on lines 9 and 10 have been merged by the optimiser, but this is incorrect. The2nd
LOADNILinstruction was a jump target for theJMPon line 8, and merging it into the prior instruction meantthe
JMPmissed its target, and that the theLOADNILfor reg 1 was skipped. This resulted in an uninitialisedregister value being used in subsequent operations, causing the panic.
Summary
The
LOADNILmerging optimisation merges multipleLOADNILinstructions of consecutive registers into a singleinstruction, however it does not take into account if any of the
LOADNILinstructions are jump targets, and so breaksthe logic.