955511 - net-snmpd crash on time out 969061 - net-snmpd crash on time out 1038011 - net-snmp: snmpd crashes/hangs when AgentX subagent times-out Based on usptream commit 793d596838ff7cb48a73b675d62897c56c9e62df, heavily backported to net-snmp-5.5 diff -up net-snmp-5.7.2/agent/mibgroup/agentx/master_admin.c.disconnect-crash net-snmp-5.7.2/agent/mibgroup/agentx/master_admin.c --- net-snmp-5.7.2/agent/mibgroup/agentx/master_admin.c.disconnect-crash 2013-07-03 15:26:35.884813210 +0200 +++ net-snmp-5.7.2/agent/mibgroup/agentx/master_admin.c 2013-07-03 15:26:35.908813135 +0200 @@ -158,6 +158,7 @@ close_agentx_session(netsnmp_session * s for (sp = session->subsession; sp != NULL; sp = sp->next) { if (sp->sessid == sessid) { + netsnmp_remove_delegated_requests_for_session(sp); unregister_mibs_by_session(sp); unregister_index_by_session(sp); unregister_sysORTable_by_session(sp); diff -up net-snmp-5.7.2/agent/mibgroup/agentx/master.c.disconnect-crash net-snmp-5.7.2/agent/mibgroup/agentx/master.c --- net-snmp-5.7.2/agent/mibgroup/agentx/master.c.disconnect-crash 2013-07-03 15:26:35.000000000 +0200 +++ net-snmp-5.7.2/agent/mibgroup/agentx/master.c 2013-07-03 15:29:00.644362208 +0200 @@ -222,7 +222,7 @@ agentx_got_response(int operation, /* response is too late, free the cache */ if (magic) netsnmp_free_delegated_cache((netsnmp_delegated_cache*) magic); - return 0; + return 1; } requests = cache->requests; diff -up net-snmp-5.7.2/agent/snmp_agent.c.disconnect-crash net-snmp-5.7.2/agent/snmp_agent.c --- net-snmp-5.7.2/agent/snmp_agent.c.disconnect-crash 2013-07-03 15:26:35.893813182 +0200 +++ net-snmp-5.7.2/agent/snmp_agent.c 2013-07-03 15:28:28.979460861 +0200 @@ -1446,6 +1446,7 @@ free_agent_snmp_session(netsnmp_agent_se netsnmp_free_cachemap(asp->cache_store); asp->cache_store = NULL; } + agent_snmp_session_release_cancelled(asp); SNMP_FREE(asp); } @@ -1457,6 +1458,11 @@ netsnmp_check_for_delegated(netsnmp_agen if (NULL == asp->treecache) return 0; + + if (agent_snmp_session_is_cancelled(asp)) { + printf("request %p cancelled\n", asp); + return 0; + } for (i = 0; i <= asp->treecache_num; i++) { for (request = asp->treecache[i].requests_begin; request; @@ -1535,39 +1541,48 @@ int netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess) { netsnmp_agent_session *asp; - int count = 0; + int total_count = 0; for (asp = agent_delegated_list; asp; asp = asp->next) { /* * check each request */ + int i; + int count = 0; netsnmp_request_info *request; - for(request = asp->requests; request; request = request->next) { - /* - * check session - */ - netsnmp_assert(NULL!=request->subtree); - if(request->subtree->session != sess) - continue; + for (i = 0; i <= asp->treecache_num; i++) { + for (request = asp->treecache[i].requests_begin; request; + request = request->next) { + /* + * check session + */ + netsnmp_assert(NULL!=request->subtree); + if(request->subtree->session != sess) + continue; - /* - * matched! mark request as done - */ - netsnmp_request_set_error(request, SNMP_ERR_GENERR); - ++count; + /* + * matched! mark request as done + */ + netsnmp_request_set_error(request, SNMP_ERR_GENERR); + ++count; + } + } + if (count) { + agent_snmp_session_mark_cancelled(asp); + total_count += count; } } /* * if we found any, that request may be finished now */ - if(count) { + if(total_count) { DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session " - "%8p\n", count, sess)); - netsnmp_check_outstanding_agent_requests(); + "%8p\n", total_count, sess)); + netsnmp_check_delegated_requests(); } - return count; + return total_count; } int @@ -2739,19 +2754,11 @@ handle_var_requests(netsnmp_agent_sessio return final_status; } -/* - * loop through our sessions known delegated sessions and check to see - * if they've completed yet. If there are no more delegated sessions, - * check for and process any queued requests - */ void -netsnmp_check_outstanding_agent_requests(void) +netsnmp_check_delegated_requests(void) { netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL; - /* - * deal with delegated requests - */ for (asp = agent_delegated_list; asp; asp = next_asp) { next_asp = asp->next; /* save in case we clean up asp */ if (!netsnmp_check_for_delegated(asp)) { @@ -2790,6 +2797,22 @@ netsnmp_check_outstanding_agent_requests prev_asp = asp; } } +} + +/* + * loop through our sessions known delegated sessions and check to see + * if they've completed yet. If there are no more delegated sessions, + * check for and process any queued requests + */ +void +netsnmp_check_outstanding_agent_requests(void) +{ + netsnmp_agent_session *asp; + + /* + * deal with delegated requests + */ + netsnmp_check_delegated_requests(); /* * if we are processing a set and there are more delegated @@ -2819,7 +2842,8 @@ netsnmp_check_outstanding_agent_requests netsnmp_processing_set = netsnmp_agent_queued_list; DEBUGMSGTL(("snmp_agent", "SET request remains queued while " - "delegated requests finish, asp = %8p\n", asp)); + "delegated requests finish, asp = %8p\n", + agent_delegated_list)); break; } #endif /* NETSNMP_NO_WRITE_SUPPORT */ @@ -2880,6 +2904,11 @@ check_delayed_request(netsnmp_agent_sess case SNMP_MSG_GETBULK: case SNMP_MSG_GETNEXT: netsnmp_check_all_requests_status(asp, 0); + if (agent_snmp_session_is_cancelled(asp)) { + printf("request %p is cancelled\n", asp); + DEBUGMSGTL(("snmp_agent","canceling next walk for asp %p\n", asp)); + break; + } handle_getnext_loop(asp); if (netsnmp_check_for_delegated(asp) && netsnmp_check_transaction_id(asp->pdu->transid) != @@ -3838,4 +3867,73 @@ netsnmp_set_all_requests_error(netsnmp_a return error_value; } #endif /* NETSNMP_FEATURE_REMOVE_SET_ALL_REQUESTS_ERROR */ + +/* + * Ugly hack to fix bug #950602 and preserve ABI + * (the official patch adds netsnmp_agent_session->flags). + * We must create parallel database of netsnmp_agent_sessions + * and put cancelled requests there instead of marking + * netsnmp_agent_session->flags. + */ +static netsnmp_agent_session **cancelled_agent_snmp_sessions; +static int cancelled_agent_snmp_sessions_count; +static int cancelled_agent_snmp_sessions_max; + +int +agent_snmp_session_mark_cancelled(netsnmp_agent_session *session) +{ + DEBUGMSGTL(("agent:cancelled", "Cancelling session %p\n", session)); + if (!session) + return 0; + if (cancelled_agent_snmp_sessions_count + 1 > cancelled_agent_snmp_sessions_max) { + netsnmp_agent_session **aux; + int max = cancelled_agent_snmp_sessions_max + 10; + aux = realloc(cancelled_agent_snmp_sessions, sizeof(netsnmp_agent_session*) * max); + if (!aux) + return SNMP_ERR_GENERR; + cancelled_agent_snmp_sessions = aux; + cancelled_agent_snmp_sessions_max = max; + } + cancelled_agent_snmp_sessions[cancelled_agent_snmp_sessions_count] = session; + cancelled_agent_snmp_sessions_count++; + return 0; +} + +int +agent_snmp_session_is_cancelled(netsnmp_agent_session *session) +{ + int i; + for (i=0; i