multipath-tools: New way to limit the IPC command length.
authorGris Ge <fge@redhat.com>
Fri, 20 Jan 2017 13:39:00 +0000 (21:39 +0800)
committerChristophe Varoqui <christophe.varoqui@opensvc.com>
Fri, 10 Feb 2017 11:35:54 +0000 (12:35 +0100)
Problem:

    mpath_recv_reply() from libmpathcmd return -EINVAL on command 'show
    maps json' with 2k paths.  No error will be triggered for command
    `multipathd -k'show maps json` as multipathd is using their own
    implementation on recv_packet() which does not have size limitation.

Root cause:

    Commit 174e717d351789a3cb29e1417f8e910baabcdb16 introduced the
    limitation on max bytes(65535) of reply string from multipathd.
    With 2k paths(1k mpaths) simulated by scsi_debug, the 'show maps json'
    requires 2066784 bytes which trigged the EINVAL error.

Fix:
    * Remove the limitation of MAX_REPLY_LEN in libmpathcmd.

    * New functions `recv_packet_from_client()` in uxsock.h of libmultipath
      which enforce the limitation of buffer size to 512(_MAX_CMD_LEN).

    * Change multipathd socket listener to use
      `recv_packet_from_client()`.

Signed-off-by: Gris Ge <fge@redhat.com>
libmpathcmd/mpath_cmd.c
libmultipath/uxsock.c
libmultipath/uxsock.h
multipathd/uxlsnr.c

index d9c5790..7fc9e98 100644 (file)
@@ -142,8 +142,6 @@ int mpath_recv_reply(int fd, char **reply, unsigned int timeout)
        len = mpath_recv_reply_len(fd, timeout);
        if (len <= 0)
                return len;
-       if (len > MAX_REPLY_LEN)
-               return -EINVAL;
        *reply = malloc(len);
        if (!*reply)
                return -1;
index 0ca9e50..492f4b9 100644 (file)
 #include "memory.h"
 #include "uxsock.h"
 #include "debug.h"
+
+/*
+ * Code is similar with mpath_recv_reply() with data size limitation
+ * and debug-able malloc.
+ * When limit == 0, it means no limit on data size, used for socket client
+ * to receiving data from multipathd.
+ */
+static int _recv_packet(int fd, char **buf, unsigned int timeout,
+                       ssize_t limit);
+
 /*
  * create a unix domain socket and start listening on it
  * return a file descriptor open on the socket
@@ -81,26 +91,37 @@ int send_packet(int fd, const char *buf)
        return mpath_send_cmd(fd, buf);
 }
 
-/*
- * receive a packet in length prefix format
- */
-int recv_packet(int fd, char **buf, unsigned int timeout)
+static int _recv_packet(int fd, char **buf, unsigned int timeout, ssize_t limit)
 {
-       int err;
-       ssize_t len;
+       int err = 0;
+       ssize_t len = 0;
 
        *buf = NULL;
        len = mpath_recv_reply_len(fd, timeout);
        if (len <= 0)
                return len;
+       if ((limit > 0) && (len > limit))
+               return -EINVAL;
        (*buf) = MALLOC(len);
        if (!*buf)
                return -ENOMEM;
        err = mpath_recv_reply_data(fd, *buf, len, timeout);
-       if (err) {
+       if (err != 0) {
                FREE(*buf);
                (*buf) = NULL;
-               return err;
        }
-       return 0;
+       return err;
+}
+
+/*
+ * receive a packet in length prefix format
+ */
+int recv_packet(int fd, char **buf, unsigned int timeout)
+{
+       return _recv_packet(fd, buf, timeout, 0 /* no limit */);
+}
+
+int recv_packet_from_client(int fd, char **buf, unsigned int timeout)
+{
+       return _recv_packet(fd, buf, timeout, _MAX_CMD_LEN);
 }
index 442b564..8e7401d 100644 (file)
@@ -2,3 +2,12 @@
 int ux_socket_listen(const char *name);
 int send_packet(int fd, const char *buf);
 int recv_packet(int fd, char **buf, unsigned int timeout);
+
+#define _MAX_CMD_LEN           512
+
+/*
+ * Used for receiving socket command from untrusted socket client where data
+ * size is restricted to 512(_MAX_CMD_LEN) at most.
+ * Return -EINVAL if data length requested by client exceeded the _MAX_CMD_LEN.
+ */
+int recv_packet_from_client(int fd, char **buf, unsigned int timeout);
index dfef03e..6ca62af 100644 (file)
@@ -241,13 +241,15 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data)
                                if (clock_gettime(CLOCK_MONOTONIC, &start_time)
                                    != 0)
                                        start_time.tv_sec = 0;
-                               if (recv_packet(c->fd, &inbuf,
-                                               uxsock_timeout) != 0) {
+                               if (recv_packet_from_client(c->fd, &inbuf,
+                                                           uxsock_timeout)
+                                   != 0) {
                                        dead_client(c);
                                        continue;
                                }
                                if (!inbuf) {
-                                       condlog(4, "recv_packet get null request");
+                                       condlog(4, "recv_packet_from_client "
+                                               "get null request");
                                        continue;
                                }
                                condlog(4, "cli[%d]: Got request [%s]",