device-mapper-multipath/0066-multipathd-sanitize-uxsock_listen.patch
Benjamin Marzinski 1dad67a5af device-mapper-multipath-0.8.5-1
Update Source to upstream version 0.8.5 plus post tag commits
  * Patches 0001-0102 are from
    https://github.com/openSUSE/multipath-tools/tree/queue and are
    already queued for upstream.
Rename files
  * Previous patches 0059-0068 are now patches 0103-0111
2021-01-19 18:06:09 -06:00

158 lines
5.1 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Sat, 26 Sep 2020 15:22:34 +0200
Subject: [PATCH] multipathd: sanitize uxsock_listen()
We were allocating 1025 poll fds, which is weird. Change it to a power of two,
and make this more easily customizable in general. Use POLLFDS_BASE rather
than the hard-coded "2" for the number of fds we poll besides client
connections. Introduce a maximum number of clients that can connect. When
this number is reached, we simply stop polling the accept socket, so that new
connections aren't accepted any more. Don't attempt to realloc() the pollfd
array if the number of clients decreases. It's unlikely to ever be more than
one or two pages. Finally, there's no need to wake up every 5s. Our signal
handling is robust. Just sleep forever in ppoll() if nothing happens.
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/uxlsnr.c | 70 ++++++++++++++++++++++++++++-----------------
1 file changed, 43 insertions(+), 27 deletions(-)
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index ce2b6800..cd462b6d 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -41,14 +41,25 @@
#include "cli.h"
#include "uxlsnr.h"
-static struct timespec sleep_time = {5, 0};
-
struct client {
struct list_head node;
int fd;
};
-#define MIN_POLLS 1023
+/* The number of fds we poll on, other than individual client connections */
+#define POLLFDS_BASE 2
+#define POLLFD_CHUNK (4096 / sizeof(struct pollfd))
+/* Minimum mumber of pollfds to reserve for clients */
+#define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE)
+/*
+ * Max number of client connections allowed
+ * During coldplug, there may be a large number of "multipath -u"
+ * processes connecting.
+ */
+#define MAX_CLIENTS (16384 - POLLFDS_BASE)
+
+/* Compile-time error if POLLFD_CHUNK is too small */
+static __attribute__((unused)) char ___a[-(MIN_POLLS <= 0)];
static LIST_HEAD(clients);
static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -282,13 +293,13 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
char *inbuf;
char *reply;
sigset_t mask;
- int old_clients = MIN_POLLS;
+ int max_pfds = MIN_POLLS + POLLFDS_BASE;
/* conf->sequence_nr will be 1 when uxsock_listen is first called */
unsigned int sequence_nr = 0;
struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 };
condlog(3, "uxsock: startup listener");
- polls = (struct pollfd *)MALLOC((MIN_POLLS + 2) * sizeof(struct pollfd));
+ polls = MALLOC(max_pfds * sizeof(*polls));
if (!polls) {
condlog(0, "uxsock: failed to allocate poll fds");
exit_daemon();
@@ -312,28 +323,33 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
list_for_each_entry(c, &clients, node) {
num_clients++;
}
- if (num_clients != old_clients) {
+ if (num_clients + POLLFDS_BASE > max_pfds) {
struct pollfd *new;
- if (num_clients <= MIN_POLLS && old_clients > MIN_POLLS) {
- new = REALLOC(polls, (2 + MIN_POLLS) *
- sizeof(struct pollfd));
- } else if (num_clients <= MIN_POLLS && old_clients <= MIN_POLLS) {
- new = polls;
- } else {
- new = REALLOC(polls, (2 + num_clients) *
- sizeof(struct pollfd));
- }
- if (!new) {
- condlog(0, "%s: failed to realloc %d poll fds",
- "uxsock", 2 + num_clients);
- num_clients = old_clients;
- } else {
- old_clients = num_clients;
+ int n_new = max_pfds + POLLFD_CHUNK;
+
+ new = REALLOC(polls, n_new * sizeof(*polls));
+ if (new) {
+ max_pfds = n_new;
polls = new;
+ } else {
+ condlog(1, "%s: realloc failure, %d clients not served",
+ __func__,
+ num_clients + POLLFDS_BASE - max_pfds);
+ num_clients = max_pfds - POLLFDS_BASE;
}
}
- polls[0].fd = ux_sock;
- polls[0].events = POLLIN;
+ if (num_clients < MAX_CLIENTS) {
+ polls[0].fd = ux_sock;
+ polls[0].events = POLLIN;
+ } else {
+ /*
+ * New clients can't connect, num_clients won't grow
+ * to MAX_CLIENTS or higher
+ */
+ condlog(1, "%s: max client connections reached, pausing polling",
+ __func__);
+ polls[0].fd = -1;
+ }
reset_watch(notify_fd, &wds, &sequence_nr);
if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
@@ -343,19 +359,19 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
polls[1].events = POLLIN;
/* setup the clients */
- i = 2;
+ i = POLLFDS_BASE;
list_for_each_entry(c, &clients, node) {
polls[i].fd = c->fd;
polls[i].events = POLLIN;
i++;
- if (i >= 2 + num_clients)
+ if (i >= max_pfds)
break;
}
n_pfds = i;
pthread_cleanup_pop(1);
/* most of our life is spent in this call */
- poll_count = ppoll(polls, n_pfds, &sleep_time, &mask);
+ poll_count = ppoll(polls, n_pfds, NULL, &mask);
handle_signals(false);
if (poll_count == -1) {
@@ -388,7 +404,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
}
/* see if a client wants to speak to us */
- for (i = 2; i < n_pfds; i++) {
+ for (i = POLLFDS_BASE; i < n_pfds; i++) {
if (polls[i].revents & POLLIN) {
struct timespec start_time;
--
2.17.2