Hello,

I'm using the Perl module SNMP.pm in asynchronous mode to query some
values from some SNMP agents periodically in a daemon.  This used to
crash after some time, so I ran the whole thing (my daemon in Perl)
under gdb to get backtraces and investigate the cause.

The SNMP.pm uses XS code (which is translated to C code during build) to
call snmplib.  The XS code contains a callback function, __snmp_xs_cb,
which is supposed to be called back exactly once by snmplib: when the
SNMP reply was received or the request timed out.  __snmp_xs_cb would
then call a callback implemented in Perl by the user of SNMP.pm.

Most of the time this works well.

But it turned out that sometimes __snmp_xs_cb would be called *twice*
(or maybe even more often) for the same SNMP request.  The problem is,
__snmp_xs_cb uses some dynamically allocated data, cb_data, which is
freed once __snmp_xs_cb is called.  So any call after the first
dereferenced a pointer to already freed memory.  That use after free
resulted in a crash.

Using gdb and inspecting the local variables in __snmp_xs_cb after a
crash I could identify the conditions when this mysterious "superfluous
callback" was called:

  int dont_free = (op==1 && reqid==0) ||
                  (pdu && pdu->command == SNMP_MSG_REPORT);

My problem is:  I cannot interpret my findings!

op==1 means NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE (I should have used the
symbolic value instead of the literal 1, but I got it from gdb), OK.
Every request is assigned a request id "reqid" by snmplib.  A reqid of 0
would be a callback unrelated to any request?  Of course not, because
cb_data belongs to a specific request! But what does this condition
indicate then?

SNMP_MSG_REPORT means some SNMPv3 problem.  __snmp_xs_cb will also call
the Perl callback in this case, but to the Perl code it just looks like
a timeout.  I think snmplib already handles this case, so it should not
reach the Perl code.  Also compare
http://sourceforge.net/p/net-snmp/patches/1256/ .  That patch even
prevents snmplib from calling its callback, fixing it one level earlier
in the call stack.  But I cannot decide which fix is right.

Attached patch prevents the use-after-free condition and I would be
grateful if you could explain to me why it works and help me prepare it
for integration into net-snmp master.

Best regards,
 Michael Bunk

From 98119418b4a020b9d08135d439b8a53ca7fd9a38 Mon Sep 17 00:00:00 2001
From: Michael Bunk <m...@computer-leipzig.com>
Date: Fri, 25 Apr 2014 15:09:19 +0200
Subject: [PATCH] Fix perl crash because of use after free

Under certain conditions (which I don't understand but which are represented by
the new variable dont_free), the callback function would be called twice.  But
for the second callback, certain perl variables were already freed, leading to
use after free and perl crashing.

This patch fixes the situation by preventing the perl variables to be freed in
the first callback, so they are still available later, when the actual SNMP
reply arrives.
---
 perl/SNMP/SNMP.xs |   22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/perl/SNMP/SNMP.xs b/perl/SNMP/SNMP.xs
index 2a734e8..a7ef97a 100644
--- a/perl/SNMP/SNMP.xs
+++ b/perl/SNMP/SNMP.xs
@@ -1198,6 +1198,7 @@ void *cb_data;
   int old_numeric, old_printfull, old_format;
   netsnmp_transport *transport = NULL;
 
+  int dont_free = (op==1 && reqid==0) || (pdu && pdu->command == SNMP_MSG_REPORT);
   SV* cb = ((struct snmp_xs_cb_data*)cb_data)->perl_cb;
   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);
@@ -1207,8 +1208,13 @@ void *cb_data;
   ENTER;
   SAVETMPS;
 
-  if (cb_data != ss->callback_magic)
+  if(dont_free)
+  {
+  }
+  else if (cb_data != ss->callback_magic)
+  {
     free(cb_data);
+  }
 
   sv_setpv(*err_str_svp, (char*)snmp_errstring(pdu->errstat));
   sv_setiv(*err_num_svp, pdu->errstat);
@@ -1365,7 +1371,12 @@ void *cb_data;
   default:;
   } /* switch op */
   if (cb_data != ss->callback_magic)
-    sv_2mortal(cb);
+  {
+    if(!dont_free)
+    {
+      sv_2mortal(cb);
+    }
+  }
   cb = __push_cb_args2(cb,
                  (SvTRUE(varlist_ref) ? sv_2mortal(varlist_ref):varlist_ref),
 	         (SvTRUE(traplist_ref) ? sv_2mortal(traplist_ref):traplist_ref));
@@ -1374,7 +1385,12 @@ void *cb_data;
   FREETMPS;
   LEAVE;
   if (cb_data != ss->callback_magic)
-    sv_2mortal(sess_ref);
+  {
+    if(!dont_free)
+    {
+      sv_2mortal(sess_ref);
+    }
+  }
   return 1;
 }
 
-- 
1.7.10.4

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

------------------------------------------------------------------------------
Start Your Social Network Today - Download eXo Platform
Build your Enterprise Intranet with eXo Platform Software
Java Based Open Source Intranet - Social, Extensible, Cloud Ready
Get Started Now And Turn Your Intranet Into A Collaboration Platform
http://p.sf.net/sfu/ExoPlatform
_______________________________________________
Net-snmp-coders mailing list
Net-snmp-coders@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/net-snmp-coders

Reply via email to