diff options
-rw-r--r-- | sys/kern/uipc_accf.c | 6 | ||||
-rw-r--r-- | tests/sys/kern/socket_accf.c | 32 |
2 files changed, 37 insertions, 1 deletions
diff --git a/sys/kern/uipc_accf.c b/sys/kern/uipc_accf.c index c63b5a1179bc..9e30e7839103 100644 --- a/sys/kern/uipc_accf.c +++ b/sys/kern/uipc_accf.c @@ -276,10 +276,14 @@ accept_filt_setopt(struct socket *so, struct sockopt *sopt) * without first removing it. */ SOCK_LOCK(so); - if (!SOLISTENING(so) || so->sol_accept_filter != NULL) { + if (__predict_false(!SOLISTENING(so))) { error = EINVAL; goto out; } + if (__predict_false(so->sol_accept_filter != NULL)) { + error = EBUSY; + goto out; + } /* * Invoke the accf_create() method of the filter if required. The diff --git a/tests/sys/kern/socket_accf.c b/tests/sys/kern/socket_accf.c index 747bcda87010..ae6522397cf7 100644 --- a/tests/sys/kern/socket_accf.c +++ b/tests/sys/kern/socket_accf.c @@ -209,11 +209,43 @@ ATF_TC_BODY(tls, tc) ATF_REQUIRE((a = accept(l, NULL, 0)) > 0); } +/* Check changing to a different filter. */ +ATF_TC_WITHOUT_HEAD(change); +ATF_TC_BODY(change, tc) +{ + struct accept_filter_arg dfa = { + .af_name = "dataready" + }; + struct accept_filter_arg hfa = { + .af_name = "httpready" + }; + struct sockaddr_in sin; + int n, l; + + l = listensock(&sin); + accfon(l, &dfa); + + /* Refuse to change filter without explicit removal of the old one. */ + ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa, + sizeof(hfa)) != 0 && errno == EBUSY); + + /* But allow after clearing. */ + ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0) == 0); + ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa, + sizeof(hfa)) == 0); + + /* Must be listening socket. */ + ATF_REQUIRE((n = socket(PF_INET, SOCK_STREAM, 0)) > 0); + ATF_REQUIRE(setsockopt(n, SOL_SOCKET, SO_ACCEPTFILTER, &dfa, + sizeof(dfa)) != 0 && errno == EINVAL); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, data); ATF_TP_ADD_TC(tp, http); ATF_TP_ADD_TC(tp, tls); + ATF_TP_ADD_TC(tp, change); return (atf_no_error()); } |