394591: [4.7] snmpd crashes at OS reboot 429671: snmpd exits with buffer overlow after stress Source: upstream, http://sourceforge.net/tracker/index.php?func=detail&aid=1678788&group_id=12694&atid=312694 Modified-By: Jan Safranek <jsafrane@redhat.com> (smux_snmp_process: length is now ssize_t, to be able to check errors from recv(), adapted to RHEL5) diff -up net-snmp-5.3.2.2/agent/mibgroup/smux/smux.c.smux-select net-snmp-5.3.2.2/agent/mibgroup/smux/smux.c --- net-snmp-5.3.2.2/agent/mibgroup/smux/smux.c.smux-select 2007-10-16 09:17:51.000000000 +0200 +++ net-snmp-5.3.2.2/agent/mibgroup/smux/smux.c 2008-07-10 17:39:09.000000000 +0200 @@ -350,7 +350,7 @@ var_smux_write(int action, u_char buf[SMUXMAXPKTSIZE], *ptr, sout[3], type; int reterr; size_t var_len, datalen, name_length, packet_len; - ssize_t len; + ssize_t len, tmp_len; long reqid, errsts, erridx; u_char var_type, *dataptr; @@ -426,12 +426,17 @@ var_smux_write(int action, */ if ((len = recv(rptr->sr_fd, buf, SMUXMAXPKTSIZE, MSG_PEEK)) <= 0) { + if ((len == -1) && ((errno == EINTR) || (errno == EAGAIN))) + { + continue; + } DEBUGMSGTL(("smux", "[var_smux_write] peek failed or timed out\n")); /* * do we need to do a peer cleanup in this case?? */ smux_peer_cleanup(rptr->sr_fd); + smux_snmp_select_list_del(rptr->sr_fd); return SNMP_ERR_GENERR; } @@ -455,13 +460,19 @@ var_smux_write(int action, /* * receive the first packet */ - if ((len = recv(rptr->sr_fd, buf, len, 0)) <= 0) { + tmp_len = len; + do + { + len = tmp_len; + len = recv(rptr->sr_fd, buf, len, 0); + } + while((len == -1) && ((errno == EINTR) || (errno == EAGAIN))); + + if (len <= 0) { DEBUGMSGTL(("smux", "[var_smux_write] recv failed or timed out\n")); - /* - * do we need to do a peer cleanup in this case?? - */ smux_peer_cleanup(rptr->sr_fd); + smux_snmp_select_list_del(rptr->sr_fd); return SNMP_ERR_GENERR; } @@ -593,7 +604,13 @@ smux_accept(int sd) /* * now block for an OpenPDU */ - if ((length = recv(fd, (char *) data, SMUXMAXPKTSIZE, 0)) <= 0) { + do + { + length = recv(fd, (char *) data, SMUXMAXPKTSIZE, 0); + } + while((length == -1) && ((errno == EINTR) || (errno == EAGAIN))); + + if (length <= 0) { DEBUGMSGTL(("smux", "[smux_accept] peer on fd %d died or timed out\n", fd)); @@ -660,10 +677,45 @@ smux_accept(int sd) int smux_process(int fd) { - int length; + int length, tmp_length; u_char data[SMUXMAXPKTSIZE]; + u_char type, *ptr; + size_t packet_len; + + do + { + length = recv(fd, (char *) data, SMUXMAXPKTSIZE, MSG_PEEK); + } + while((length == -1) && ((errno == EINTR) || (errno == EAGAIN))); + + if (length <= 0) + { + snmp_log_perror("[smux_process] peek failed"); + smux_peer_cleanup(fd); + return -1; + } + + /* + * determine if we received more than one packet + */ + packet_len = length; + ptr = asn_parse_header(data, &packet_len, &type); + packet_len += (ptr - data); + if (length > packet_len) { + /* + * set length to receive only the first packet + */ + length = packet_len; + } + + tmp_length = length; + do + { + length = tmp_length; + length = recv(fd, (char *) data, length, 0); + } + while((length == -1) && ((errno == EINTR) || (errno == EAGAIN))); - length = recv(fd, (char *) data, SMUXMAXPKTSIZE, 0); if (length <= 0) { /* * the peer went away, close this descriptor @@ -729,7 +781,16 @@ smux_pdu_process(int fd, u_char * data, break; case SMUX_TRAP: snmp_log(LOG_INFO, "Got trap from peer on fd %d\n", fd); - ptr = smux_trap_process(ptr, &len); + if (ptr != 0) + { + DEBUGMSGTL(("smux", "[smux_pdu_process] call smux_trap_process.\n")); + ptr = smux_trap_process(ptr, &len); + } + else + { + DEBUGMSGTL(("smux", "[smux_pdu_process] smux_trap_process not called: ptr=NULL.\n")); + DEBUGMSGTL(("smux", "[smux_pdu_process] Error: \n%s\n", snmp_api_errstring(0))); + } /* * watch out for close on top of this...should return correct end */ @@ -1266,7 +1327,8 @@ smux_snmp_process(int exact, size_t * return_len, u_char * return_type, int sd) { u_char packet[SMUXMAXPKTSIZE], *ptr, result[SMUXMAXPKTSIZE]; - int length = SMUXMAXPKTSIZE; + ssize_t length = SMUXMAXPKTSIZE; + int tmp_length; u_char type; size_t packet_len; @@ -1302,10 +1364,18 @@ smux_snmp_process(int exact, * peek at what's received */ length = recv(sd, (char *) result, SMUXMAXPKTSIZE, MSG_PEEK); - if (length < 0) { - snmp_log_perror("[smux_snmp_process] peek failed"); - smux_peer_cleanup(sd); - return NULL; + if (length <= 0) { + if ((length == -1) && ((errno == EINTR) || (errno == EAGAIN))) + { + continue; + } + else + { + snmp_log_perror("[smux_snmp_process] peek failed"); + smux_peer_cleanup(sd); + smux_snmp_select_list_del(sd); + return NULL; + } } DEBUGMSGTL(("smux", "[smux_snmp_process] Peeked at %d bytes\n", @@ -1328,11 +1398,19 @@ smux_snmp_process(int exact, /* * receive the first packet */ - length = recv(sd, (char *) result, length, 0); - if (length < 0) { - snmp_log_perror("[smux_snmp_process] recv failed"); - smux_peer_cleanup(sd); - return NULL; + tmp_length = length; + do + { + length = tmp_length; + length = recv(sd, (char *) result, length, 0); + } + while((length == -1) && ((errno == EINTR) || (errno == EAGAIN))); + + if (length <= 0) { + snmp_log_perror("[smux_snmp_process] recv failed"); + smux_peer_cleanup(sd); + smux_snmp_select_list_del(sd); + return NULL; } DEBUGMSGTL(("smux", "[smux_snmp_process] Received %d bytes\n", @@ -1950,3 +2028,50 @@ smux_trap_process(u_char * rsp, size_t * } +#define NUM_SOCKETS 32 +static int sdlist[NUM_SOCKETS], sdlen = 0; + +int smux_snmp_select_list_add(int sd) +{ + if (sdlen < NUM_SOCKETS) + { + sdlist[sdlen++] = sd; + return(1); + } + return(0); +} + +int smux_snmp_select_list_del(int sd) +{ + int i, found=0; + + for (i = 0; i < (sdlen); i++) { + if (sdlist[i] == sd) + { + sdlist[i] = 0; + found = 1; + } + if ((found) &&(i < (sdlen - 1))) + sdlist[i] = sdlist[i + 1]; + } + if (found) + { + sdlen--; + return(1); + } + return(0); +} + +int smux_snmp_select_list_get_length() +{ + return(sdlen); +} + +int smux_snmp_select_list_get_SD_from_List(int pos) +{ + if (pos < NUM_SOCKETS) + { + return(sdlist[pos]); + } + return(0); +} diff -up net-snmp-5.3.2.2/agent/mibgroup/smux/smux.h.smux-select net-snmp-5.3.2.2/agent/mibgroup/smux/smux.h --- net-snmp-5.3.2.2/agent/mibgroup/smux/smux.h.smux-select 2005-02-25 22:47:25.000000000 +0100 +++ net-snmp-5.3.2.2/agent/mibgroup/smux/smux.h 2008-07-10 17:35:55.000000000 +0200 @@ -67,3 +67,16 @@ extern void smux_parse_peer_auth(con extern void smux_free_peer_auth(void); extern void send_enterprise_trap_vars(int, int, oid *, int, netsnmp_variable_list *); + +/* Add socket-fd to list */ +int smux_snmp_select_list_add(int sd); + +/* Remove socket-fd from list */ +int smux_snmp_select_list_del(int sd); + +/* Returns the count of added socket-fd's in the list */ +int smux_snmp_select_list_get_length(); + +/* Returns the socket-fd number from the position of the list */ +int smux_snmp_select_list_get_SD_from_List(int pos); + diff -up net-snmp-5.3.2.2/agent/snmpd.c.smux-select net-snmp-5.3.2.2/agent/snmpd.c --- net-snmp-5.3.2.2/agent/snmpd.c.smux-select 2007-05-17 15:53:50.000000000 +0200 +++ net-snmp-5.3.2.2/agent/snmpd.c 2008-07-10 17:35:55.000000000 +0200 @@ -197,12 +197,9 @@ extern char **argvrestartp; extern char *argvrestart; extern char *argvrestartname; -#define NUM_SOCKETS 32 - #ifdef USING_SMUX_MODULE #include <mibgroup/smux/smux.h> -static int sdlist[NUM_SOCKETS], sdlen = 0; -#endif /* USING_SMUX_MODULE */ +#endif /* USING_SMUX_MODULE */ /* * Prototypes. @@ -1111,9 +1108,14 @@ receive(void) FD_SET(smux_listen_sd, &readfds); numfds = smux_listen_sd >= numfds ? smux_listen_sd + 1 : numfds; - for (i = 0; i < sdlen; i++) { - FD_SET(sdlist[i], &readfds); - numfds = sdlist[i] >= numfds ? sdlist[i] + 1 : numfds; + + for (i = 0; i < smux_snmp_select_list_get_length(); i++) { + sd = smux_snmp_select_list_get_SD_from_List(i); + if (sd != 0) + { + FD_SET(sd, &readfds); + numfds = sd >= numfds ? sd + 1 : numfds; + } } } #endif /* USING_SMUX_MODULE */ @@ -1135,13 +1137,11 @@ receive(void) * handle the SMUX sd's */ if (smux_listen_sd >= 0) { - for (i = 0; i < sdlen; i++) { - if (FD_ISSET(sdlist[i], &readfds)) { - if (smux_process(sdlist[i]) < 0) { - for (; i < (sdlen - 1); i++) { - sdlist[i] = sdlist[i + 1]; - } - sdlen--; + for (i = 0; i < smux_snmp_select_list_get_length(); i++) { + sd = smux_snmp_select_list_get_SD_from_List(i); + if (FD_ISSET(sd, &readfds)) { + if (smux_process(sd) < 0) { + smux_snmp_select_list_del(sd); } } } @@ -1150,10 +1150,11 @@ receive(void) */ if (FD_ISSET(smux_listen_sd, &readfds)) { if ((sd = smux_accept(smux_listen_sd)) >= 0) { - sdlist[sdlen++] = sd; + smux_snmp_select_list_add(sd); } } } + #endif /* USING_SMUX_MODULE */ netsnmp_dispatch_external_events(&count, &readfds, &writefds, &exceptfds);