accept_filter: return different errors for non-listener and a busy socket

The fact that an accept filter needs to be cleared first before setting to
a different one isn't properly documented.  The requirement that the
socket needs already be listening, although trivial, isn't documented
either.  At least return a more meaningful error than EINVAL for an
existing filter.  Cover this with a test case.
This commit is contained in:
Gleb Smirnoff 2024-04-24 21:50:58 -07:00
parent c68eed82a3
commit 19307b86d3
2 changed files with 37 additions and 1 deletions

View file

@ -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

View file

@ -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());
}