Nmap Development mailing list archives

Re: Assertion error related to Lua garbage collection of nsock pcap objects


From: Daniel Miller <bonsaiviking () gmail com>
Date: Tue, 31 Jul 2012 07:06:51 -0500

On Mon, Jul 30, 2012 at 10:38 PM, Patrick Donnelly <batrick () batbytes com> wrote:
On Mon, Jul 30, 2012 at 6:35 PM, Daniel Miller <bonsaiviking () gmail com> wrote:
A different solution would be to make sure that the Lua garbage collector
can reach the nsock_iod from the nse_nsock_udata.

This is already in the code:

    lua_getuservalue(L, 1); /* the socket user value */
    lua_pushvalue(L, -2); /* the pcap socket nsiod */
    lua_pushboolean(L, 1); /* dummy variable */
    lua_rawset(L, -3);

The pcap nsiod is anchored in the nsock userdata's uservalue (what
used to be a userdata environment in Lua 5.1).

What is the callstack for the error?

--
- Patrick Donnelly

I've got it! But first, backtraces. The backtrace for the original
assertion error:

#0  __GI_abort () at abort.c:53
#1  0x00502095 in __assert_fail_base (fmt=0x63b8b8 "%s%s%s:%u:
%s%sAssertion `%s' failed.\n%n",
    assertion=0x8313bb8 "msiod->state != NSIOD_STATE_DELETED",
file=0x8313de0 "nsock_event.c", line=406, function=0x8313db8
"msevent_new")
    at assert.c:94
#2  0x00502147 in __GI___assert_fail (assertion=0x8313bb8
"msiod->state != NSIOD_STATE_DELETED", file=0x8313de0 "nsock_event.c",
line=406,
    function=0x8313db8 "msevent_new") at assert.c:103
#3  0x08268021 in msevent_new (nsp=0x84c2b10, type=NSE_TYPE_PCAP_READ,
msiod=0x85a8c10, timeout_msecs=90, handler=
    0x82470ef <pcap_receive_handler(nsock_pool, nsock_event, void*)>,
userdata=0x85aa70c) at nsock_event.c:406
#4  0x08269053 in nsock_pcap_read_packet (nsp=0x84c2b10, nsiod=0x85a8c10,
    handler=0x82470ef <pcap_receive_handler(nsock_pool, nsock_event,
void*)>, timeout_msecs=90, userdata=0x85aa70c) at nsock_pcap.c:342
#5  0x082474d9 in l_pcap_receive (L=0x85a7c78) at nse_nsock.cc:970
#6  0x08289ad3 in luaD_precall (L=0x85a7c78, func=0x85bc268,
nresults=4) at ldo.c:317
#7  0x082a846a in luaV_execute (L=0x85a7c78) at lvm.c:710
#8  0x0828a470 in unroll (L=0x85a7c78, ud=0x0) at ldo.c:430
#9  0x0828aaae in resume (L=0x85a7c78, ud=0x85bc278) at ldo.c:517
#10 0x08288cc4 in luaD_rawrunprotected (L=0x85a7c78, f=0x828a7c1
<resume>, ud=0x85bc278) at ldo.c:131
#11 0x0828ab5d in lua_resume (L=0x85a7c78, from=0x84bea38, nargs=5) at ldo.c:530
#12 0x082bb013 in auxresume (L=0x84bea38, co=0x85a7c78, narg=5) at lcorolib.c:31
#13 0x082bb2ff in luaB_coresume (L=0x84bea38) at lcorolib.c:53
#14 0x08289ad3 in luaD_precall (L=0x84bea38, func=0x85b8708,
nresults=2) at ldo.c:317
#15 0x082a846a in luaV_execute (L=0x84bea38) at lvm.c:710
#16 0x0828a1ae in luaD_call (L=0x84bea38, func=0x84f48b0, nResults=0,
allowyield=0) at ldo.c:393
#17 0x0828478d in lua_callk (L=0x84bea38, nargs=2, nresults=0, ctx=0,
k=0) at lapi.c:902
#18 0x0823d6fb in run_main (L=0x84bea38) at nse_main.cc:504
#19 0x08289ad3 in luaD_precall (L=0x84bea38, func=0x84f48a0,
nresults=0) at ldo.c:317
#20 0x0828a165 in luaD_call (L=0x84bea38, func=0x84f48a0, nResults=0,
allowyield=0) at ldo.c:392
#21 0x0828487c in f_call (L=0x84bea38, ud=0xbfffeb78) at lapi.c:920
#22 0x08288cc4 in luaD_rawrunprotected (L=0x84bea38, f=0x8284829
<f_call>, ud=0xbfffeb78) at ldo.c:131
#23 0x0828aec1 in luaD_pcall (L=0x84bea38, func=0x8284829 <f_call>,
u=0xbfffeb78, old_top=16, ef=8) at ldo.c:590
#24 0x082849a8 in lua_pcallk (L=0x84bea38, nargs=1, nresults=0,
errfunc=1, ctx=0, k=0) at lapi.c:946
#25 0x0823e4fa in script_scan (targets=..., scantype=SCRIPT_SCAN) at
nse_main.cc:657
#26 0x080cfe9d in nmap_main (argc=11, argv=0xbffff704) at nmap.cc:1997
#27 0x080c1501 in main (argc=11, argv=0xbffff704) at main.cc:198

The backtrace for the only call to nsi_delete:

Breakpoint 2, nsi_delete (nsockiod=0x84e8ea8, pending_response=1) at
nsock_iod.c:162
162     void nsi_delete(nsock_iod nsockiod, int pending_response) {
(gdb) bt
#0  nsi_delete (nsockiod=0x84e8ea8, pending_response=1) at nsock_iod.c:162
#1  0x082468c8 in pcap_gc (L=0x84bea38) at nse_nsock.cc:885
#2  0x08289ad3 in luaD_precall (L=0x84bea38, func=0x85af308,
nresults=0) at ldo.c:317
#3  0x0828a165 in luaD_call (L=0x84bea38, func=0x85af308, nResults=0,
allowyield=0) at ldo.c:392
#4  0x08290936 in dothecall (L=0x84bea38, ud=0x0) at lgc.c:807
#5  0x08288cc4 in luaD_rawrunprotected (L=0x84bea38, f=0x82908e7
<dothecall>, ud=0x0) at ldo.c:131
#6  0x0828aec1 in luaD_pcall (L=0x84bea38, func=0x82908e7 <dothecall>,
u=0x0, old_top=264, ef=0) at ldo.c:590
#7  0x08290b19 in GCTM (L=0x84bea38, propagateerrors=1) at lgc.c:826
#8  0x0829229c in luaC_forcestep (L=0x84bea38) at lgc.c:1153
#9  0x082850ce in lua_gc (L=0x84bea38, what=5, data=0) at lapi.c:1056
#10 0x082b8234 in luaB_collectgarbage (L=0x84bea38) at lbaselib.c:169
#11 0x08289ad3 in luaD_precall (L=0x84bea38, func=0x85af2f8,
nresults=0) at ldo.c:317
#12 0x082a846a in luaV_execute (L=0x84bea38) at lvm.c:710
#13 0x0828a1ae in luaD_call (L=0x84bea38, func=0x84e9420, nResults=0,
allowyield=0) at ldo.c:393
#14 0x0828478d in lua_callk (L=0x84bea38, nargs=2, nresults=0, ctx=0,
k=0) at lapi.c:902
#15 0x0823d6fb in run_main (L=0x84bea38) at nse_main.cc:504
#16 0x08289ad3 in luaD_precall (L=0x84bea38, func=0x84e9410,
nresults=0) at ldo.c:317
#17 0x0828a165 in luaD_call (L=0x84bea38, func=0x84e9410, nResults=0,
allowyield=0) at ldo.c:392
#18 0x0828487c in f_call (L=0x84bea38, ud=0xbfffeb78) at lapi.c:920
#19 0x08288cc4 in luaD_rawrunprotected (L=0x84bea38, f=0x8284829
<f_call>, ud=0xbfffeb78) at ldo.c:131
#20 0x0828aec1 in luaD_pcall (L=0x84bea38, func=0x8284829 <f_call>,
u=0xbfffeb78, old_top=16, ef=8) at ldo.c:590
#21 0x082849a8 in lua_pcallk (L=0x84bea38, nargs=1, nresults=0,
errfunc=1, ctx=0, k=0) at lapi.c:946
#22 0x0823e4fa in script_scan (targets=..., scantype=SCRIPT_SCAN) at
nse_main.cc:657
#23 0x080cfe9d in nmap_main (argc=11, argv=0xbffff704) at nmap.cc:1997
#24 0x080c1501 in main (argc=11, argv=0xbffff704) at main.cc:198

Ok, now! Using the uservalue is much cleaner than altering the
metatable, but the bug was that it was only set when a new nsiod was
created. If an existing nsiod could be reused, no reference was added
from the socket object to that nsiod, so when the first socket got
garbage-collected, the nsiod was no longer reachable. It didn't get
deleted immediately, since the nsock_gc routine knows that pcap nsiods
can be shared, but the next iteration of the garbage collector
wouldn't be able to reach it.

I'm attaching a patch that adds the pcap nsiod to the socket's
uservalue every time. This seems to be working well so far.

Dan

Attachment: nsock_uv.patch
Description:

_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://seclists.org/nmap-dev/

Current thread: