So here's an elusive one that can be triggered every now and then by the
new regression test. Once an AgentX session is opened and we send an
invalid packet appl_agentx_recv() goes to appl_agentx_free(), since
there's no recovery. appl_agentx_free() tries to neatly close all
open sessions by sending a close-pdu, followed by calling
appl_agentx_send() directly.
However, if the socket has been closed in the meantime we hit
appl_agentx_send()'s error path, which also calls appl_agentx_free().
This in turn leads to use after free cases.
To fix this don't call appl_agentx_send() directly anymore, but just
schedule it via conn_wev. To make sure as much data as possible is
written out do a last unchecked courtesy flush before definitively
freeing the connection. Since appl_agentx_forceclose() arms conn_wev
move the event_del() calls down in appl_agentx_free().
Other calls of appl_agentx_send() should be fine, but just convert
all of them to be consistent and safe.
OK?
martijn@
Index: usr.sbin/snmpd/application_agentx.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/application_agentx.c,v
retrieving revision 1.12
diff -u -p -r1.12 application_agentx.c
--- usr.sbin/snmpd/application_agentx.c 24 Oct 2023 14:11:14 -0000 1.12
+++ usr.sbin/snmpd/application_agentx.c 26 Oct 2023 08:43:02 -0000
@@ -254,9 +254,6 @@ appl_agentx_free(struct appl_agentx_conn
{
struct appl_agentx_session *session;
- event_del(&(conn->conn_rev));
- event_del(&(conn->conn_wev));
-
while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) {
if (conn->conn_ax == NULL)
appl_agentx_session_free(session);
@@ -265,7 +262,12 @@ appl_agentx_free(struct appl_agentx_conn
reason);
}
+ event_del(&(conn->conn_rev));
+ event_del(&(conn->conn_wev));
+
RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
+ if (conn->conn_ax != NULL)
+ (void)ax_send(conn->conn_ax);
ax_free(conn->conn_ax);
if (conn->conn_backend)
fatalx("AgentX(%"PRIu32"): disappeared unexpected",
@@ -419,7 +421,7 @@ appl_agentx_recv(int fd, short event, vo
pdu->ap_header.aph_transactionid,
pdu->ap_header.aph_packetid, smi_getticks(),
APPL_ERROR_NOERROR, 0, NULL, 0);
- appl_agentx_send(-1, EV_WRITE, conn);
+ event_add(&(conn->conn_wev), NULL);
break;
case AX_PDU_TYPE_INDEXALLOCATE:
case AX_PDU_TYPE_INDEXDEALLOCATE:
@@ -431,7 +433,7 @@ appl_agentx_recv(int fd, short event, vo
APPL_ERROR_PROCESSINGERROR, 1,
pdu->ap_payload.ap_vbl.ap_varbind,
pdu->ap_payload.ap_vbl.ap_nvarbind);
- appl_agentx_send(-1, EV_WRITE, conn);
+ event_add(&(conn->conn_wev), NULL);
break;
case AX_PDU_TYPE_ADDAGENTCAPS:
case AX_PDU_TYPE_REMOVEAGENTCAPS:
@@ -451,7 +453,7 @@ appl_agentx_recv(int fd, short event, vo
pdu->ap_header.aph_transactionid,
pdu->ap_header.aph_packetid, smi_getticks(),
error, 0, NULL, 0);
- appl_agentx_send(-1, EV_WRITE, conn);
+ event_add(&(conn->conn_wev), NULL);
ax_pdu_free(pdu);
if (session == NULL || error != APPL_ERROR_PARSEERROR)
@@ -560,13 +562,13 @@ appl_agentx_open(struct appl_agentx_conn
ax_response(conn->conn_ax, session->sess_id,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
- appl_agentx_send(-1, EV_WRITE, conn);
+ event_add(&(conn->conn_wev), NULL);
return;
fail:
ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0);
- appl_agentx_send(-1, EV_WRITE, conn);
+ event_add(&(conn->conn_wev), NULL);
if (session != NULL)
free(session->sess_descr.aos_string);
free(session);
@@ -592,7 +594,7 @@ appl_agentx_close(struct appl_agentx_ses
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
smi_getticks(), error, 0, NULL, 0);
- appl_agentx_send(-1, EV_WRITE, conn);
+ event_add(&(conn->conn_wev), NULL);
if (error == APPL_ERROR_NOERROR)
return;
@@ -612,7 +614,7 @@ appl_agentx_forceclose(struct appl_backe
session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
ax_close(session->sess_conn->conn_ax, session->sess_id,
(enum ax_close_reason) reason);
- appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+ event_add(&(session->sess_conn->conn_wev), NULL);
strlcpy(name, session->sess_backend.ab_name, sizeof(name));
appl_agentx_session_free(session);
@@ -671,7 +673,7 @@ appl_agentx_register(struct appl_agentx_
ax_response(session->sess_conn->conn_ax, session->sess_id,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
smi_getticks(), error, 0, NULL, 0);
- appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+ event_add(&(session->sess_conn->conn_wev), NULL);
}
void
@@ -698,7 +700,7 @@ appl_agentx_unregister(struct appl_agent
ax_response(session->sess_conn->conn_ax, session->sess_id,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
smi_getticks(), error, 0, NULL, 0);
- appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+ event_add(&(session->sess_conn->conn_wev), NULL);
}
#define AX_PDU_FLAG_INDEX (AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)
@@ -748,7 +750,7 @@ appl_agentx_get(struct appl_backend *bac
requestid, context, srl, nsr) == -1)
appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
else
- appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+ event_add(&(session->sess_conn->conn_wev), NULL);
free(srl);
if (context != NULL)
free(context->aos_string);
@@ -801,7 +803,7 @@ appl_agentx_getnext(struct appl_backend
requestid, context, srl, nsr) == -1)
appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
else
- appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+ event_add(&(session->sess_conn->conn_wev), NULL);
free(srl);
if (context != NULL)
free(context->aos_string);
Index: regress/usr.sbin/snmpd/Makefile
===================================================================
RCS file: /cvs/src/regress/usr.sbin/snmpd/Makefile,v
retrieving revision 1.5
diff -u -p -r1.5 Makefile
--- regress/usr.sbin/snmpd/Makefile 24 Oct 2023 14:34:40 -0000 1.5
+++ regress/usr.sbin/snmpd/Makefile 26 Oct 2023 08:43:02 -0000
@@ -51,6 +51,7 @@ AGENTX_TARGETS+= agentx_ping_new_index
AGENTX_TARGETS+= agentx_ping_any_index
AGENTX_TARGETS+= agentx_ping_nbo_nnbo
AGENTX_TARGETS+= agentx_ping_nnbo_nbo
+AGENTX_TARGETS+= agentx_ping_invalid_version_close
AGENTX_TARGETS+= agentx_close_notopen
AGENTX_TARGETS+= agentx_close_reasonother
AGENTX_TARGETS+= agentx_close_reasonparseerror
Index: regress/usr.sbin/snmpd/agentx.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/snmpd/agentx.c,v
retrieving revision 1.1
diff -u -p -r1.1 agentx.c
--- regress/usr.sbin/snmpd/agentx.c 24 Oct 2023 14:34:40 -0000 1.1
+++ regress/usr.sbin/snmpd/agentx.c 26 Oct 2023 08:43:02 -0000
@@ -706,6 +706,45 @@ agentx_ping_nnbo_nbo(void)
close(s);
}
+/*
+ * Test that everything continues running in double exception condition
+ */
+void
+agentx_ping_invalid_version_close(void)
+{
+ struct sockaddr_storage ss;
+ struct message msg = {};
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ socklen_t salen;
+ int snmp_s, ax_s;
+ uint32_t sessionid, packetid;
+ struct varbind varbind = {
+ .type = TYPE_NULL,
+ .name = OID_STRUCT(MIB_SUBAGENT_PING, 10, 0),
+ };
+ int32_t requestid;
+ char buf[1024];
+ size_t n;
+
+ ax_s = agentx_connect(axsocket);
+ sessionid = agentx_open(ax_s, 0, 0,
+ OID_ARG(MIB_SUBAGENT_PING, 10), __func__);
+ message_add_header(&msg, 0xFF, AGENTX_PING_PDU, INSTANCE_REGISTRATION,
+ sessionid, 0, packetid);
+
+ agentx_write(ax_s, &msg);
+ close(ax_s);
+
+ salen = snmp_resolve(SOCK_DGRAM, hostname, servname, sa);
+ snmp_s = snmp_connect(SOCK_DGRAM, sa, salen);
+ requestid = snmpv2_get(snmp_s, community, 0, &varbind, 1);
+
+ varbind.type = TYPE_NOSUCHOBJECT;
+
+ snmpv2_response_validate(snmp_s, 1000, community, requestid, 0, 0,
+ &varbind, 1);
+}
+
void
agentx_close_notopen(void)
{
Index: regress/usr.sbin/snmpd/regress.h
===================================================================
RCS file: /cvs/src/regress/usr.sbin/snmpd/regress.h,v
retrieving revision 1.1
diff -u -p -r1.1 regress.h
--- regress/usr.sbin/snmpd/regress.h 24 Oct 2023 14:34:40 -0000 1.1
+++ regress/usr.sbin/snmpd/regress.h 26 Oct 2023 08:43:02 -0000
@@ -170,6 +170,7 @@ void agentx_ping_new_index(void);
void agentx_ping_any_index(void);
void agentx_ping_nbo_nnbo(void);
void agentx_ping_nnbo_nbo(void);
+void agentx_ping_invalid_version_close(void);
void agentx_close_notopen(void);
void agentx_close_reasonother(void);
void agentx_close_reasonparseerror(void);
Index: regress/usr.sbin/snmpd/snmpd_regress.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/snmpd/snmpd_regress.c,v
retrieving revision 1.1
diff -u -p -r1.1 snmpd_regress.c
--- regress/usr.sbin/snmpd/snmpd_regress.c 24 Oct 2023 14:34:40 -0000
1.1
+++ regress/usr.sbin/snmpd/snmpd_regress.c 26 Oct 2023 08:43:02 -0000
@@ -36,6 +36,7 @@ const struct {
{ "agentx_ping_any_index", agentx_ping_any_index },
{ "agentx_ping_nbo_nnbo", agentx_ping_nbo_nnbo },
{ "agentx_ping_nnbo_nbo", agentx_ping_nnbo_nbo },
+ { "agentx_ping_invalid_version_close",
agentx_ping_invalid_version_close },
{ "agentx_close_notopen", agentx_close_notopen },
{ "agentx_close_reasonother", agentx_close_reasonother },
{ "agentx_close_reasonparseerror", agentx_close_reasonparseerror },