public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH v2 0/3] Improved exit()/_exit() handling
@ 2025-12-11  3:54 David Gibson
  2025-12-11  3:54 ` [PATCH v2 1/3] tcp: Suppress new instance of cppcheck bug 14191 David Gibson
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: David Gibson @ 2025-12-11  3:54 UTC (permalink / raw)
  To: Stefano Brivio, passt-dev; +Cc: David Gibson

As discussed on IRC, while working on the forwarding extensions, I
discovered that failures at certain points of initialisation (in
particular tcp_init() or udp_init() can result in a zombie pasta child
process.

Here's a fix.

v2:
 * Fix a typo
 * Use PR_SET_PDEATHSIG instead of explicit kill()
 * Include updated version of Laurent's cppcheck bug workaround

David Gibson (2):
  treewide: Introduce passt_exit() helper
  pasta: Clean up waiting pasta child on failures

Laurent Vivier (1):
  tcp: Suppress new instance of cppcheck bug 14191

 conf.c       | 11 ++++-------
 log.h        |  9 +++++++--
 passt.c      |  5 ++---
 pasta.c      | 21 +++++++++++++++------
 tap.c        |  6 ++----
 tcp.c        | 10 ++++++----
 util.c       | 25 +++++++++++++++++++------
 util.h       |  1 -
 vhost_user.c |  4 ++--
 9 files changed, 57 insertions(+), 35 deletions(-)

-- 
2.52.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/3] tcp: Suppress new instance of cppcheck bug 14191
  2025-12-11  3:54 [PATCH v2 0/3] Improved exit()/_exit() handling David Gibson
@ 2025-12-11  3:54 ` David Gibson
  2025-12-11  3:54 ` [PATCH v2 2/3] treewide: Introduce passt_exit() helper David Gibson
  2025-12-11  3:54 ` [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures David Gibson
  2 siblings, 0 replies; 7+ messages in thread
From: David Gibson @ 2025-12-11  3:54 UTC (permalink / raw)
  To: Stefano Brivio, passt-dev; +Cc: Laurent Vivier, David Gibson

From: Laurent Vivier <lvivier@redhat.com>

ee9b2361d ("cppcheck: Suppress a buggy cppcheck warning") added a
a suppression for a cppchec bug, since filed (and fixed) in upstream
cppcheck as https://trac.cppcheck.net/ticket/14191.  9139e60fd ("tcp:
Acknowledge everything if it looks like bulk traffic, not interactive")
introduced a new point which triggers the same cppcheck bug.

Add a suppression for the new instance.  This is a revision of Laurent's
earlier patch, updating the comments to make the connection between the
two instances clear, and adding unmatchedSuppression so it doesn't cause
a bogus warning with unaffected cppcheck versions.

Signed-off-by: Laurent Vivier <lvivier@redhat.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 tcp.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/tcp.c b/tcp.c
index 7c721b4b..1711bcc7 100644
--- a/tcp.c
+++ b/tcp.c
@@ -1132,6 +1132,11 @@ int tcp_update_seqack_wnd(const struct ctx *c, struct tcp_tap_conn *conn,
 				return 0;
 		}
 
+		/* This trips a cppcheck bug in some versions, including
+		 * cppcheck 2.18.3.
+		 * https://trac.cppcheck.net/ticket/14191
+		 */
+		/* cppcheck-suppress [uninitvar,unmatchedSuppression] */
 		if ((unsigned)SNDBUF_GET(conn) > (long long)tinfo->tcpi_rtt *
 						 tinfo->tcpi_delivery_rate /
 						 1000 / 1000 *
@@ -1143,10 +1148,7 @@ int tcp_update_seqack_wnd(const struct ctx *c, struct tcp_tap_conn *conn,
 		/* Fall back to acknowledging everything we got */
 		conn->seq_ack_to_tap = conn->seq_from_tap;
 	} else {
-		/* This trips a cppcheck bug in some versions, including
-		 * cppcheck 2.18.3.
-		 * https://sourceforge.net/p/cppcheck/discussion/general/thread/fecde59085/
-		 */
+		/* cppcheck bug 14191 again, see above */
 		/* cppcheck-suppress [uninitvar,unmatchedSuppression] */
 		conn->seq_ack_to_tap = tinfo->tcpi_bytes_acked +
 		                       conn->seq_init_from_tap;
-- 
2.52.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 2/3] treewide: Introduce passt_exit() helper
  2025-12-11  3:54 [PATCH v2 0/3] Improved exit()/_exit() handling David Gibson
  2025-12-11  3:54 ` [PATCH v2 1/3] tcp: Suppress new instance of cppcheck bug 14191 David Gibson
@ 2025-12-11  3:54 ` David Gibson
  2025-12-11  3:54 ` [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures David Gibson
  2 siblings, 0 replies; 7+ messages in thread
From: David Gibson @ 2025-12-11  3:54 UTC (permalink / raw)
  To: Stefano Brivio, passt-dev; +Cc: David Gibson

In d0006fa78 ("treewide: use _exit() over exit()"), we replaced use of
the normal exit(3) with direct calls to _exit(2).  That was because glibc
exit(3) made some unexpected futex() calls, which hit our seccomp profile.

We've since had some bugs due to missing the extra cleanup that exit(3)
implemented, for which we've added explicit cleanup calls.  Specifically,
we have fflush() calls in some places to avoid leaving incomplete messages
on stdout/stderr, and in other places fsync_pcap_and_log() to avoid
leaving incomplete log or pcap files.

It's easy to forget these when adding new error paths, so instead,
implement our own passt_exit() wrapper to perform vital cleanup then call
_exit(2).  This also provides an obvious place to add any additional
cleanups we discover we need in future.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 conf.c       | 11 ++++-------
 log.h        |  9 +++++++--
 passt.c      |  5 ++---
 pasta.c      | 10 ++++------
 tap.c        |  6 ++----
 util.c       | 24 ++++++++++++++++++------
 util.h       |  1 -
 vhost_user.c |  4 ++--
 8 files changed, 39 insertions(+), 31 deletions(-)

diff --git a/conf.c b/conf.c
index fdc19e81..24b44419 100644
--- a/conf.c
+++ b/conf.c
@@ -826,7 +826,7 @@ static void conf_ip6_local(struct ip6_ctx *ip6)
  * usage() - Print usage, exit with given status code
  * @name:	Executable name
  * @f:		Stream to print usage info to
- * @status:	Status code for _exit()
+ * @status:	Status code for exit(2)
  */
 static void usage(const char *name, FILE *f, int status)
 {
@@ -997,8 +997,7 @@ static void usage(const char *name, FILE *f, int status)
 		"    SPEC is as described for TCP above\n"
 		"    default: none\n");
 
-	(void)fflush(f);
-	_exit(status);
+	passt_exit(status);
 
 pasta_opts:
 
@@ -1052,8 +1051,7 @@ pasta_opts:
 		"  --ns-mac-addr ADDR	Set MAC address on tap interface\n"
 		"  --no-splice		Disable inbound socket splicing\n");
 
-	(void)fflush(f);
-	_exit(status);
+	passt_exit(status);
 }
 
 /**
@@ -1621,8 +1619,7 @@ void conf(struct ctx *c, int argc, char **argv)
 			FPRINTF(stdout,
 				c->mode == MODE_PASTA ? "pasta " : "passt ");
 			FPRINTF(stdout, VERSION_BLOB);
-			(void)fflush(stdout);
-			_exit(EXIT_SUCCESS);
+			passt_exit(EXIT_SUCCESS);
 		case 15:
 			ret = snprintf(c->ip4.ifname_out,
 				       sizeof(c->ip4.ifname_out), "%s", optarg);
diff --git a/log.h b/log.h
index c8473c0d..b7b20672 100644
--- a/log.h
+++ b/log.h
@@ -9,6 +9,11 @@
 #include <stdbool.h>
 #include <syslog.h>
 
+/* This would make more sense in util.h, but because we use it in die(), that
+ * would cause awkward circular reference problems.
+ */
+void passt_exit(int status) __attribute__((noreturn));
+
 #define LOGFILE_SIZE_DEFAULT		(1024 * 1024UL)
 #define LOGFILE_CUT_RATIO		30	/* When full, cut ~30% size */
 #define LOGFILE_SIZE_MIN		(5UL * MAX(BUFSIZ, PAGE_SIZE))
@@ -32,13 +37,13 @@ void logmsg_perror(int pri, const char *format, ...)
 #define die(...)							\
 	do {								\
 		err(__VA_ARGS__);					\
-		_exit(EXIT_FAILURE);					\
+		passt_exit(EXIT_FAILURE);				\
 	} while (0)
 
 #define die_perror(...)							\
 	do {								\
 		err_perror(__VA_ARGS__);				\
-		_exit(EXIT_FAILURE);					\
+		passt_exit(EXIT_FAILURE);				\
 	} while (0)
 
 extern int log_file;
diff --git a/passt.c b/passt.c
index 5ed88d07..cf38822f 100644
--- a/passt.c
+++ b/passt.c
@@ -177,8 +177,7 @@ static void exit_handler(int signal)
 {
 	(void)signal;
 
-	fsync_pcap_and_log();
-	_exit(EXIT_SUCCESS);
+	passt_exit(EXIT_SUCCESS);
 }
 
 /**
@@ -399,7 +398,7 @@ int main(int argc, char **argv)
 	flow_init();
 
 	if ((!c.no_udp && udp_init(&c)) || (!c.no_tcp && tcp_init(&c)))
-		_exit(EXIT_FAILURE);
+		passt_exit(EXIT_FAILURE);
 
 	proto_update_l2_buf(c.guest_mac);
 
diff --git a/pasta.c b/pasta.c
index 674b5542..5c693de1 100644
--- a/pasta.c
+++ b/pasta.c
@@ -70,15 +70,13 @@ void pasta_child_handler(int signal)
 	if (pasta_child_pid &&
 	    !waitid(P_PID, pasta_child_pid, &infop, WEXITED | WNOHANG)) {
 		if (infop.si_pid == pasta_child_pid) {
-			fsync_pcap_and_log();
-
 			if (infop.si_code == CLD_EXITED)
-				_exit(infop.si_status);
+				passt_exit(infop.si_status);
 
 			/* If killed by a signal, si_status is the number.
 			 * Follow common shell convention of returning it + 128.
 			 */
-			_exit(infop.si_status + 128);
+			passt_exit(infop.si_status + 128);
 
 			/* Nothing to do, detached PID namespace going away */
 		}
@@ -511,7 +509,7 @@ void pasta_netns_quit_inotify_handler(struct ctx *c, int inotify_fd)
 
 		if (!strncmp(ev->name, c->netns_base, sizeof(c->netns_base))) {
 			info("Namespace %s is gone, exiting", c->netns_base);
-			_exit(EXIT_SUCCESS);
+			passt_exit(EXIT_SUCCESS);
 		}
 	}
 }
@@ -539,7 +537,7 @@ void pasta_netns_quit_timer_handler(struct ctx *c, union epoll_ref ref)
 			return;
 
 		info("Namespace %s is gone, exiting", c->netns_base);
-		_exit(EXIT_SUCCESS);
+		passt_exit(EXIT_SUCCESS);
 	}
 
 	close(fd);
diff --git a/tap.c b/tap.c
index e3ea61cb..9d1344b2 100644
--- a/tap.c
+++ b/tap.c
@@ -1149,10 +1149,8 @@ void tap_sock_reset(struct ctx *c)
 {
 	info("Client connection closed%s", c->one_off ? ", exiting" : "");
 
-	if (c->one_off) {
-		fsync_pcap_and_log();
-		_exit(EXIT_SUCCESS);
-	}
+	if (c->one_off)
+		passt_exit(EXIT_SUCCESS);
 
 	/* Close the connected socket, wait for a new connection */
 	epoll_del(c->epollfd, c->fd_tap);
diff --git a/util.c b/util.c
index bfeb6191..da12c962 100644
--- a/util.c
+++ b/util.c
@@ -555,7 +555,7 @@ void pidfile_write(int fd, pid_t pid)
 
 	if (write(fd, pid_buf, n) < 0) {
 		perror("PID file write");
-		_exit(EXIT_FAILURE);
+		passt_exit(EXIT_FAILURE);
 	}
 
 	close(fd);
@@ -592,12 +592,12 @@ int __daemon(int pidfile_fd, int devnull_fd)
 
 	if (pid == -1) {
 		perror("fork");
-		_exit(EXIT_FAILURE);
+		passt_exit(EXIT_FAILURE);
 	}
 
 	if (pid) {
 		pidfile_write(pidfile_fd, pid);
-		_exit(EXIT_SUCCESS);
+		passt_exit(EXIT_SUCCESS);
 	}
 
 	if (setsid()				< 0 ||
@@ -605,7 +605,7 @@ int __daemon(int pidfile_fd, int devnull_fd)
 	    dup2(devnull_fd, STDOUT_FILENO)	< 0 ||
 	    dup2(devnull_fd, STDERR_FILENO)	< 0 ||
 	    close(devnull_fd))
-		_exit(EXIT_FAILURE);
+		passt_exit(EXIT_FAILURE);
 
 	return 0;
 }
@@ -1225,17 +1225,29 @@ void abort_with_msg(const char *fmt, ...)
 }
 
 /**
- * fsync_pcap_and_log() - Flush pcap and log files as needed
+ * passt_exit() - Perform vital cleanup and exit
+ *
+ * We don't use exit(3) because on some C library versions it can do unexpected
+ * things that hit our seccomp profile (e.g. futex() calls).  This is a bespoke
+ * wrapper around _exit(2) performing just the cleanup that we need.
  *
  * #syscalls fsync
  */
-void fsync_pcap_and_log(void)
+void passt_exit(int status)
 {
+	/* Make sure we don't leave the pcap file truncated */
 	if (pcap_fd != -1 && fsync(pcap_fd))
 		warn_perror("Failed to flush pcap file, it might be truncated");
 
+	/* Make sure we don't leave an incomplete log */
 	if (log_file != -1)
 		(void)fsync(log_file);
+
+	/* Make sure we don't leave any messages incomplete */
+	(void)fflush(stderr);
+	(void)fflush(stdout);
+	
+	_exit(status);
 }
 
 /**
diff --git a/util.h b/util.h
index f7a941fd..4cbb5da3 100644
--- a/util.h
+++ b/util.h
@@ -242,7 +242,6 @@ int read_all_buf(int fd, void *buf, size_t len);
 int read_remainder(int fd, const struct iovec *iov, size_t cnt, size_t skip);
 void close_open_files(int argc, char **argv);
 bool snprintf_check(char *str, size_t size, const char *format, ...);
-void fsync_pcap_and_log(void);
 long clamped_scale(long x, long y, long lo, long hi, long f);
 
 /**
diff --git a/vhost_user.c b/vhost_user.c
index aa7c869d..f9a5646c 100644
--- a/vhost_user.c
+++ b/vhost_user.c
@@ -60,7 +60,7 @@ void vu_print_capabilities(void)
 	info("{");
 	info("  \"type\": \"net\"");
 	info("}");
-	_exit(EXIT_SUCCESS);
+	passt_exit(EXIT_SUCCESS);
 }
 
 /**
@@ -1202,7 +1202,7 @@ void vu_control_handler(struct vu_dev *vdev, int fd, uint32_t events)
 	    !vdev->context->migrate_target) {
 		if (vdev->context->migrate_exit) {
 			info("Migration complete, exiting");
-			_exit(EXIT_SUCCESS);
+			passt_exit(EXIT_SUCCESS);
 		}
 
 		info("Migration complete");
-- 
2.52.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures
  2025-12-11  3:54 [PATCH v2 0/3] Improved exit()/_exit() handling David Gibson
  2025-12-11  3:54 ` [PATCH v2 1/3] tcp: Suppress new instance of cppcheck bug 14191 David Gibson
  2025-12-11  3:54 ` [PATCH v2 2/3] treewide: Introduce passt_exit() helper David Gibson
@ 2025-12-11  3:54 ` David Gibson
  2025-12-11  7:16   ` Stefano Brivio
  2025-12-11 13:52   ` Paul Holzinger
  2 siblings, 2 replies; 7+ messages in thread
From: David Gibson @ 2025-12-11  3:54 UTC (permalink / raw)
  To: Stefano Brivio, passt-dev; +Cc: David Gibson

When pasta is invoked with a command rather than an existing namespace to
attach to, it spawns a child process to run a shell or other command.  We
create that process during conf(), since we need the namespace to exist for
much of our setup.  However, we don't want the specified command to run
until the pasta network interface is ready for use.  Therefore,
pasta_spawn_cmd() executing in the child waits before exec()ing.  main()
signals the child to continue with SIGUSR1 shortly before entering the
main forwarding loop.

This has the downside that if we exit due to any kind of failure between
conf() and the SIGUSR1, the child process will be around waiting
indefinitely.  The user must manually clean this up.

Make this cleaner, by having the child use PR_SET_PDEATHSIG to have
itself killed if the parent dies during this window.  Technically
speaking this is racy: if the parent dies before the child can call
the prctl() it will be left zombie-like as before.  However, as long
as the parent completes pasta_wait_for_ns() before dying, I wasn't
able to trigger the race.  Since the consequences of this going wrong
are merely a bit ugly, I think that's good enough.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 pasta.c | 11 +++++++++++
 util.c  |  1 +
 2 files changed, 12 insertions(+)

diff --git a/pasta.c b/pasta.c
index 5c693de1..c307b8a8 100644
--- a/pasta.c
+++ b/pasta.c
@@ -40,6 +40,7 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <net/ethernet.h>
+#include <sys/prctl.h>
 #include <sys/syscall.h>
 #include <linux/magic.h>
 
@@ -189,6 +190,10 @@ static int pasta_spawn_cmd(void *arg)
 	size_t conf_hostname_len;
 	sigset_t set;
 
+	/* If the parent dies with an error, so should we */
+	if (prctl(PR_SET_PDEATHSIG, SIGKILL))
+		die_perror("Couldn't set PR_SET_PDEATHSIG");
+
 	/* We run in a detached PID and mount namespace: mount /proc over */
 	if (mount("", "/proc", "proc", 0, NULL))
 		warn_perror("Couldn't mount /proc");
@@ -215,6 +220,12 @@ static int pasta_spawn_cmd(void *arg)
 	sigaddset(&set, SIGUSR1);
 	sigwaitinfo(&set, NULL);
 
+	/* Once exec()ed this process is more valuable, and easier to see and
+	 * clean up.  Let us outlive our parent now.
+	 */
+	if (prctl(PR_SET_PDEATHSIG, 0))
+		die_perror("Couldn't clear PR_SET_PDEATHSIG");
+
 	execvp(a->exe, a->argv);
 
 	die_perror("Failed to start command or shell");
diff --git a/util.c b/util.c
index da12c962..27303950 100644
--- a/util.c
+++ b/util.c
@@ -35,6 +35,7 @@
 #include "log.h"
 #include "pcap.h"
 #include "epoll_ctl.h"
+#include "pasta.h"
 #ifdef HAS_GETRANDOM
 #include <sys/random.h>
 #endif
-- 
2.52.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures
  2025-12-11  3:54 ` [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures David Gibson
@ 2025-12-11  7:16   ` Stefano Brivio
  2025-12-11  8:45     ` David Gibson
  2025-12-11 13:52   ` Paul Holzinger
  1 sibling, 1 reply; 7+ messages in thread
From: Stefano Brivio @ 2025-12-11  7:16 UTC (permalink / raw)
  To: David Gibson; +Cc: passt-dev, Paul Holzinger

On Thu, 11 Dec 2025 14:54:36 +1100
David Gibson <david@gibson.dropbear.id.au> wrote:

> When pasta is invoked with a command rather than an existing namespace to
> attach to, it spawns a child process to run a shell or other command.  We
> create that process during conf(), since we need the namespace to exist for
> much of our setup.  However, we don't want the specified command to run
> until the pasta network interface is ready for use.  Therefore,
> pasta_spawn_cmd() executing in the child waits before exec()ing.  main()
> signals the child to continue with SIGUSR1 shortly before entering the
> main forwarding loop.
> 
> This has the downside that if we exit due to any kind of failure between
> conf() and the SIGUSR1, the child process will be around waiting
> indefinitely.  The user must manually clean this up.
> 
> Make this cleaner, by having the child use PR_SET_PDEATHSIG to have
> itself killed if the parent dies during this window.  Technically
> speaking this is racy: if the parent dies before the child can call
> the prctl() it will be left zombie-like as before.  However, as long
> as the parent completes pasta_wait_for_ns() before dying, I wasn't
> able to trigger the race.  Since the consequences of this going wrong
> are merely a bit ugly, I think that's good enough.
> 
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>

Is this:

Suggested-by: Paul Holzinger <pholzing@redhat.com>

? In any case, Cc'ing him with full quote to be sure he doesn't miss v2.

> ---
>  pasta.c | 11 +++++++++++
>  util.c  |  1 +
>  2 files changed, 12 insertions(+)
> 
> diff --git a/pasta.c b/pasta.c
> index 5c693de1..c307b8a8 100644
> --- a/pasta.c
> +++ b/pasta.c
> @@ -40,6 +40,7 @@
>  #include <arpa/inet.h>
>  #include <netinet/in.h>
>  #include <net/ethernet.h>
> +#include <sys/prctl.h>
>  #include <sys/syscall.h>
>  #include <linux/magic.h>
>  
> @@ -189,6 +190,10 @@ static int pasta_spawn_cmd(void *arg)
>  	size_t conf_hostname_len;
>  	sigset_t set;
>  
> +	/* If the parent dies with an error, so should we */
> +	if (prctl(PR_SET_PDEATHSIG, SIGKILL))
> +		die_perror("Couldn't set PR_SET_PDEATHSIG");
> +
>  	/* We run in a detached PID and mount namespace: mount /proc over */
>  	if (mount("", "/proc", "proc", 0, NULL))
>  		warn_perror("Couldn't mount /proc");
> @@ -215,6 +220,12 @@ static int pasta_spawn_cmd(void *arg)
>  	sigaddset(&set, SIGUSR1);
>  	sigwaitinfo(&set, NULL);
>  
> +	/* Once exec()ed this process is more valuable, and easier to see and
> +	 * clean up.  Let us outlive our parent now.
> +	 */
> +	if (prctl(PR_SET_PDEATHSIG, 0))
> +		die_perror("Couldn't clear PR_SET_PDEATHSIG");
> +
>  	execvp(a->exe, a->argv);
>  
>  	die_perror("Failed to start command or shell");
> diff --git a/util.c b/util.c
> index da12c962..27303950 100644
> --- a/util.c
> +++ b/util.c
> @@ -35,6 +35,7 @@
>  #include "log.h"
>  #include "pcap.h"
>  #include "epoll_ctl.h"
> +#include "pasta.h"
>  #ifdef HAS_GETRANDOM
>  #include <sys/random.h>
>  #endif

-- 
Stefano


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures
  2025-12-11  7:16   ` Stefano Brivio
@ 2025-12-11  8:45     ` David Gibson
  0 siblings, 0 replies; 7+ messages in thread
From: David Gibson @ 2025-12-11  8:45 UTC (permalink / raw)
  To: Stefano Brivio; +Cc: passt-dev, Paul Holzinger

[-- Attachment #1: Type: text/plain, Size: 3595 bytes --]

On Thu, Dec 11, 2025 at 08:16:45AM +0100, Stefano Brivio wrote:
> On Thu, 11 Dec 2025 14:54:36 +1100
> David Gibson <david@gibson.dropbear.id.au> wrote:
> 
> > When pasta is invoked with a command rather than an existing namespace to
> > attach to, it spawns a child process to run a shell or other command.  We
> > create that process during conf(), since we need the namespace to exist for
> > much of our setup.  However, we don't want the specified command to run
> > until the pasta network interface is ready for use.  Therefore,
> > pasta_spawn_cmd() executing in the child waits before exec()ing.  main()
> > signals the child to continue with SIGUSR1 shortly before entering the
> > main forwarding loop.
> > 
> > This has the downside that if we exit due to any kind of failure between
> > conf() and the SIGUSR1, the child process will be around waiting
> > indefinitely.  The user must manually clean this up.
> > 
> > Make this cleaner, by having the child use PR_SET_PDEATHSIG to have
> > itself killed if the parent dies during this window.  Technically
> > speaking this is racy: if the parent dies before the child can call
> > the prctl() it will be left zombie-like as before.  However, as long
> > as the parent completes pasta_wait_for_ns() before dying, I wasn't
> > able to trigger the race.  Since the consequences of this going wrong
> > are merely a bit ugly, I think that's good enough.
> > 
> > Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> 
> Is this:
> 
> Suggested-by: Paul Holzinger <pholzing@redhat.com>
> 
> ? In any case, Cc'ing him with full quote to be sure he doesn't miss v2.

It is, yes.  Sorry, forgot to include that.

> > ---
> >  pasta.c | 11 +++++++++++
> >  util.c  |  1 +
> >  2 files changed, 12 insertions(+)
> > 
> > diff --git a/pasta.c b/pasta.c
> > index 5c693de1..c307b8a8 100644
> > --- a/pasta.c
> > +++ b/pasta.c
> > @@ -40,6 +40,7 @@
> >  #include <arpa/inet.h>
> >  #include <netinet/in.h>
> >  #include <net/ethernet.h>
> > +#include <sys/prctl.h>
> >  #include <sys/syscall.h>
> >  #include <linux/magic.h>
> >  
> > @@ -189,6 +190,10 @@ static int pasta_spawn_cmd(void *arg)
> >  	size_t conf_hostname_len;
> >  	sigset_t set;
> >  
> > +	/* If the parent dies with an error, so should we */
> > +	if (prctl(PR_SET_PDEATHSIG, SIGKILL))
> > +		die_perror("Couldn't set PR_SET_PDEATHSIG");
> > +
> >  	/* We run in a detached PID and mount namespace: mount /proc over */
> >  	if (mount("", "/proc", "proc", 0, NULL))
> >  		warn_perror("Couldn't mount /proc");
> > @@ -215,6 +220,12 @@ static int pasta_spawn_cmd(void *arg)
> >  	sigaddset(&set, SIGUSR1);
> >  	sigwaitinfo(&set, NULL);
> >  
> > +	/* Once exec()ed this process is more valuable, and easier to see and
> > +	 * clean up.  Let us outlive our parent now.
> > +	 */
> > +	if (prctl(PR_SET_PDEATHSIG, 0))
> > +		die_perror("Couldn't clear PR_SET_PDEATHSIG");
> > +
> >  	execvp(a->exe, a->argv);
> >  
> >  	die_perror("Failed to start command or shell");
> > diff --git a/util.c b/util.c
> > index da12c962..27303950 100644
> > --- a/util.c
> > +++ b/util.c
> > @@ -35,6 +35,7 @@
> >  #include "log.h"
> >  #include "pcap.h"
> >  #include "epoll_ctl.h"
> > +#include "pasta.h"
> >  #ifdef HAS_GETRANDOM
> >  #include <sys/random.h>
> >  #endif
> 
> -- 
> Stefano
> 

-- 
David Gibson (he or they)	| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you, not the other way
				| around.
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures
  2025-12-11  3:54 ` [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures David Gibson
  2025-12-11  7:16   ` Stefano Brivio
@ 2025-12-11 13:52   ` Paul Holzinger
  1 sibling, 0 replies; 7+ messages in thread
From: Paul Holzinger @ 2025-12-11 13:52 UTC (permalink / raw)
  To: David Gibson, passt-dev; +Cc: Stefano Brivio


On 11/12/2025 04:54, David Gibson wrote:
> When pasta is invoked with a command rather than an existing namespace to
> attach to, it spawns a child process to run a shell or other command.  We
> create that process during conf(), since we need the namespace to exist for
> much of our setup.  However, we don't want the specified command to run
> until the pasta network interface is ready for use.  Therefore,
> pasta_spawn_cmd() executing in the child waits before exec()ing.  main()
> signals the child to continue with SIGUSR1 shortly before entering the
> main forwarding loop.
>
> This has the downside that if we exit due to any kind of failure between
> conf() and the SIGUSR1, the child process will be around waiting
> indefinitely.  The user must manually clean this up.
>
> Make this cleaner, by having the child use PR_SET_PDEATHSIG to have
> itself killed if the parent dies during this window.  Technically
> speaking this is racy: if the parent dies before the child can call
> the prctl() it will be left zombie-like as before.  However, as long
> as the parent completes pasta_wait_for_ns() before dying, I wasn't
> able to trigger the race.  Since the consequences of this going wrong
> are merely a bit ugly, I think that's good enough.
>
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Paul Holzinger <pholzing@redhat.com>
> ---
>   pasta.c | 11 +++++++++++
>   util.c  |  1 +
>   2 files changed, 12 insertions(+)
>
> diff --git a/pasta.c b/pasta.c
> index 5c693de1..c307b8a8 100644
> --- a/pasta.c
> +++ b/pasta.c
> @@ -40,6 +40,7 @@
>   #include <arpa/inet.h>
>   #include <netinet/in.h>
>   #include <net/ethernet.h>
> +#include <sys/prctl.h>
>   #include <sys/syscall.h>
>   #include <linux/magic.h>
>   
> @@ -189,6 +190,10 @@ static int pasta_spawn_cmd(void *arg)
>   	size_t conf_hostname_len;
>   	sigset_t set;
>   
> +	/* If the parent dies with an error, so should we */
> +	if (prctl(PR_SET_PDEATHSIG, SIGKILL))
> +		die_perror("Couldn't set PR_SET_PDEATHSIG");
> +
>   	/* We run in a detached PID and mount namespace: mount /proc over */
>   	if (mount("", "/proc", "proc", 0, NULL))
>   		warn_perror("Couldn't mount /proc");
> @@ -215,6 +220,12 @@ static int pasta_spawn_cmd(void *arg)
>   	sigaddset(&set, SIGUSR1);
>   	sigwaitinfo(&set, NULL);
>   
> +	/* Once exec()ed this process is more valuable, and easier to see and
> +	 * clean up.  Let us outlive our parent now.
> +	 */
> +	if (prctl(PR_SET_PDEATHSIG, 0))
> +		die_perror("Couldn't clear PR_SET_PDEATHSIG");
> +
>   	execvp(a->exe, a->argv);
>   
>   	die_perror("Failed to start command or shell");
> diff --git a/util.c b/util.c
> index da12c962..27303950 100644
> --- a/util.c
> +++ b/util.c
> @@ -35,6 +35,7 @@
>   #include "log.h"
>   #include "pcap.h"
>   #include "epoll_ctl.h"
> +#include "pasta.h"
>   #ifdef HAS_GETRANDOM
>   #include <sys/random.h>
>   #endif

-- 
Paul Holzinger


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-12-11 13:52 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-11  3:54 [PATCH v2 0/3] Improved exit()/_exit() handling David Gibson
2025-12-11  3:54 ` [PATCH v2 1/3] tcp: Suppress new instance of cppcheck bug 14191 David Gibson
2025-12-11  3:54 ` [PATCH v2 2/3] treewide: Introduce passt_exit() helper David Gibson
2025-12-11  3:54 ` [PATCH v2 3/3] pasta: Clean up waiting pasta child on failures David Gibson
2025-12-11  7:16   ` Stefano Brivio
2025-12-11  8:45     ` David Gibson
2025-12-11 13:52   ` Paul Holzinger

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).