Looks like `read -e -u N' creates (and leaks) a new stream for fd N.
If a second `read' tries to fdopen the same fd once more, Android's
FDSAN (on and enforcing defualt) causes the process to abort:

$ bash -c 'read -eu3; read -eu3' 3<&0
fdsan: failed to exchange ownership of file descriptor:
fd 3 is owned by FILE* 0x7131a45d18, was expected to be unowned
Aborted                    bash -c 'read -eu3; read -eu3' 3<&0

I guess when Readline is being used for `read' input, a proper thing
to do is dup() the fd supplied by -u (thereby defeating the point of
the option..) and fclose() the resulting stream when done.

I think the current behavior is due to a report[1] about -u being
silently ignored with -e, though maybe forbidding the combination
explicitly would be fine too.

[1]: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=927768
---
 builtins/read.def | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/builtins/read.def b/builtins/read.def
index 35b71967..fbba6c62 100644
--- a/builtins/read.def
+++ b/builtins/read.def
@@ -201,6 +201,17 @@ read_builtin_timeout (int fd)
 #endif
 }
 
+void reset_rl_instream (FILE *save_instream)
+{
+  fclose (rl_instream);
+  rl_instream = save_instream;
+}
+
+void uw_reset_rl_instream (void *fp)
+{
+  reset_rl_instream (fp);
+}
+
 /* Read the value of the shell variables whose names follow.
    The reading is done from the current input stream, whatever
    that may be.  Successive words of the input line are assigned
@@ -613,12 +624,26 @@ read_builtin (WORD_LIST *list)
   save_instream = 0;
   if (edit && fd != 0)
     {
+      int fd2;
+      FILE *fp2;
+
+      if ((fd2 = dup (fd)) < 0 || (fp2 = fdopen (fd2, "r")) == 0)
+       {
+         builtin_error ("%d: %s", fd, strerror (errno));
+         if (fd2 >= 0)
+           close (fd2);
+         run_unwind_frame ("read_builtin");
+         return (EXECUTION_FAILURE);
+       }
+
       if (bash_readline_initialized == 0)
        initialize_readline ();
 
-      unwind_protect_var (rl_instream);
       save_instream = rl_instream;
-      rl_instream = fdopen (fd, "r");  
+      rl_instream = fp2;
+      add_unwind_protect (uw_reset_rl_instream, save_instream);
+
+      fd = fd2;
     }
 #endif
 
@@ -896,7 +921,7 @@ add_char:
 
 #if defined (READLINE)
   if (save_instream)
-    rl_instream = save_instream;       /* can't portably free it */
+    reset_rl_instream (save_instream);
 #endif
 
   discard_unwind_frame ("read_builtin");
-- 
2.45.1


Reply via email to