If a child process sets stdin to non-blocking and does not set it back to 
blocking before exiting, other processes may fail to read from stdin.

Reproducer steps :

$ cat set_nonblock.c 
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
        char buff[256];
        int flags = fcntl (0, F_GETFL);
        if (fcntl(0, F_SETFL, flags | O_NONBLOCK)) {
                perror("fcntl failed");
        }
        if (read(0, buff, 256) == -1) {
                perror("read failed");
        }
}

$ cc -o set_nonblock set_nonblock.c
$ cat test.sh 
#!/bin/bash

./set_nonblock

cat

$ ./test.sh 
read failed: Resource temporarily unavailable
cat: -: Resource temporarily unavailable

Attached patch sets standard file descriptors to blocking before child process 
starts.

-- 
--
Siteshwar Vashisht
From f4fd3c17cb15f87fc8733b44bd477e32ff2d002a Mon Sep 17 00:00:00 2001
From: Siteshwar Vashisht <svashi...@redhat.com>
Date: Sun, 22 Jan 2017 08:25:23 +0100
Subject: [PATCH] Make stdin blocking when command starts

---
 execute_cmd.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/execute_cmd.c b/execute_cmd.c
index 00389df..d63f6e5 100644
--- a/execute_cmd.c
+++ b/execute_cmd.c
@@ -531,6 +531,22 @@ async_redirect_stdin ()
     internal_error (_("cannot redirect standard input from /dev/null: %s"), strerror (errno));
 }
 
+/* Make stdin,stdout and stderr blocking */
+static void
+set_standard_fds_blocking() {
+  int flags = fcntl (0, F_GETFL);
+  if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK))
+    sys_error (_("Failed to make stdin blocking"));
+
+  flags = fcntl (1, F_GETFL);
+  if (fcntl(1, F_SETFL, flags & ~O_NONBLOCK))
+    sys_error (_("Failed to make stdout blocking"));
+
+  flags = fcntl (2, F_GETFL);
+  if (fcntl(2, F_SETFL, flags & ~O_NONBLOCK))
+    sys_error (_("Failed to make stderr blocking"));
+}
+
 #define DESCRIBE_PID(pid) do { if (interactive) describe_pid (pid); } while (0)
 
 extern int rpm_requires;
@@ -594,6 +610,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
   exec_result = EXECUTION_SUCCESS;
 
+  set_standard_fds_blocking();
+
   /* If a command was being explicitly run in a subshell, or if it is
      a shell control-structure, and it has a pipe, then we do the command
      in a subshell. */
-- 
2.9.3

Reply via email to