XXX: DO NOT REMOVE UNLESS VERIFIED THAT libvirt virCommandMassClose does not use malloc

https://www.redhat.com/archives/libvir-list/2020-August/msg00598.html

Add a portable generic implementation of virMassClose as fallback on
non-FreeBSD and non-glibc.

This implementation uses poll(2) to look for open files to keep
performance reasonable while not using any mallocs.

This solves a deadlock with musl libc.

Signed-off-by: Natanael Copa <ncopa alpinelinux org>
---
 src/util/vircommand.c | 66 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 58 insertions(+), 8 deletions(-)

diff --git a/src/util/vircommand.c b/src/util/vircommand.c
index 8e372c3152..5319b9c9b3 100644
--- a/src/util/vircommand.c
+++ b/src/util/vircommand.c
@@ -455,7 +455,7 @@ virExecCommon(virCommand *cmd, gid_t *groups, int ngroups)
     return 0;
 }
 
-# ifdef __linux__
+# if defined(__linux__) && defined(__GLIBC__)
 /* On Linux, we can utilize procfs and read the table of opened
  * FDs and selectively close only those FDs we don't want to pass
  * onto child process (well, the one we will exec soon since this
@@ -500,7 +500,7 @@ virCommandMassCloseGetFDsGeneric(virCommand *cmd G_GNUC_UNUSED,
     virBitmapSetAll(fds);
     return 0;
 }
-# endif /* !__linux__ */
+# endif /* __linux__ && __GLIBC__ */
 
 # ifdef __FreeBSD__
 
@@ -554,7 +554,7 @@ virCommandMassClose(virCommand *cmd,
     return 0;
 }
 
-# else /* ! __FreeBSD__ */
+# elif defined(__GLIBC__)  /* ! __FreeBSD__ */
 
 static int
 virCommandMassClose(virCommand *cmd,
@@ -581,13 +581,8 @@ virCommandMassClose(virCommand *cmd,
 
     fds = virBitmapNew(openmax);
 
-#  ifdef __linux__
     if (virCommandMassCloseGetFDsLinux(cmd, fds) < 0)
         return -1;
-#  else
-    if (virCommandMassCloseGetFDsGeneric(cmd, fds) < 0)
-        return -1;
-#  endif
 
     fd = virBitmapNextSetBit(fds, 2);
     for (; fd >= 0; fd = virBitmapNextSetBit(fds, fd)) {
@@ -605,6 +600,61 @@ virCommandMassClose(virCommand *cmd,
     return 0;
 }
 
+#else /* ! __FreeBSD__ && ! __GLIBC__ */
+static int
+virCommandMassClose(virCommand* cmd,
+                    int childin,
+                    int childout,
+                    int childerr)
+{
+    static struct pollfd pfds[1024];
+    int fd = 0;
+    int i, total;
+    int max_fd = sysconf(_SC_OPEN_MAX);
+
+    if (max_fd < 0) {
+        virReportSystemError(errno, "%s", _("sysconf(_SC_OPEN_MAX) failed"));
+        return -1;
+    }
+
+    total = max_fd - fd;
+    for (i = 0; i < (total < 1024 ? total : 1024); i++)
+        pfds[i].events = 0;
+
+    while (fd < max_fd) {
+        int nfds, r = 0;
+
+        total = max_fd - fd;
+        nfds =  total < 1024 ? total : 1024;
+
+        for (i = 0; i < nfds; i++)
+            pfds[i].fd = fd + i;
+
+        do {
+            r = poll(pfds, nfds, 0);
+        } while (r == -1 && errno == EINTR);
+
+        if (r < 0) {
+            virReportSystemError(errno, "%s", _("poll() failed"));
+            return -1;
+        }
+
+        for (i = 0; i < nfds; i++)
+            if (pfds[i].revents != POLLNVAL) {
+                if (pfds[i].fd == childin || pfds[i].fd == childout || pfds[i].fd == childerr)
+                    continue;
+                if (!virCommandFDIsSet(cmd, pfds[i].fd)) {
+                    VIR_MASS_CLOSE(pfds[i].fd);
+                } else if (virSetInherit(pfds[i].fd, true) < 0) {
+                    virReportSystemError(errno, _("failed to preserve fd %d"), pfds[i].fd);
+                    return -1;
+                }
+            }
+        fd += nfds;
+    }
+    return 0;
+}
+
 # endif /* ! __FreeBSD__ */
 
 /*
-- 
2.33.0

