From: Mikhail Dmitrichenko m.dmitrichenko222@gmail.com
client_alloc() only logged allocation failures and then continued into code that dereferenced client and pollfd. Failed realloc() calls could also overwrite the existing arrays, leaving wdmd with corrupted client state.
Return -ENOMEM from client_alloc(), propagate failures through client_add(), and close fds that were not added to the poll table. This keeps startup failures explicit and avoids leaking accepted or setup fds when client slot allocation fails.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
Signed-off-by: Mikhail Dmitrichenko m.dmitrichenko222@gmail.com --- wdmd/main.c | 68 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 16 deletions(-)
diff --git a/wdmd/main.c b/wdmd/main.c index 7014462..64bafee 100644 --- a/wdmd/main.c +++ b/wdmd/main.c @@ -174,39 +174,62 @@ static char *time_str(void) * test clients */
-static void client_alloc(void) +static int client_alloc(void) { + struct client *new_client; + struct pollfd *new_pollfd; + int new_size = client_size + CLIENT_NALLOC; int i;
if (!client) { - client = malloc(CLIENT_NALLOC * sizeof(struct client)); - pollfd = malloc(CLIENT_NALLOC * sizeof(struct pollfd)); + new_client = malloc(CLIENT_NALLOC * sizeof(struct client)); + new_pollfd = malloc(CLIENT_NALLOC * sizeof(struct pollfd)); + + if (!new_client || !new_pollfd) { + free(new_client); + free(new_pollfd); + log_error("can't alloc for client or pollfd array"); + return -ENOMEM; + } + + client = new_client; + pollfd = new_pollfd; } else { - client = realloc(client, (client_size + CLIENT_NALLOC) * - sizeof(struct client)); - pollfd = realloc(pollfd, (client_size + CLIENT_NALLOC) * - sizeof(struct pollfd)); - if (!pollfd) + new_client = realloc(client, new_size * sizeof(struct client)); + if (!new_client) { + log_error("can't alloc for client array"); + return -ENOMEM; + } + client = new_client; + + new_pollfd = realloc(pollfd, new_size * sizeof(struct pollfd)); + if (!new_pollfd) { log_error("can't alloc for pollfd"); + return -ENOMEM; + } + pollfd = new_pollfd; } - if (!client || !pollfd) - log_error("can't alloc for client array");
- for (i = client_size; i < client_size + CLIENT_NALLOC; i++) { + for (i = client_size; i < new_size; i++) { memset(&client[i], 0, sizeof(struct client)); client[i].fd = -1; pollfd[i].fd = -1; pollfd[i].revents = 0; } - client_size += CLIENT_NALLOC; + client_size = new_size; + return 0; }
static int client_add(int fd, void (*workfn)(int ci), void (*deadfn)(int ci)) { + int rv; int i;
- if (!client) - client_alloc(); + if (!client) { + rv = client_alloc(); + if (rv < 0) + return rv; + } again: for (i = 0; i < client_size; i++) { if (!client[i].used) { @@ -222,7 +245,9 @@ static int client_add(int fd, void (*workfn)(int ci), void (*deadfn)(int ci)) } }
- client_alloc(); + rv = client_alloc(); + if (rv < 0) + return rv; goto again; }
@@ -691,7 +716,8 @@ static void process_listener(int ci)
setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
- client_add(fd, process_connection, client_pid_dead); + if (client_add(fd, process_connection, client_pid_dead) < 0) + close(fd); }
static void close_clients(void) @@ -755,6 +781,11 @@ static int setup_clients(void) return rv;
ci = client_add(fd, process_listener, client_pid_dead); + if (ci < 0) { + close(fd); + return ci; + } + strncpy(client[ci].name, "listen", WDMD_NAME_SIZE); client[ci].internal = 1; return 0; @@ -1841,6 +1872,11 @@ static int setup_signals(void) return -errno;
ci = client_add(fd, process_signals, client_pid_dead); + if (ci < 0) { + close(fd); + return ci; + } + strncpy(client[ci].name, "signal", WDMD_NAME_SIZE); client[ci].internal = 1; return 0;
sanlock-devel@lists.fedorahosted.org