]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/io: handle failed session data initialization
authorFrantisek Tobias <frantisek.tobias@nic.cz>
Tue, 20 Jan 2026 13:21:20 +0000 (14:21 +0100)
committerFrantisek Tobias <frantisek.tobias@nic.cz>
Wed, 21 Jan 2026 13:32:03 +0000 (14:32 +0100)
daemon/io.c
daemon/io.h
daemon/session2.c
daemon/session2.h
daemon/worker.c

index 3f609c6e533301b340d0caefa77d5f336bf6862c..487e593803778edded1fc9d0330e8a54ac8c1117 100644 (file)
@@ -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;
 }
 
index 268a8c9df480e20981aea2a267991b3b00412c27..2adcffd12258ddf81f50cfec983bf972da7ff465 100644 (file)
 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);
index 49db0fa1a2b60d2e60744271d7041127611b0eda..970e07a937fdfcf2a7efb185d976f1122e5227af 100644 (file)
@@ -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
index 0b94a86911537ed6f303089d6218e9640e35215b..72c53da4693d45f789e096744c500ab8a14830ab 100644 (file)
@@ -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;
 }
 
index 5501217b55a742fb1f343ea2ddf43918042ae873..9bbc0607994206bc4d4284b625fa2757f7e0aee4 100644 (file)
@@ -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;