BUG/MEDIUM: proto: fix fd leak in <proto>_connect_server

This fixes the fd leak, introduced in the commit d3fc982cd7
("MEDIUM: proto: make common fd checks in sock_create_server_socket").

Initially sock_create_server_socket() was designed to return only created
socket FD or -1. Its callers from upper protocol layers were required to test
the returned errno and were required then to apply different configuration
related checks to obtained positive sock_fd. A lot of this code was duplicated
among protocols implementations.

The new refactored version of sock_create_server_socket() gathers in one place
all duplicated checks, but in order to be complient with upper protocol
layers, it needs the 3rd parameter: 'stream_err', in which it sets the
Stream Error Flag for upper levels, if the obtained sock_fd has passed all
additional checks.

No backport needed since this was introduced in 3.0-dev10.
This commit is contained in:
Valentine Krasnobaeva 2024-05-21 19:24:31 +02:00 committed by Willy Tarreau
parent 04a42a92f4
commit 5f713c03be
5 changed files with 30 additions and 19 deletions

View File

@ -30,7 +30,7 @@
#include <haproxy/listener-t.h>
#include <haproxy/sock-t.h>
int sock_create_server_socket(struct connection *conn, struct proxy *be);
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err);
void sock_enable(struct receiver *rx);
void sock_disable(struct receiver *rx);
void sock_unbind(struct receiver *rx);

View File

@ -277,7 +277,7 @@ int quic_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct s
int quic_connect_server(struct connection *conn, int flags)
{
int fd;
int fd, stream_err;
struct server *srv;
struct proxy *be;
struct conn_src *src;
@ -302,9 +302,10 @@ int quic_connect_server(struct connection *conn, int flags)
}
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
fd = conn->handle.fd = sock_create_server_socket(conn, be);
if (fd & SF_ERR_MASK)
return fd;
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
if (fd == -1) {
return stream_err;
}
/* FD is ok, perform protocol specific settings */
/* allow specific binding :

View File

@ -265,7 +265,7 @@ int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct so
int tcp_connect_server(struct connection *conn, int flags)
{
int fd;
int fd, stream_err;
struct server *srv;
struct proxy *be;
struct conn_src *src;
@ -301,9 +301,10 @@ int tcp_connect_server(struct connection *conn, int flags)
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
fd = conn->handle.fd = sock_create_server_socket(conn, be);
if ((fd & SF_ERR_MASK) == fd)
return fd;
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
if (fd == -1) {
return stream_err;
}
/* FD is OK, continue with protocol specific settings */
if (be->options & PR_O_TCP_SRV_KA) {

View File

@ -219,7 +219,7 @@ static int uxst_suspend_receiver(struct receiver *rx)
*/
static int uxst_connect_server(struct connection *conn, int flags)
{
int fd;
int fd, stream_err;
struct server *srv;
struct proxy *be;
@ -240,9 +240,10 @@ static int uxst_connect_server(struct connection *conn, int flags)
}
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
fd = conn->handle.fd = sock_create_server_socket(conn, be);
if (fd & SF_ERR_MASK)
return fd;
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
if (fd == -1) {
return stream_err;
}
/* FD is ok, continue with protocol specific settings */
if (global.tune.server_sndbuf)

View File

@ -256,9 +256,11 @@ static int sock_handle_system_err(struct connection *conn, struct proxy *be)
* using the configured namespace if needed, or the one passed by the proxy
* protocol if required to do so. It then calls socket() or socketat(). On
* success, checks if mark or tos sockopts need to be set on the file handle.
* Returns the FD or error code.
* Returns backend connection socket FD on success, stream_err flag needed by
* upper level is set as SF_ERR_NONE; -1 on failure, stream_err is set to
* appropriate value.
*/
int sock_create_server_socket(struct connection *conn, struct proxy *be)
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err)
{
const struct netns_entry *ns = NULL;
int sock_fd;
@ -275,7 +277,9 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be)
/* at first, handle common to all proto families system limits and permission related errors */
if (sock_fd == -1) {
return sock_handle_system_err(conn, be);
*stream_err = sock_handle_system_err(conn, be);
return -1;
}
/* now perform some runtime condition checks */
@ -288,8 +292,9 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be)
close(sock_fd);
conn->err_code = CO_ER_CONF_FDLIM;
conn->flags |= CO_FL_ERROR;
*stream_err = SF_ERR_PRXCOND; /* it is a configuration limit */
return SF_ERR_PRXCOND; /* it is a configuration limit */
return -1;
}
if (fd_set_nonblock(sock_fd) == -1 ||
@ -299,8 +304,9 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be)
close(sock_fd);
conn->err_code = CO_ER_SOCK_ERR;
conn->flags |= CO_FL_ERROR;
*stream_err = SF_ERR_INTERNAL;
return SF_ERR_INTERNAL;
return -1;
}
if (master == 1 && fd_set_cloexec(sock_fd) == -1) {
@ -309,8 +315,9 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be)
close(sock_fd);
conn->err_code = CO_ER_SOCK_ERR;
conn->flags |= CO_FL_ERROR;
*stream_err = SF_ERR_INTERNAL;
return SF_ERR_INTERNAL;
return -1;
}
if (conn->flags & CO_FL_OPT_MARK)
@ -318,6 +325,7 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be)
if (conn->flags & CO_FL_OPT_TOS)
sock_set_tos(sock_fd, conn->dst, conn->tos);
stream_err = SF_ERR_NONE;
return sock_fd;
}