diff --git a/sys/kern/uipc_accf.c new/sys/kern/uipc_accf.c --- a/sys/kern/uipc_accf.c 2026-09-04 12:29:11.301125561 +0000 +++ b/sys/kern/uipc_accf.c 2026-09-04 12:29:00.685262638 +0000 @@ -43,10 +43,18 @@ #include #include #include +#include static SLIST_HEAD(, accept_filter) accept_filtlsthd = SLIST_HEAD_INITIALIZER(&accept_filtlsthd); +/* + * Registry lock: serializes add/del against lookups issued from + * setsockopt(SO_ACCEPTFILTER) on any CPU (previously unsynchronized). + */ +static struct spinlock accept_filt_spin = + SPINLOCK_INITIALIZER(accept_filt_spin, "accept_filt_spin"); + MALLOC_DEFINE(M_ACCF, "accf", "accept filter data"); static int unloadable = 0; @@ -66,21 +74,38 @@ accept_filt_add(struct accept_filter *filt) { struct accept_filter *p; + int reused = 0; + int error = 0; - SLIST_FOREACH(p, &accept_filtlsthd, accf_next) - if (strcmp(p->accf_name, filt->accf_name) == 0) { - if (p->accf_callback != NULL) { - return (EEXIST); - } else { - p->accf_callback = filt->accf_callback; - kfree(filt, M_ACCF); - return (0); - } + spin_lock(&accept_filt_spin); + SLIST_FOREACH(p, &accept_filtlsthd, accf_next) { + if (strcmp(p->accf_name, filt->accf_name) == 0) + break; + } + if (p != NULL) { + if (p->accf_callback != NULL || p->accf_refs > 0) { + error = EEXIST; + } else { + /* + * Re-use the leaked entry, but refresh ALL + * callbacks: previously accf_create/accf_destroy + * kept pointing into the previously unloaded + * module's text. + */ + p->accf_callback = filt->accf_callback; + p->accf_create = filt->accf_create; + p->accf_destroy = filt->accf_destroy; + reused = 1; } - - if (p == NULL) + } else { SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next); - return (0); + } + spin_unlock(&accept_filt_spin); + + if (reused) + kfree(filt, M_ACCF); + + return (error); } int @@ -88,24 +113,60 @@ { struct accept_filter *p; - p = accept_filt_get(name); - if (p == NULL) + spin_lock(&accept_filt_spin); + SLIST_FOREACH(p, &accept_filtlsthd, accf_next) { + if (strcmp(p->accf_name, name) == 0) + break; + } + if (p == NULL) { + spin_unlock(&accept_filt_spin); return (ENOENT); + } + if (p->accf_refs > 0) { + /* sockets are still attached to this filter */ + spin_unlock(&accept_filt_spin); + return (EBUSY); + } p->accf_callback = NULL; + p->accf_create = NULL; + p->accf_destroy = NULL; + spin_unlock(&accept_filt_spin); return (0); } +/* + * Look up a filter by name and take a reference on it. The reference must + * be released with accept_filt_release() once the socket stops using the + * filter. Holding a reference prevents the registering module from being + * unloaded underneath live sockets (previously no refcount was kept). + */ struct accept_filter * accept_filt_get(char *name) { struct accept_filter *p; - SLIST_FOREACH(p, &accept_filtlsthd, accf_next) + spin_lock(&accept_filt_spin); + SLIST_FOREACH(p, &accept_filtlsthd, accf_next) { if (strcmp(p->accf_name, name) == 0) - return (p); + break; + } + if (p != NULL) + p->accf_refs++; + spin_unlock(&accept_filt_spin); - return (NULL); + return (p); +} + +void +accept_filt_release(struct accept_filter *filt) +{ + if (filt == NULL) + return; + spin_lock(&accept_filt_spin); + KKASSERT(filt->accf_refs > 0); + filt->accf_refs--; + spin_unlock(&accept_filt_spin); } int @@ -117,7 +178,7 @@ switch (event) { case MOD_LOAD: - p = kmalloc(sizeof(*p), M_ACCF, M_WAITOK); + p = kmalloc(sizeof(*p), M_ACCF, M_WAITOK | M_ZERO); bcopy(accfp, p, sizeof(*p)); crit_enter(); error = accept_filt_add(p); diff --git a/sys/kern/uipc_socket.c new/sys/kern/uipc_socket.c --- a/sys/kern/uipc_socket.c 2026-09-04 12:29:11.301125561 +0000 +++ b/sys/kern/uipc_socket.c 2026-09-04 12:29:00.693262535 +0000 @@ -2008,9 +2008,11 @@ /* removing the filter */ if (sopt == NULL) { if (af != NULL) { - if (af->so_accept_filter != NULL && - af->so_accept_filter->accf_destroy != NULL) { - af->so_accept_filter->accf_destroy(so); + if (af->so_accept_filter != NULL) { + if (af->so_accept_filter->accf_destroy != NULL) { + af->so_accept_filter->accf_destroy(so); + } + accept_filt_release(af->so_accept_filter); } if (af->so_accept_filter_str != NULL) { kfree(af->so_accept_filter_str, M_ACCF); @@ -2035,7 +2037,9 @@ if (error) goto out; afp = accept_filt_get(afap->af_name); - if (afp == NULL) { + if (afp == NULL || afp->accf_callback == NULL) { + if (afp != NULL) + accept_filt_release(afp); error = ENOENT; goto out; } @@ -2050,6 +2054,7 @@ } af->so_accept_filter_arg = afp->accf_create(so, afap->af_arg); if (af->so_accept_filter_arg == NULL) { + accept_filt_release(afp); kfree(af->so_accept_filter_str, M_ACCF); kfree(af, M_ACCF); so->so_accf = NULL; diff --git a/sys/sys/socketvar.h new/sys/sys/socketvar.h --- a/sys/sys/socketvar.h 2026-09-04 12:29:11.301125561 +0000 +++ b/sys/sys/socketvar.h 2026-09-04 12:29:00.693262535 +0000 @@ -369,6 +369,7 @@ (struct socket *so, char *arg); void (*accf_destroy) (struct socket *so); + int accf_refs; /* sockets attached to filter */ SLIST_ENTRY(accept_filter) accf_next; /* next on the list */ }; @@ -494,6 +495,7 @@ int accept_filt_add (struct accept_filter *filt); int accept_filt_del (char *name); struct accept_filter * accept_filt_get (char *name); +void accept_filt_release (struct accept_filter *filt); #ifdef ACCEPT_FILTER_MOD int accept_filt_generic_mod_event (module_t mod, int event, void *data); SYSCTL_DECL(_net_inet_accf);