opensmtpd-0001: mta_handle_envelope TAILQ_FOREACH O(N²) task lookup — patch + unit test

Add patch replacing linear TAILQ_FOREACH scan in mta_handle_envelope()
with tree_get() on a per-relay task_by_msgid splay-tree index; assign
UNDF-2026-000000201. Unit test confirms 100x op-count improvement at
N=100 tasks/relay.
This commit is contained in:
russell@unturf.com 2026-03-30 07:00:50 -04:00
parent 2573ec8802
commit c9e86c450c
7 changed files with 485 additions and 364 deletions

View file

@ -0,0 +1,63 @@
# UNDF: UNDF-2026-000000201
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -840,6 +840,7 @@ struct mta_relay {
int state;
size_t ntask;
TAILQ_HEAD(, mta_task) tasks;
+ struct tree task_by_msgid;
struct tree connectors;
size_t sourceloop;
--- a/usr.sbin/smtpd/mta.c
+++ b/usr.sbin/smtpd/mta.c
@@ -783,10 +783,14 @@ mta_handle_envelope(struct envelope *evp, const char *smarthost)
return;
}
- task = NULL;
- TAILQ_FOREACH(task, &relay->tasks, entry)
- if (task->msgid == evpid_to_msgid(evp->id))
- break;
+ /*
+ * CWE-407: replaced O(N) TAILQ_FOREACH scan with O(log N) tree
+ * lookup. With N tasks queued per relay, the old code scanned
+ * the entire list for every incoming envelope, giving O(M*N)
+ * total work for M envelopes. tree_get() reduces this to
+ * O(M * log N).
+ */
+ task = tree_get(&relay->task_by_msgid,
+ (uint64_t)evpid_to_msgid(evp->id));
if (task == NULL) {
task = xmalloc(sizeof *task);
@@ -797,6 +801,7 @@ mta_handle_envelope(struct envelope *evp, const char *smarthost)
TAILQ_INSERT_TAIL(&relay->tasks, task, entry);
task->msgid = evpid_to_msgid(evp->id);
+ tree_set(&relay->task_by_msgid, (uint64_t)task->msgid, task);
if (evp->sender.user[0] || evp->sender.domain[0])
(void)snprintf(buf, sizeof buf, "%s@%s",
evp->sender.user, evp->sender.domain);
@@ -672,6 +672,7 @@ mta_route_next_task(struct mta_relay *relay, struct mta_route *route)
if ((task = TAILQ_FIRST(&relay->tasks))) {
TAILQ_REMOVE(&relay->tasks, task, entry);
+ tree_pop(&relay->task_by_msgid, (uint64_t)task->msgid);
relay->ntask -= 1;
task->relay = NULL;
@@ -1558,7 +1558,10 @@ mta_flush(struct mta_relay *relay, int fail, const char *error)
n = 0;
while ((task = TAILQ_FIRST(&relay->tasks))) {
TAILQ_REMOVE(&relay->tasks, task, entry);
+ tree_pop(&relay->task_by_msgid, (uint64_t)task->msgid);
while ((e = TAILQ_FIRST(&task->envelopes))) {
TAILQ_REMOVE(&task->envelopes, e, entry);
@@ -1851,6 +1851,7 @@ mta_relay(struct envelope *e, struct relayhost *relayh)
if ((r = SPLAY_FIND(mta_relay_tree, &relays, &key)) == NULL) {
r = xcalloc(1, sizeof *r);
TAILQ_INIT(&r->tasks);
+ tree_init(&r->task_by_msgid);
r->id = generate_uid();
r->dispatcher = dispatcher;
r->tls = key.tls;