From: Frantisek Tobias Date: Tue, 20 Jan 2026 13:21:20 +0000 (+0100) Subject: daemon/io: handle failed session data initialization X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=72622e65ef80cf86ca33ebd93157af6c6431734f;p=thirdparty%2Fknot-resolver.git daemon/io: handle failed session data initialization --- diff --git a/daemon/io.c b/daemon/io.c index 3f609c6e5..487e59380 100644 --- a/daemon/io.c +++ b/daemon/io.c @@ -381,20 +381,29 @@ static void tcp_accept_internal(uv_stream_t *master, int status, enum kr_proto g return; } - struct session2 *s; - int res = io_create(master->loop, &s, SOCK_STREAM, AF_UNSPEC, grp, + union session_or_handle out = { 0 }; + int res = io_create(master->loop, &out, SOCK_STREAM, AF_UNSPEC, grp, NULL, 0, false); if (res) { if (res == UV_EMFILE) { the_worker->too_many_open = true; the_worker->rconcurrent_highwatermark = the_worker->stats.rconcurrent; } - /* Since res isn't OK struct session wasn't allocated \ borrowed. - * We must release client handle only. - */ + if (out.handle != NULL) { + /* Since res isn't OK struct session wasn't + * allocated \ borrowed. We must release client handle + * only. But first accept the connection, as it has + * already been established by the kernel and + * it is required for proper termination. + */ + if (uv_accept(master, (uv_stream_t *)out.handle) == 0) { + uv_close(out.handle, (uv_close_cb)free); + } + } return; } + struct session2 *s = out.session; kr_require(s->outgoing == false); uv_tcp_t *client = (uv_tcp_t *)session2_get_handle(s); @@ -916,12 +925,12 @@ int io_listen_xdp(uv_loop_t *loop, struct endpoint *ep, const char *ifname) } #endif -int io_create(uv_loop_t *loop, struct session2 **out_session, int type, - unsigned family, enum kr_proto grp, +int io_create(uv_loop_t *loop, union session_or_handle *out, + int type, unsigned family, enum kr_proto grp, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing) { - *out_session = NULL; + out->session = NULL; int ret = -1; uv_handle_t *handle; if (type == SOCK_DGRAM) { @@ -945,11 +954,12 @@ int io_create(uv_loop_t *loop, struct session2 **out_session, int type, } struct session2 *s = session2_new_io(handle, grp, layer_param, layer_param_count, outgoing); - if (s == NULL) { - ret = -1; + if (unlikely(s == NULL)) { + out->handle = handle; + return -1; } - *out_session = s; + out->session = s; return ret; } diff --git a/daemon/io.h b/daemon/io.h index 268a8c9df..2adcffd12 100644 --- a/daemon/io.h +++ b/daemon/io.h @@ -16,6 +16,12 @@ struct tls_ctx; struct tls_client_ctx; struct io_stream_data; +/* union used for io_create. handle will be used only if session init fails, + * allowing us to terminated the uv_handle gracefully */ +union session_or_handle { + struct session2 *session; + uv_handle_t *handle; +}; /** Bind address into a file-descriptor (only, no libuv). type is e.g. SOCK_DGRAM */ int io_bind(const struct sockaddr *addr, int type, const endpoint_flags_t *flags); @@ -41,7 +47,7 @@ void tcp_timeout_trigger(uv_timer_t *timer); * \param type = SOCK_* * \param family = AF_* * \param has_tls has meanings only when type is SOCK_STREAM */ -int io_create(uv_loop_t *loop, struct session2 **out_session, int type, +int io_create(uv_loop_t *loop, union session_or_handle *out, int type, unsigned family, enum kr_proto grp, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing); diff --git a/daemon/session2.c b/daemon/session2.c index 49db0fa1a..970e07a93 100644 --- a/daemon/session2.c +++ b/daemon/session2.c @@ -873,7 +873,8 @@ struct session2 *session2_new(enum session2_transport_type transport_type, session2_inc_refs(s); /* Session owns the timer */ /* Initialize the layer's session data */ - for (size_t i = 0; i < grp->num_layers; i++) { + size_t i; + for (i = 0; i < grp->num_layers; i++) { struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; struct protolayer_data *sess_data = protolayer_sess_data_get(s, i); if (sess_data) { @@ -882,13 +883,31 @@ struct session2 *session2_new(enum session2_transport_type transport_type, } void *param = get_init_param(grp->layers[i], layer_param, layer_param_count); - if (globals->sess_init) - globals->sess_init(s, sess_data, param); + if (globals->sess_init && globals->sess_init(s, sess_data, param) != 0) { + /* Init failed, terminate session */ + goto failed_init; + } } session2_touch(s); return s; + +failed_init: + while (i --> 0) { + struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; + struct protolayer_data *sess_data = protolayer_sess_data_get(s, i); + if (globals->sess_deinit) { + globals->sess_deinit(s, sess_data); + } + + if (sess_data) { + memset(sess_data, 0, globals->sess_size); + sess_data->session = NULL; + } + } + + return NULL; } /** De-allocates the session. Must only be called once the underlying IO handle diff --git a/daemon/session2.h b/daemon/session2.h index 0b94a8691..72c53da46 100644 --- a/daemon/session2.h +++ b/daemon/session2.h @@ -965,9 +965,11 @@ static inline struct session2 *session2_new_io(uv_handle_t *handle, { struct session2 *s = session2_new(SESSION2_TRANSPORT_IO, layer_grp, layer_param, layer_param_count, outgoing); - s->transport.io.handle = handle; - handle->data = s; - session2_inc_refs(s); /* Session owns the handle */ + if (likely(s != NULL)) { + s->transport.io.handle = handle; + handle->data = s; + session2_inc_refs(s); /* Session owns the handle */ + } return s; } @@ -981,7 +983,9 @@ static inline struct session2 *session2_new_child(struct session2 *parent, { struct session2 *s = session2_new(SESSION2_TRANSPORT_PARENT, layer_grp, layer_param, layer_param_count, outgoing); - s->transport.parent = parent; + if (likely(s != NULL)) { + s->transport.parent = parent; + } return s; } diff --git a/daemon/worker.c b/daemon/worker.c index 5501217b5..9bbc06079 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -157,8 +157,8 @@ static struct session2 *ioreq_spawn(int socktype, sa_family_t family, } /* Create connection for iterative query */ - struct session2 *s; - int ret = io_create(the_worker->loop, &s, socktype, family, grp, + union session_or_handle out; + int ret = io_create(the_worker->loop, &out, socktype, family, grp, layer_param, layer_param_count, true); if (ret) { if (ret == UV_EMFILE) { @@ -167,6 +167,7 @@ static struct session2 *ioreq_spawn(int socktype, sa_family_t family, } return NULL; } + struct session2 *s = out.session; /* Bind to outgoing address, according to IP v4/v6. */ union kr_sockaddr *addr;