Menu

#2860 perl segmentation fault if any outstanding callbacks during global destruction

perl
open
nobody
perl (81)
5
2018-04-24
2018-04-24
No

If I create some async perl code, but don't call SNMP::MainLoop() or e.g. exit in a callback, then perl dies with a Segmentation fault.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/perl -w
use strict;
use SNMP;

# A strange thing...
#
# using any of many non-trivial modules seems to do the trick of making SNMP
# cause a segmentation fault. At least Moo does for me. But removing the use
# statement and it works in my tests so far.
use Moo;

my $sess = SNMP::Session->new(
    'DestHost' => '1.2.3.4',
    'Version' => '2c',
    'Community' => 'public',
);

# Setting up a callback
$sess->get( [ [ 'ifInOctets', 1 ] ], sub { } );
# But not starting the MainLoop causes SNMP to segmentation fault
# SNMP::MainLoop();

I've looked in SNMP.xs and when it dies, sess_ref can be NULL or ((HV*)SvRV(sess_ref)) can be NULL.

My testing suggests that this is because during global destruction, the sess_ref could've been destroyed first.

In the supplied patch, we test for ${^GLOBAL_PHASE} eq "DESTRUCT" and if true, it doesn't try to call any remaining callbacks. Patch (also attached):

--- net-snmp-5.7.2.1+dfsg.orig/perl/SNMP/SNMP.xs    2018-04-24 13:17:18.495389000 +0200
+++ net-snmp-5.7.2.1+dfsg/perl/SNMP/SNMP.xs 2018-04-24 15:44:05.451389000 +0200
@@ -1199,6 +1199,20 @@
   netsnmp_transport *transport = NULL;

   SV* cb = ((struct snmp_xs_cb_data*)cb_data)->perl_cb;
+  // During global destruction,
+  // sess_ref can be NULL or
+  // ((HV*)SvRV(sess_ref)) can be NULL
+  // So test for global destruction
+  // ${^GLOBAL_PHASE} starts with a CTRL-G == chr(7)
+  // see perldoc perlvar
+  SV* global_phase_sv = get_sv("\x07LOBAL_PHASE" , 0);
+  if (global_phase_sv != NULL) {
+    const char *global_phase = SvPVutf8_nolen(global_phase_sv);
+    if (!strcmp(global_phase, "DESTRUCT")) {
+      return 1;
+    }
+  }
+
   SV* sess_ref = ((struct snmp_xs_cb_data*)cb_data)->sess_ref;
   SV **err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1);
   SV **err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1);

I've applied this same patch against net-snmp-5.4.4.tar.gz and net-snmp-5.8.pre2.tar.gz where it also worked. Running on debian jessie / 8.

1 Attachments

Discussion


Log in to post a comment.