> diff --git a/tools/testing/selftests/bpf/network_helpers.c 
> b/tools/testing/selftests/bpf/network_helpers.c
> index b82f572641b7..25e4d2fa6f6f 100644
> --- a/tools/testing/selftests/bpf/network_helpers.c
> +++ b/tools/testing/selftests/bpf/network_helpers.c

[ ... ]

> @@ -305,13 +309,69 @@ int connect_to_addr(int type, const struct 
> sockaddr_storage *addr, socklen_t add
>               return -1;
>       }
>
> -     if (connect(fd, (const struct sockaddr *)addr, addrlen)) {
> +     start_ms = get_time_ms();
> +     err = connect(fd, (const struct sockaddr *)addr, addrlen);
> +
> +     if (!err)
> +             return fd;
> +
> +     if (errno != EINPROGRESS) {
>               log_err("Failed to connect to server");
> -             save_errno_close(fd);
> -             return -1;
> +             goto close;
>       }
>
> -     return fd;
> +     duration_ms = get_time_ms() - start_ms;
> +     remaining_ms = duration_ms < opts->timeout_ms ?
> +                            opts->timeout_ms - duration_ms :
> +                            0;
> +     if (!remaining_ms) {
> +             errno = ETIMEDOUT;
> +             log_err("Can not poll connection, already in timeout");
> +             goto close;
> +     }
> +
> +     while (remaining_ms) {
> +             struct pollfd pfd = { .fd = fd, .events = POLLOUT };
> +
> +             start_ms = get_time_ms();
> +             ret = poll(&pfd, 1, remaining_ms);
> +
> +             if (ret == 0) {
> +                     errno = ETIMEDOUT;
> +                     log_err("Connection timeout while polling");
> +                     goto close;
> +             } else if (ret < 0 && errno == EINTR) {
> +                     duration_ms = get_time_ms() - start_ms;
> +                     remaining_ms = duration_ms < remaining_ms ?
> +                                            remaining_ms - duration_ms :
> +                                            0;
> +                     if (!remaining_ms) {
> +                             errno = ETIMEDOUT;
> +                             log_err("Connection timeout after signal");
> +                             goto close;
> +                     }
> +             } else if (ret < 0) {
> +                     log_err("Failed to poll connect status");
> +                     goto close;
> +             }
> +
> +             errlen = sizeof(err);
> +             if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) < 0) {
> +                     log_err("Failed to getsockopt");
> +                     goto close;
> +             }
> +
> +             if (err) {
> +                     errno = err;
> +                     log_err("Eventually failed to connect to server");
> +                     goto close;
> +             }
> +             return fd;
> +     }

Does the EINTR branch need to continue the loop? When poll() returns
EINTR, the code recomputes remaining_ms but then falls through to
getsockopt(SO_ERROR). If the connection is still in progress, SO_ERROR
will be 0 and connect_to_addr() returns the fd even though the socket
is not yet connected.

Also, no path inside the while loop reaches the loop condition again -
every branch either does 'goto close' or 'return fd'. So after the EINTR
budget recalculation runs, the code never re-enters poll() to use that
budget.

Would adding 'continue;' at the end of the EINTR branch fix this? That
way poll() gets called again with the updated remaining_ms, and the
connection completes properly after a signal interruption.

Test_progs installs a SIGINT handler without SA_RESTART in
test_progs.c, so EINTR is reachable here.

> +
> +close:
> +     save_errno_close(fd);
> +     return -1;
>  }

[ ... ]


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/31503700726

Reply via email to