Package: libsnmp-dev
Version: 5.7.2.1~dfsg-5
Severity: important
Tags: patch

Dear Maintainer,

I was debugging a sub agent running on a thread started by a larger
application.
If the process used up more file descriptors than FD_SETSIZE before starting
the thread which was running the sub agent, the sub agent crashed on the
init_snmp() function. This should not be a problem since versions over NetSNMP
5.5 can and should use netsnmp_large_fd_set struct to deal with large file
descriptors.

Debugging the issue I've found that the functions used to manipulate the large
file descriptor sets ( netsnmp_large_fd_setfd(),  netsnmp_large_fd_clr(),
netsnmp_large_fd_is_set(),  netsnmp_large_fd_set_resize() ) use the macros
FD_SET, FD_CLR, FD_ISSET. These macros should be size independent, however in
newer versions of libc library they have an inbuilt buffer overflow protection
which tests agains the FD_SETSIZE, when manipulating a file descriptor from the
set.

Also the functions snmp_synch_response_cb() and snmp_sess_synch_response()
still use the standard fd_set struct, wich causes an infinite loop if the
response is expected on a file descriptor larger than FD_SETSIZE. This is
aready fixed in upstream PATCH 3394386, I've just used the fix.

Attached a suggested patch.

-- System Information:
Debian Release: jessie/sid
  APT prefers trusty-updates
  APT policy: (500, 'trusty-updates'), (500, 'trusty-security'), (500,
'trusty'), (100, 'trusty-backports')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 3.13.0-30-generic (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages libsnmp-dev depends on:
ii  libc6-dev        2.19-0ubuntu6
ii  libsensors4-dev  1:3.3.4-2ubuntu1
ii  libsnmp30        5.7.2.1~dfsg-5
ii  libssl-dev       1.0.1f-1ubuntu2.4
ii  libwrap0-dev     7.6.q-25
ii  procps           1:3.3.9-1ubuntu2

libsnmp-dev recommends no packages.

libsnmp-dev suggests no packages.
commit 530958fd97c34ebb213f4fb82746af126b00eb14
Author: Petr Zajicek <petr.zaji...@nangu.tv>
Date:   Tue Jul 15 15:20:21 2014 +0200

    Bug 36980: Improved netsnmp_large_fd_set struct support in sub agents.
    
    Changes to be committed:
    	modified:   snmplib/large_fd_set.c
    	modified:   snmplib/snmp_client.c

diff --git a/snmplib/large_fd_set.c b/snmplib/large_fd_set.c
index 32f57b3..ffe37f8 100644
--- a/snmplib/large_fd_set.c
+++ b/snmplib/large_fd_set.c
@@ -79,6 +79,10 @@ netsnmp_large_fd_is_set(SOCKET fd, netsnmp_large_fd_set * fdset)
 
 #else
 
+const unsigned int number_of_bits = (8 * (int) sizeof (__fd_mask));
+inline unsigned int pos_in_array( int fd ) { return fd / number_of_bits; }
+inline __fd_mask get_mask_for_fd( int fd ) { return (__fd_mask) ( 1UL << (fd % number_of_bits) ); }
+
 void
 netsnmp_large_fd_setfd(int fd, netsnmp_large_fd_set * fdset)
 {
@@ -87,7 +91,7 @@ netsnmp_large_fd_setfd(int fd, netsnmp_large_fd_set * fdset)
     while (fd >= (int)fdset->lfs_setsize)
         netsnmp_large_fd_set_resize(fdset, 2 * (fdset->lfs_setsize + 1));
 
-    FD_SET(fd, fdset->lfs_setptr);
+    ((__fd_mask*)(fdset->lfs_setptr))[ pos_in_array( fd ) ] |= get_mask_for_fd( fd );
 }
 
 void
@@ -96,7 +100,9 @@ netsnmp_large_fd_clr(int fd, netsnmp_large_fd_set * fdset)
     netsnmp_assert(fd >= 0);
 
     if ((unsigned)fd < fdset->lfs_setsize)
-        FD_CLR(fd, fdset->lfs_setptr);
+    {
+        ((__fd_mask*)(fdset->lfs_setptr))[ pos_in_array( fd ) ] &= ~get_mask_for_fd( fd );
+    }
 }
 
 int
@@ -104,7 +110,10 @@ netsnmp_large_fd_is_set(int fd, netsnmp_large_fd_set * fdset)
 {
     netsnmp_assert(fd >= 0);
 
-    return (unsigned)fd < fdset->lfs_setsize && FD_ISSET(fd, fdset->lfs_setptr);
+    if( (unsigned)fd > fdset->lfs_setsize )
+        return 0;
+
+    return ((__fd_mask*)(fdset->lfs_setptr))[ pos_in_array( fd ) ] & get_mask_for_fd( fd );
 }
 
 #endif
@@ -182,7 +191,7 @@ netsnmp_large_fd_set_resize(netsnmp_large_fd_set * fdset, int setsize)
          * resized *fdset but that were not defined in the original *fdset.
          */
         for (i = fdset->lfs_setsize; i < setsize; i++)
-            FD_CLR(i, fdset->lfs_setptr);
+            ((__fd_mask*)(fdset->lfs_setptr))[ pos_in_array( i ) ] &= ~get_mask_for_fd( i );
     }
 #endif
 
diff --git a/snmplib/snmp_client.c b/snmplib/snmp_client.c
index c1aa9c4..998776f 100644
--- a/snmplib/snmp_client.c
+++ b/snmplib/snmp_client.c
@@ -97,6 +97,7 @@ SOFTWARE.
 #include <net-snmp/library/mib.h>
 #include <net-snmp/library/snmp_logging.h>
 #include <net-snmp/library/snmp_assert.h>
