[2/6] gdb/ser-pipe.c: Duplicate the file descriptors

Message ID 20231117111840.2040709-3-ahajkova@redhat.com
State New
Headers
Series Add vDefaultInferiorFd feature |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed

Commit Message

Alexandra Petlanova Hajkova Nov. 17, 2023, 11:18 a.m. UTC
  Duplicate the numbers of STDOUT/STDIN/STDERR file descriptors
GDB is connected to. Preserved numbers of the file descriptors
could be then sent to the GDBserver. If GDBserver is run locally
and will accept he numbers of the file descriptors, it can start
the inferior connected to the same STDIN/OUT/ERR, GDB is connected to.
---
 gdb/ser-pipe.c | 25 +++++++++++++++++++++++++
 gdb/serial.c   |  4 ++++
 gdb/serial.h   |  4 ++++
 3 files changed, 33 insertions(+)
  

Comments

Tom Tromey Dec. 12, 2023, 7:42 p.m. UTC | #1
>>>>> "Alexandra" == Alexandra Hájková <ahajkova@redhat.com> writes:

Alexandra> Duplicate the numbers of STDOUT/STDIN/STDERR file descriptors
Alexandra> GDB is connected to. Preserved numbers of the file descriptors
Alexandra> could be then sent to the GDBserver. If GDBserver is run locally
Alexandra> and will accept he numbers of the file descriptors, it can start
Alexandra> the inferior connected to the same STDIN/OUT/ERR, GDB is connected to.

Thanks for the patch.

Alexandra> +  /* Preserve STDIN/STDOUT/STDERR so they won't be closed on
Alexandra> +     exec later, after we fork.  */
Alexandra> +  int saved_stdin = dup (STDIN_FILENO);
Alexandra> +  int saved_stdout = dup (STDOUT_FILENO);
Alexandra> +  int saved_stderr = dup (STDERR_FILENO);

I wonder what should happen if any of these fail.

Alexandra> @@ -128,6 +144,10 @@ pipe_open (struct serial *scb, const char *name)
Alexandra>  	  close (err_pdes[1]);
Alexandra>  	}
 
Alexandra> +      mark_fd_no_cloexec (saved_stdout);
Alexandra> +      mark_fd_no_cloexec (saved_stdin);
Alexandra> +      mark_fd_no_cloexec (saved_stderr);
Alexandra> +

This happens in the child process, but due to vfork, it actually affects
the parent process as well.  vfork is weird.

However I think this introduces a funny bug, in that these particular
descriptors are marked as no-cloexec by number.  So, while they are
closed again in the parent:

Alexandra> +  close (saved_stdout);
Alexandra> +  close (saved_stdin);
Alexandra> +  close (saved_stderr);

... their numbers are preserved for not-closing, and so it's possible
they could be reused and inherited by accident by some future
subprocess.

Probably these closes in the parent should be paired with calls to
unmark_fd_no_cloexec.

I'd suggest hoisting the calls to mark_fd_no_cloexec near the dup()s,
too, just to make it more clear.

Tom
  

Patch

diff --git a/gdb/ser-pipe.c b/gdb/ser-pipe.c
index 47ccd33cece..84253cf2e2c 100644
--- a/gdb/ser-pipe.c
+++ b/gdb/ser-pipe.c
@@ -77,6 +77,16 @@  pipe_open (struct serial *scb, const char *name)
       return -1;
     }
 
+  /* Preserve STDIN/STDOUT/STDERR so they won't be closed on
+     exec later, after we fork.  */
+  int saved_stdin = dup (STDIN_FILENO);
+  int saved_stdout = dup (STDOUT_FILENO);
+  int saved_stderr = dup (STDERR_FILENO);
+
+  scb->fds[0] = saved_stdin;
+  scb->fds[1] = saved_stdout;
+  scb->fds[2] = saved_stderr;
+
   /* Create the child process to run the command in.  Note that the
      apparent call to vfork() below *might* actually be a call to
      fork() due to the fact that autoconf will ``#define vfork fork''
@@ -90,6 +100,12 @@  pipe_open (struct serial *scb, const char *name)
       close (pdes[1]);
       close (err_pdes[0]);
       close (err_pdes[1]);
+      close (saved_stdout);
+      close (saved_stdin);
+      close (saved_stderr);
+      scb->fds[0] = -1;
+      scb->fds[1] = -1;
+      scb->fds[2] = -1;
       return -1;
     }
 
@@ -128,6 +144,10 @@  pipe_open (struct serial *scb, const char *name)
 	  close (err_pdes[1]);
 	}
 
+      mark_fd_no_cloexec (saved_stdout);
+      mark_fd_no_cloexec (saved_stdin);
+      mark_fd_no_cloexec (saved_stderr);
+
       close_most_fds ();
 
       const char *shellfile = get_shell ();
@@ -139,6 +159,11 @@  pipe_open (struct serial *scb, const char *name)
   close (pdes[1]);
   if (err_pdes[1] != -1)
     close (err_pdes[1]);
+
+  close (saved_stdout);
+  close (saved_stdin);
+  close (saved_stderr);
+
   /* :end chunk */
   state = XNEW (struct pipe_state);
   state->pid = pid;
diff --git a/gdb/serial.c b/gdb/serial.c
index 8a8bab46ead..a2bbe9a972d 100644
--- a/gdb/serial.c
+++ b/gdb/serial.c
@@ -182,6 +182,10 @@  new_serial (const struct serial_ops *ops)
 
   scb->ops = ops;
 
+  scb->fds[0] = -1;
+  scb->fds[1] = -1;
+  scb->fds[2] = -1;
+
   scb->bufp = scb->buf;
   scb->error_fd = -1;
   scb->refcnt = 1;
diff --git a/gdb/serial.h b/gdb/serial.h
index 3b861200302..113f7360a55 100644
--- a/gdb/serial.h
+++ b/gdb/serial.h
@@ -254,6 +254,10 @@  struct serial
     int async_state;		/* Async internal state.  */
     void *async_context;	/* Async event thread's context */
     serial_event_ftype *async_handler;/* Async event handler */
+
+    /* File descriptors for preserved STDIN/STDOUT/STDERR
+       to be sent to GDBserver when run locally.  */
+    int fds[3];
   };
 
 struct serial_ops