fix: propagate SSH child setup failures
CI / lint (pull_request) Successful in 11s
CI / sanitizers (address) (pull_request) Successful in 38s
CI / sanitizers (undefined) (pull_request) Successful in 36s
CI / fuzz-build (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 32s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 33s
CI / lint (pull_request) Successful in 11s
CI / sanitizers (address) (pull_request) Successful in 38s
CI / sanitizers (undefined) (pull_request) Successful in 36s
CI / fuzz-build (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 32s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 33s
This commit is contained in:
+16
-11
@@ -16,6 +16,12 @@ typedef struct {
|
|||||||
char* remote_path;
|
char* remote_path;
|
||||||
} RemoteDest;
|
} RemoteDest;
|
||||||
|
|
||||||
|
static void ssh_child_setup_failed(int status_fd) {
|
||||||
|
ssize_t wret = write(status_fd, "x", 1);
|
||||||
|
(void)wret;
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
static void remote_dest_destroy(RemoteDest* r) {
|
static void remote_dest_destroy(RemoteDest* r) {
|
||||||
free(r->user);
|
free(r->user);
|
||||||
free(r->host);
|
free(r->host);
|
||||||
@@ -157,11 +163,12 @@ Client* client_connect_ssh(const char* destination, int port, const char* server
|
|||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(sv[0]);
|
close(sv[0]);
|
||||||
close(exec_pipe[0]);
|
close(exec_pipe[0]);
|
||||||
fcntl(exec_pipe[1], F_SETFD, FD_CLOEXEC);
|
if (fcntl(exec_pipe[1], F_SETFD, FD_CLOEXEC) < 0)
|
||||||
if (sv[1] != STDIN_FILENO)
|
ssh_child_setup_failed(exec_pipe[1]);
|
||||||
dup2(sv[1], STDIN_FILENO);
|
if (sv[1] != STDIN_FILENO && dup2(sv[1], STDIN_FILENO) < 0)
|
||||||
if (sv[1] != STDOUT_FILENO)
|
ssh_child_setup_failed(exec_pipe[1]);
|
||||||
dup2(sv[1], STDOUT_FILENO);
|
if (sv[1] != STDOUT_FILENO && dup2(sv[1], STDOUT_FILENO) < 0)
|
||||||
|
ssh_child_setup_failed(exec_pipe[1]);
|
||||||
if (sv[1] > 1)
|
if (sv[1] > 1)
|
||||||
close(sv[1]);
|
close(sv[1]);
|
||||||
|
|
||||||
@@ -172,7 +179,7 @@ Client* client_connect_ssh(const char* destination, int port, const char* server
|
|||||||
ssh_user_len = strlen(r.host) + 1;
|
ssh_user_len = strlen(r.host) + 1;
|
||||||
char* ssh_user = malloc(ssh_user_len);
|
char* ssh_user = malloc(ssh_user_len);
|
||||||
if (!ssh_user)
|
if (!ssh_user)
|
||||||
_exit(1);
|
ssh_child_setup_failed(exec_pipe[1]);
|
||||||
if (r.user && r.user[0] != '\0')
|
if (r.user && r.user[0] != '\0')
|
||||||
snprintf(ssh_user, ssh_user_len, "%s@%s", r.user, r.host);
|
snprintf(ssh_user, ssh_user_len, "%s@%s", r.user, r.host);
|
||||||
else
|
else
|
||||||
@@ -183,7 +190,7 @@ Client* client_connect_ssh(const char* destination, int port, const char* server
|
|||||||
char port_str[16];
|
char port_str[16];
|
||||||
char* remote_command = ssh_build_remote_command(server_path, old_args);
|
char* remote_command = ssh_build_remote_command(server_path, old_args);
|
||||||
if (!remote_command)
|
if (!remote_command)
|
||||||
_exit(1);
|
ssh_child_setup_failed(exec_pipe[1]);
|
||||||
ssh_argv[ac++] = "ssh";
|
ssh_argv[ac++] = "ssh";
|
||||||
ssh_argv[ac++] = "-o";
|
ssh_argv[ac++] = "-o";
|
||||||
ssh_argv[ac++] = "Compression=no";
|
ssh_argv[ac++] = "Compression=no";
|
||||||
@@ -201,9 +208,7 @@ Client* client_connect_ssh(const char* destination, int port, const char* server
|
|||||||
ssh_argv[ac] = NULL;
|
ssh_argv[ac] = NULL;
|
||||||
execvp("ssh", ssh_argv);
|
execvp("ssh", ssh_argv);
|
||||||
log_perror("exec of ssh failed");
|
log_perror("exec of ssh failed");
|
||||||
ssize_t wret = write(exec_pipe[1], "x", 1);
|
ssh_child_setup_failed(exec_pipe[1]);
|
||||||
(void)wret;
|
|
||||||
_exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(sv[1]);
|
close(sv[1]);
|
||||||
@@ -213,7 +218,7 @@ Client* client_connect_ssh(const char* destination, int port, const char* server
|
|||||||
ssize_t n = read(exec_pipe[0], &exec_status, 1);
|
ssize_t n = read(exec_pipe[0], &exec_status, 1);
|
||||||
close(exec_pipe[0]);
|
close(exec_pipe[0]);
|
||||||
|
|
||||||
if (n > 0) {
|
if (n != 0) {
|
||||||
close(sv[0]);
|
close(sv[0]);
|
||||||
waitpid(pid, NULL, 0);
|
waitpid(pid, NULL, 0);
|
||||||
remote_dest_destroy(&r);
|
remote_dest_destroy(&r);
|
||||||
|
|||||||
@@ -14,18 +14,23 @@ static void test_ssh_connect_invalid_dest_empty() {
|
|||||||
EXPECT_NULL(client);
|
EXPECT_NULL(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test client_connect_ssh with malformed destination (just a colon).
|
/* A child that cannot exec ssh must not be returned as a successful client. */
|
||||||
* parse_remote_dest succeeds, ssh is exec'd and fails, but the function
|
|
||||||
* creates a Client that must be cleaned up. */
|
|
||||||
static void test_ssh_connect_malformed() {
|
static void test_ssh_connect_malformed() {
|
||||||
|
const char* old_path = getenv("PATH");
|
||||||
|
char* saved_path = old_path ? strdup(old_path) : NULL;
|
||||||
|
setenv("PATH", "", 1);
|
||||||
|
|
||||||
|
/* cppcheck-suppress constVariablePointer */
|
||||||
Client* client = client_connect_ssh(":", 22, NULL, false);
|
Client* client = client_connect_ssh(":", 22, NULL, false);
|
||||||
/* ssh binary exists, so exec succeeds; the function returns a Client.
|
|
||||||
* We just verify it doesn't crash and clean up properly. */
|
if (saved_path) {
|
||||||
if (client != NULL) {
|
setenv("PATH", saved_path, 1);
|
||||||
client_disconnect(client);
|
free(saved_path);
|
||||||
client_delete(client);
|
} else {
|
||||||
|
unsetenv("PATH");
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(true);
|
|
||||||
|
EXPECT_NULL(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test client_connect_ssh with valid format but unreachable host.
|
/* Test client_connect_ssh with valid format but unreachable host.
|
||||||
|
|||||||
Reference in New Issue
Block a user