node-shark
node-shark copied to clipboard
Repeated calls of .dissect() segfault
The slightly modified example from README.md segfaults after a number of successful iterations, sometimes as early as at the second call of dissector.dissect(buffer), sometimes only after tens, hundreds, or even thousands of iterations where the dissection were successful.
My test program below segfaults in various setups, e.g. on Ubuntu 10.04 and 12.09, Mac OSX, using Node 0.8.22, and libwireshark-1.8.x and -1.9.x.
The output below from a sample run includes the stack-traces which are produced by segfault-handler
. The stack-traces I have seen so far stipulate that segfault consistently occurs at the same locations, independent of how many iterations before it had completed successfully before.
Commenting out //console.log(packet.dns.Queries);
does not improve the situation. This hints that the problem is somewhere with dissection.cpp
, or the run-time environment of Node, and not in dissectionNode.cpp
, nor the Lazy*.cpp
parts, which parse the dissectors' output after the dissection only if the returned packet
object is referenced in console.log()
.
Currently, I am out of ideas how to debug this problem further. Might it be related to some limits, such as stack or heap sizes? This would explain why the number of successful iterations changes from time to time.
Many thanks for any suggestions, investigative questions, or assistance!
'use strict';
var nodeshark = require("nodeshark");
var dissector = new nodeshark.Dissector(nodeshark.LINK_LAYER_TYPE_ETHERNET);
var SegfaultHandler = require('segfault-handler');
SegfaultHandler.registerHandler();
var buffer = new Buffer([
0x58, 0x6d, 0x8f, 0x67, 0x8a, 0x4d, 0x00, 0x1b, 0x21, 0xcf, 0xa1, 0x00, 0x08, 0x00, 0x45, 0x00,
0x00, 0x3b, 0xd1, 0xb0, 0x40, 0x00, 0x40, 0x11, 0xc5, 0xde, 0x0a, 0x14, 0x08, 0x65, 0xc0, 0xa8,
0xd0, 0x01, 0xc5, 0x32, 0x00, 0x35, 0x00, 0x27, 0xa3, 0x5b, 0x65, 0x89, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6c, 0x04, 0x6c, 0x69, 0x76, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
]);
var i= 0;
setInterval(function(){
var packet= dissector.dissect(buffer);
//console.log(packet);
//console.log(packet.dns);
console.log(packet.dns.Queries);
console.log('--------------------------------------');
console.log(i++);
}, 100);
Here is the output with the stacktrace from node 0.8.22 with libwireshark-1.9.x under Ubuntu 10.04 where the program consistently crashes upon the third call of dissector.dissect(buffer), after it had run successfully in the first two iterations.
Note that #define SHOW_CREATES
has been enabled in nodeshark/src/dissectorNode.cpp
which produces additional output ***** create ...
.
$ node nodesharkReadmeShortManyTimesMini.js
***** create child: dns
***** create child: Queries
***** create rawData: rawData
***** create datasource: Frame
***** create child: mail.live.com: type A, class IN
***** create rawData: rawData
***** create child: dns.qry.name
***** create rawData: rawData
***** create child: dns.qry.type
***** create rawData: rawData
***** create child: dns.qry.class
***** create rawData: rawData
{ sizeInPacket: 19,
positionInPacket: 54,
abbreviation: 'Queries',
dataSource: 'Frame',
representation: 'Queries',
value: '04:6d:61:69:6c:04:6c:69:76:65:03:63:6f:6d:00:00:01:00:01',
rawData: <Buffer 04 6d 61 69 6c 04 6c 69 76 65 03 63 6f 6d 00 00 01 00 01>,
'mail.live.com: type A, class IN':
{ sizeInPacket: 19,
positionInPacket: 54,
abbreviation: 'mail.live.com: type A, class IN',
dataSource: 'Frame',
representation: 'mail.live.com: type A, class IN',
value: '04:6d:61:69:6c:04:6c:69:76:65:03:63:6f:6d:00:00:01:00:01',
rawData: <Buffer 04 6d 61 69 6c 04 6c 69 76 65 03 63 6f 6d 00 00 01 00 01>,
'dns.qry.name':
{ sizeInPacket: 15,
positionInPacket: 54,
abbreviation: 'dns.qry.name',
dataSource: 'Frame',
value: 'mail.live.com',
rawData: <Buffer 04 6d 61 69 6c 04 6c 69 76 65 03 63 6f 6d 00> },
'dns.qry.type':
{ sizeInPacket: 2,
positionInPacket: 69,
abbreviation: 'dns.qry.type',
dataSource: 'Frame',
representation: 'Type: A (Host address)',
value: '0x0001',
rawData: <Buffer 00 01> },
'dns.qry.class':
{ sizeInPacket: 2,
positionInPacket: 71,
abbreviation: 'dns.qry.class',
dataSource: 'Frame',
value: '0x0001',
rawData: <Buffer 00 01> } } }
--------------------------------------
0
***** create child: dns
***** create child: Queries
***** create rawData: rawData
***** create datasource: Frame
***** create child: mail.live.com: type A, class IN
***** create rawData: rawData
***** create child: dns.qry.name
***** create rawData: rawData
***** create child: dns.qry.type
***** create rawData: rawData
***** create child: dns.qry.class
***** create rawData: rawData
{ sizeInPacket: 19,
positionInPacket: 54,
abbreviation: 'Queries',
dataSource: 'Frame',
representation: 'Queries',
value: '04:6d:61:69:6c:04:6c:69:76:65:03:63:6f:6d:00:00:01:00:01',
rawData: <Buffer 04 6d 61 69 6c 04 6c 69 76 65 03 63 6f 6d 00 00 01 00 01>,
'mail.live.com: type A, class IN':
{ sizeInPacket: 19,
positionInPacket: 54,
abbreviation: 'mail.live.com: type A, class IN',
dataSource: 'Frame',
representation: 'mail.live.com: type A, class IN',
value: '04:6d:61:69:6c:04:6c:69:76:65:03:63:6f:6d:00:00:01:00:01',
rawData: <Buffer 04 6d 61 69 6c 04 6c 69 76 65 03 63 6f 6d 00 00 01 00 01>,
'dns.qry.name':
{ sizeInPacket: 15,
positionInPacket: 54,
abbreviation: 'dns.qry.name',
dataSource: 'Frame',
value: 'mail.live.com',
rawData: <Buffer 04 6d 61 69 6c 04 6c 69 76 65 03 63 6f 6d 00> },
'dns.qry.type':
{ sizeInPacket: 2,
positionInPacket: 69,
abbreviation: 'dns.qry.type',
dataSource: 'Frame',
representation: 'Type: A (Host address)',
value: '0x0001',
rawData: <Buffer 00 01> },
'dns.qry.class':
{ sizeInPacket: 2,
positionInPacket: 71,
abbreviation: 'dns.qry.class',
dataSource: 'Frame',
value: '0x0001',
rawData: <Buffer 00 01> } } }
--------------------------------------
1
PID 19248 received SIGSEGV for address: 0x0
/home/rs/Gremlins/node.js/node_modules/segfault-handler/build/Release/segfault-handler-native.node(+0x1061)[0x7f77231f4061]
/lib/libpthread.so.0(+0xf8f0)[0x7f772b6c38f0]
/lib/libc.so.6(+0x84652)[0x7f772b3b2652]
/lib/libglib-2.0.so.0(g_strdup+0x22)[0x7f7725927222]
/usr/local/lib/libwireshark.so.0(+0x129563b)[0x7f772852b63b]
/usr/local/lib/libwireshark.so.0(proto_tree_add_string+0x1e6)[0x7f77284f3716]
/usr/local/lib/libwireshark.so.0(proto_tree_add_string_format+0xb5)[0x7f77284f3805]
/usr/local/lib/libwireshark.so.0(+0x14d6548)[0x7f772876c548]
/usr/local/lib/libwireshark.so.0(+0x1246a68)[0x7f77284dca68]
/usr/local/lib/libwireshark.so.0(+0x1247346)[0x7f77284dd346]
/usr/local/lib/libwireshark.so.0(call_dissector_with_data+0x21)[0x7f77284dd4f1]
/usr/local/lib/libwireshark.so.0(+0x1249074)[0x7f77284df074]
/usr/local/lib/libwireshark.so.0(epan_dissect_run+0x44)[0x7f77284d4514]
/home/rs/Gremlins/node.js/node_modules/nodeshark/build/Release/nodeshark_bindings.node(_ZN9Dissector7dissectERKN2v89ArgumentsE+0x629)[0x7f772b1234d7]
[0x3984cc0b8839]
On another machine, with different versions of Linux, gcc, libwireshark 1.8.x instead of 1.9.x, dissect() still segfaults at exactly the same point after a number of initial successful iterations.
Looking at disassembled code (using objdump -dS libX.so ...
), the stack-trace reveals that strlen()
fails while copying a value with g_strdup()
in calls to string_fvalue_set()
of wireshark/epan/ftypes/ftype-string.c :
static void
string_fvalue_free(fvalue_t *fv)
{
g_free(fv->value.string);
}
static void
string_fvalue_set(fvalue_t *fv, gpointer value, gboolean already_copied)
{
DISSECTOR_ASSERT(value != NULL);
DISSECTOR_ASSERT(!already_copied);
/* Free up the old value, if we have one */
string_fvalue_free(fv);
fv->value.string = (gchar *)g_strdup(value);
}
Could that be a some "use after free" problem?
In the mean-time, I have ported my code (a dissector for a proprietary protocol) to wireshark-1.10.9, and corrected some mistakes in my pull-requests for node-shark from last year.
However, repeated calling of node-shark from node v0.10.32 still core dumps after some iterations. Thus I have learned how to build node-shark and Node with debug info, and to get post-mortem back-traces, etc., such as the one below.
Do you have any hints or questions what I should look for after Node crashes at around the 50th iteration, apparently somewhere in DissectorNode::New
, or in Garbage Collection?
gdb --args node_g --gdbjit nodesharkReadmeShortManyTimes
...
Reading symbols from node_g...done.
(gdb) start
Temporary breakpoint 1 at 0xc860df: file ../src/node_main.cc, line 65.
Starting program: /usr/local/bin/node_g --gdbjit nodesharkReadmeShortManyTimes
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Temporary breakpoint 1, main (argc=3, argv=0x7fffffffe638) at ../src/node_main.cc:65
65 return node::Start(argc, argv);
(gdb) c
Continuing.
[New Thread 0x7ffff7ff7700 (LWP 28183)]
0
1
2
3
...
51
52
*** Error in `/usr/local/bin/node_g': munmap_chunk(): invalid pointer: 0x00007fffffffdaa0 ***
Program received signal SIGABRT, Aborted.
0x00007ffff6c00bb9 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007ffff6c00bb9 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00007ffff6c03fc8 in __GI_abort () at abort.c:89
#2 0x00007ffff6c3de14 in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7ffff6d4c668 "*** Error in `%s': %s: 0x%s ***\n")
at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007ffff6c48b77 in malloc_printerr (action=<optimized out>, str=0x7ffff6d4c9e8 "munmap_chunk(): invalid pointer", ptr=<optimized out>)
at malloc.c:4996
#4 0x00007fffed292416 in frame_data_destroy (fdata=0x139a030) at frame_data.c:356
#5 0x00007ffff67c0e82 in DissectorNode::~DissectorNode (this=0x25655c0, __in_chrg=<optimized out>) at ../src/dissectorNode.cpp:377
#6 0x00007ffff67c0ede in DissectorNode::~DissectorNode (this=0x25655c0, __in_chrg=<optimized out>) at ../src/dissectorNode.cpp:381
#7 0x0000000000c70604 in node::ObjectWrap::WeakCallback (value=..., data=0x25655c0) at ../src/node_object_wrap.h:121
#8 0x0000000000966826 in v8::internal::GlobalHandles::Node::PostGarbageCollectionProcessing (this=0x135f620, isolate=0x130f070, global_handles=0x1328ac0)
at ../deps/v8/src/global-handles.cc:233
#9 0x00000000009652ff in v8::internal::GlobalHandles::PostGarbageCollectionProcessing (this=0x1328ac0, collector=v8::internal::SCAVENGER)
at ../deps/v8/src/global-handles.cc:551
#10 0x0000000000975d3c in v8::internal::Heap::PerformGarbageCollection (this=0x130f080, collector=v8::internal::SCAVENGER, tracer=0x7fffffffd5f0)
at ../deps/v8/src/heap.cc:946
#11 0x0000000000975095 in v8::internal::Heap::CollectGarbage (this=0x130f080, space=v8::internal::NEW_SPACE, collector=v8::internal::SCAVENGER,
gc_reason=0xdb398c "allocation failure", collector_reason=0x0) at ../deps/v8/src/heap.cc:633
#12 0x00000000008ebfc2 in v8::internal::Heap::CollectGarbage (this=0x130f080, space=v8::internal::NEW_SPACE, gc_reason=0xdb398c "allocation failure")
at ../deps/v8/src/heap-inl.h:436
#13 0x0000000000a72bd1 in v8::internal::JSObject::TransformToFastProperties (object=..., unused_property_fields=0) at ../deps/v8/src/objects.cc:3394
#14 0x0000000000898b3a in v8::Object::SetAccessor (this=0x135cfa0, name=...,
getter=0x7ffff67c029a <DissectorNode::dataSourceGetter(v8::Local<v8::String>, v8::AccessorInfo const&)>,
setter=0x7ffff67c0420 <DissectorNode::dataSourceSetter(v8::Local<v8::String>, v8::Local<v8::Value>, v8::AccessorInfo const&)>, data=...,
settings=v8::DEFAULT, attributes=v8::None) at ../deps/v8/src/api.cc:3121
#15 0x00007ffff67bf95a in DissectorNode::New (root=0x0, fdata=0x2652e20, edt=0x7fffffffda10, node=0x26a2190) at ../src/dissectorNode.cpp:130
#16 0x00007ffff67bdcc0 in Dissector::dissect (args=...) at ../src/dissector.cpp:211
#17 0x00002bbf94797339 in ?? ()
#18 0x00007fffffffdd58 in ?? ()
#19 0x00007fffffffdd60 in ?? ()
#20 0x0000000000000001 in ?? ()
#21 0x0000000000000000 in ?? ()
(gdb)