+#include <net-snmp/library/large_fd_set.h>
 #include <net-snmp/pdu_api.h>
 
 netsnmp_feature_child_of(snmp_client_all, libnetsnmp)
@@ -110,17 +111,6 @@ netsnmp_feature_child_of(row_create, snmp_client_all)
 #define BSD4_2
 #endif
 
-#ifndef FD_SET
-
-typedef long    fd_mask;
-#define NFDBITS	(sizeof(fd_mask) * NBBY)        /* bits per mask */
-
-#define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
-#define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
-#define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
-#define FD_ZERO(p)	memset((p), 0, sizeof(*(p)))
-#endif
-
 /*
  * Prototype definitions 
  */
@@ -1029,13 +1019,13 @@ snmp_synch_response_cb(netsnmp_session * ss,
                        netsnmp_pdu *pdu,
                        netsnmp_pdu **response, snmp_callback pcb)
 {
-    struct synch_state lstate, *state;
-    snmp_callback   cbsav;
-    void           *cbmagsav;
-    int             numfds, count;
-    fd_set          fdset;
-    struct timeval  timeout, *tvp;
-    int             block;
+    struct synch_state    lstate, *state;
+    snmp_callback         cbsav;
+    void                 *cbmagsav;
+    int                   numfds, count;
+    netsnmp_large_fd_set  fdset;
+    struct timeval        timeout, *tvp;
+    int                   block;
 
     memset((void *) &lstate, 0, sizeof(lstate));
     state = &lstate;
@@ -1043,6 +1033,7 @@ snmp_synch_response_cb(netsnmp_session * ss,
     cbmagsav = ss->callback_magic;
     ss->callback = pcb;
     ss->callback_magic = (void *) state;
+    netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
 
     if ((state->reqid = snmp_send(ss, pdu)) == 0) {
         snmp_free_pdu(pdu);
@@ -1052,17 +1043,17 @@ snmp_synch_response_cb(netsnmp_session * ss,
 
     while (state->waiting) {
         numfds = 0;
-        FD_ZERO(&fdset);
+        NETSNMP_LARGE_FD_ZERO(&fdset);
         block = NETSNMP_SNMPBLOCK;
         tvp = &timeout;
         timerclear(tvp);
-        snmp_sess_select_info_flags(0, &numfds, &fdset, tvp, &block,
+        snmp_sess_select_info2_flags(0, &numfds, &fdset, tvp, &block,
                                     NETSNMP_SELECT_NOALARMS);
         if (block == 1)
             tvp = NULL;         /* block without timeout */
-        count = select(numfds, &fdset, NULL, NULL, tvp);
+        count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp);
         if (count > 0) {
-            snmp_read(&fdset);
+            snmp_read2(&fdset);
         } else {
             switch (count) {
             case 0:
@@ -1101,6 +1092,7 @@ snmp_synch_response_cb(netsnmp_session * ss,
     *response = state->pdu;
     ss->callback = cbsav;
     ss->callback_magic = cbmagsav;
+    netsnmp_large_fd_set_cleanup(&fdset);
     return state->status;
 }
 
@@ -1115,14 +1107,14 @@ int
 snmp_sess_synch_response(void *sessp,
                          netsnmp_pdu *pdu, netsnmp_pdu **response)
 {
-    netsnmp_session *ss;
-    struct synch_state lstate, *state;
-    snmp_callback   cbsav;
-    void           *cbmagsav;
-    int             numfds, count;
-    fd_set          fdset;
-    struct timeval  timeout, *tvp;
-    int             block;
+    netsnmp_session      *ss;
+    struct synch_state    lstate, *state;
+    snmp_callback         cbsav;
+    void                 *cbmagsav;
+    int                   numfds, count;
+    netsnmp_large_fd_set  fdset;
+    struct timeval        timeout, *tvp;
+    int                   block;
 
     ss = snmp_sess_session(sessp);
     if (ss == NULL) {
@@ -1135,6 +1127,7 @@ snmp_sess_synch_response(void *sessp,
     cbmagsav = ss->callback_magic;
     ss->callback = snmp_synch_input;
     ss->callback_magic = (void *) state;
+    netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
 
     if ((state->reqid = snmp_sess_send(sessp, pdu)) == 0) {
         snmp_free_pdu(pdu);
@@ -1144,17 +1137,17 @@ snmp_sess_synch_response(void *sessp,
 
     while (state->waiting) {
         numfds = 0;
-        FD_ZERO(&fdset);
+        NETSNMP_LARGE_FD_ZERO(&fdset);
         block = NETSNMP_SNMPBLOCK;
         tvp = &timeout;
         timerclear(tvp);
-        snmp_sess_select_info_flags(sessp, &numfds, &fdset, tvp, &block,
+        snmp_sess_select_info2_flags(sessp, &numfds, &fdset, tvp, &block,
                                     NETSNMP_SELECT_NOALARMS);
         if (block == 1)
             tvp = NULL;         /* block without timeout */
-        count = select(numfds, &fdset, NULL, NULL, tvp);
+        count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp);
         if (count > 0) {
-            snmp_sess_read(sessp, &fdset);
+            snmp_sess_read2(sessp, &fdset);
         } else
             switch (count) {
             case 0:
@@ -1185,6 +1178,7 @@ snmp_sess_synch_response(void *sessp,
     *response = state->pdu;
     ss->callback = cbsav;
     ss->callback_magic = cbmagsav;
+    netsnmp_large_fd_set_cleanup(&fdset);
     return state->status;
 }
 

Reply via email to