From patchwork Wed Apr 29 20:00:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 134177 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id B8A294B9DB56 for ; Wed, 29 Apr 2026 20:01:36 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B8A294B9DB56 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=ARqW6or+ X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-yw1-x1130.google.com (mail-yw1-x1130.google.com [IPv6:2607:f8b0:4864:20::1130]) by sourceware.org (Postfix) with ESMTPS id EA7504BBCD89 for ; Wed, 29 Apr 2026 20:00:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EA7504BBCD89 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linaro.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org EA7504BBCD89 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1130 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777492857; cv=none; b=i49+9fIkT0TQAE09nHmTej07cWrLBrayln/8go1DNbrjNRgtpH4BdJNRyV/CtgF4hz9fAiAIkrDt5SqCmBWaEDr1e4uWfa2VW+3obKx6TY/5YDLcC9/LLSyZDPytojyyrMAdBmYUhgDeMJE/rroN5qNaOrALDlF67/pG9iRIzRY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777492857; c=relaxed/simple; bh=VbQ6aCRZkDNzjq5RFDr3/8yKkBHpYt8QuP9bM3aXZv8=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=jfHE+I+FCxdH+cAK/EZvd4HdxmsCQo625/X6Z9rEKFHpezpEbpzxQmz036mJK2PeP8EyP/GyCJUNIl7GHFgPsoVTml8bfV/gpir+8Y2P5j4zuiuFuAUz9M97Mbg7fphPhVRuLayDSQHuhw7EW4Rpi7T5CzXX2s3kEq/Csrcw1J0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EA7504BBCD89 Received: by mail-yw1-x1130.google.com with SMTP id 00721157ae682-7bb0d18c7f9so1876307b3.0 for ; Wed, 29 Apr 2026 13:00:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1777492856; x=1778097656; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=vLuaoxs7ooUBoD8Qv2k/YUxLJ6d0wozZS4hbu+dXexw=; b=ARqW6or+psJ1EmcputA4XT7KxWeTARL9/wO+pXu8vBHcWFRR9UF0EoSzR0Qzb+hHzz 9dZpZHgsfoZN9Eir+bcVLlgj5N6oS5cgmSr+JAFUrQgmE1KPzjNVRliTr7jQU5ln/7uh FSbgqcr8rA/DCShbNngbWZ23zvVY9ciP9RkGeajhnfS0g4fMm1ggo4IMUOnVbgaW8IqB ZW6uZazJWFYOpzb6kptZSQP9JRC6mD5I9ZAt93s9T8vP3J6lRIYO+D8RpLXo7SB/ChfU NbK0KjytPdUymt5SWjbmmOOIADxeKLA7rmx6yzJnYKDhisiah811GkE5sRD/c9/BcY++ M4zg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777492856; x=1778097656; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=vLuaoxs7ooUBoD8Qv2k/YUxLJ6d0wozZS4hbu+dXexw=; b=aBXRjIaDb8HszoNZXSa8ubTrOxpghl4Pl+BQOK5PqItbd7rX0HdGX1zkvyM76LmlLW nQ6oBgkKBgVJ06W3ucpyDK5gRApjXxqqk7JkukT4Jh7PD7ySc0Ho59i4gIQwZioTvWOL 4BkQvTGimusVSeN7aPcoDOXhxfK64oN2S38an2wUMNnVLwU4M19/qbV1OhKM72ioxeyM ymA7Doaa9BWDmWUONYm9mmEZfIN2jfuVlXI5lTDOOtazaG1G10SvBwYnsUxQOnWX69oh qJTYoVkOgd/HGSme8OWnyEz5FXLgx54/LfeH6pLdv/ZpZBu243F11aqY6ud3OE2VBzjN HfcQ== X-Gm-Message-State: AOJu0YwkYETU1Hk6H4udXBjgFCLVHKxTaAAp2wKALwVBI2PkasSdI5Mg S9c+u8gqCCpKkesffL+NO2gYUTsDgxt396FzM5aFRLE/bxO41aYw6CzyH4QdbA6NHuJYxT9ciS/ /EzV5 X-Gm-Gg: AeBDietPgaUbg2I03YGhUh5B4lSK4F+JX9Xz9C85h3APtwjCqwWCD+FC0lCceMKSEpv yUynLFST+jTaZ6JD360gUSJJosPqE7lX8p9PXkXs+lUMhux1p6u1seu139qFcFVV07zmE59yuzu Dtq2fIUhrkdT2VYIAuHQ/1IdudjvXRETpqaHOoh5CoSs6hnEj0pAZGLY17GLhdkSwPy0Cu05hnV J4oUXiITcxz8yrSRDfRZvcSm9N737N85+k6A1kc37Z2COfXgQkLkXKcZv8WTXmeF6TEnoZLvGqJ D1jWsdsosNwcKG6JUmwZkpCVbLwmcZKyCON404lF3BeJbWMq0flvq5+OmJbGFOKtZM5+LNX78r4 xacAL1H+5z3E2DYqUKeCt14WYYISfp8M1X1RTFeE4Gru5JPBKSmSgaBjBT/t6x7UlbbVrzrs1SG yX7R2QExSObYgBPFz23W+6dwno0I85D77abqCFQ1hePFGDqS8gYvkXQec= X-Received: by 2002:a05:690c:6c13:b0:79a:cc18:19c6 with SMTP id 00721157ae682-7bd52969cafmr1829807b3.34.1777492855028; Wed, 29 Apr 2026 13:00:55 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c0:44cb:86ff:9d41:bd4f:e3e]) by smtp.gmail.com with ESMTPSA id 00721157ae682-7bd258a30cdsm20475987b3.23.2026.04.29.13.00.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Apr 2026 13:00:47 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: =?utf-8?q?Cristian_Rodr=C3=ADguez?= Subject: [RFC] posix: Make system, popen, and wordexp try pidfd_spawn first (BZ 34001) Date: Wed, 29 Apr 2026 17:00:10 -0300 Message-ID: <20260429200041.3051117-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org This patch updates system, popen, and wordexp to use a new internal process management abstraction (__spawn_process_create, __spawn_process_wait, and __spawn_process_kill) that leverages pidfd_spawn on supported Linux kernels before falling back to traditional posix_spawn. The internal process_create_id_t wraps both pid_t and int (for pidfd) and uses the MSB to differentiate a returned pidfd from a standard PID. This avoid the need to use extra word to track which type is used and allows to use the interface as drop-in replacement for posix_spawn and waitpid. Because pidfd_spawn allocates a new file descriptor in the calling process for the pidfd, this change alters the resource consumption semantics of these functions. The __spawn_process_create handles if pidfd_spawns return EMFILE or ENFILE, but if the pidfd_spawns is successful a file descriptor is consumed and it counts for the process limits. Checked on x86_64-linux-gnu and i686-linux-gnu. --- include/clone_internal.h | 1 + include/spawn.h | 33 +++++ libio/iopopen.c | 23 ++-- posix/Makefile | 9 ++ posix/tst-spawn_process-fd-exhaustion.c | 120 +++++++++++++++++ posix/wordexp.c | 39 +++--- sysdeps/generic/spawn_process.c | 43 +++++++ sysdeps/posix/system.c | 25 ++-- .../unix/sysv/linux/include/bits/spawn_ext.h | 11 ++ sysdeps/unix/sysv/linux/not-errno.h | 11 ++ sysdeps/unix/sysv/linux/pidfd_spawn.c | 10 +- sysdeps/unix/sysv/linux/spawn_process.c | 121 ++++++++++++++++++ 12 files changed, 404 insertions(+), 42 deletions(-) create mode 100644 posix/tst-spawn_process-fd-exhaustion.c create mode 100644 sysdeps/generic/spawn_process.c create mode 100644 sysdeps/unix/sysv/linux/include/bits/spawn_ext.h create mode 100644 sysdeps/unix/sysv/linux/spawn_process.c diff --git a/include/clone_internal.h b/include/clone_internal.h index 567160ebb5..001797fccd 100644 --- a/include/clone_internal.h +++ b/include/clone_internal.h @@ -1,6 +1,7 @@ #ifndef _CLONE_INTERNAL_H #define _CLONE_INTERNAL_H +#include #include /* The clone3 syscall provides a superset of the functionality of the clone diff --git a/include/spawn.h b/include/spawn.h index 4a0b1849da..95375337e4 100644 --- a/include/spawn.h +++ b/include/spawn.h @@ -2,6 +2,8 @@ #include # ifndef _ISOMAC +# include + __typeof (posix_spawn) __posix_spawn; libc_hidden_proto (__posix_spawn) @@ -35,5 +37,36 @@ __typeof (posix_spawnattr_setsigdefault) __posix_spawnattr_setsigdefault __typeof (posix_spawnattr_setsigmask) __posix_spawnattr_setsigmask attribute_hidden; +typedef int process_create_id_t; +_Static_assert (sizeof (process_create_id_t) == sizeof (pid_t), + "process_create_id_t must have same size as pid_t"); + +/* Create a new process using pidfd_spawn or posix_spawn as a fallback, and + return an identifier that should be only be used with __spawn_process_wait. + The identifier is not changed if the process creation fails and the + function returns the same error code as {pidfd,posix}_spawn. */ +int __spawn_process_create (process_create_id_t *, + const char *__restric__, + const posix_spawn_file_actions_t *__restrict, + const posix_spawnattr_t *__restrict, + char *const [__restrict_arr], + char *const [__restrict_arr]) + attribute_hidden; + +/* Wait for a process created with process_create_id_t, and return the status + code as for waitpid in second argument. The third argument is the options + to be used, for instance WNOHANG. + + It returns either the process id returned by __spawn_process_create for + the case of pidfd, or the pid_t if the fallback is used. This semantic + allows to use this in place of waitpid calls. */ +process_create_id_t __spawn_process_wait (process_create_id_t, int *, int) + attribute_hidden; + +/* Send a signal to the created process using either pidfd_send_signal or + kill. */ +int __spawn_process_kill (process_create_id_t, int) + attribute_hidden; + # endif /* !_ISOMAC */ #endif /* spawn.h */ diff --git a/libio/iopopen.c b/libio/iopopen.c index 37b6b1386b..9f39dc858f 100644 --- a/libio/iopopen.c +++ b/libio/iopopen.c @@ -40,7 +40,7 @@ struct _IO_proc_file { struct _IO_FILE_plus file; /* Following fields must match those in class procbuf (procbuf.h) */ - pid_t pid; + process_create_id_t procid; struct _IO_proc_file *next; }; typedef struct _IO_proc_file _IO_proc_file; @@ -106,9 +106,16 @@ spawn_process (posix_spawn_file_actions_t *fa, FILE *fp, const char *command, } } - err = __posix_spawn (&((_IO_proc_file *) fp)->pid, _PATH_BSHELL, fa, NULL, - (char *const[]){ (char*) "sh", (char*) "-c", (char*) "--", - (char *) command, NULL }, __environ); + err = __spawn_process_create (&((_IO_proc_file *) fp)->procid, + _PATH_BSHELL, + fa, + NULL, + (char *const[]){ (char*) "sh", + (char*) "-c", + (char*) "--", + (char *) command, + NULL }, + __environ); if (err != 0) return err; @@ -274,7 +281,6 @@ _IO_new_proc_close (FILE *fp) /* This is not name-space clean. FIXME! */ int wstatus; _IO_proc_file **ptr = &proc_file_chain; - pid_t wait_pid; int status = -1; /* Unlink from proc_file_chain. */ @@ -306,11 +312,12 @@ _IO_new_proc_close (FILE *fp) { int state; __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state); - wait_pid = __waitpid (((_IO_proc_file *) fp)->pid, &wstatus, 0); + status =__spawn_process_wait (((_IO_proc_file *) fp)->procid, + &wstatus, 0); __pthread_setcancelstate (state, NULL); } - while (wait_pid == -1 && errno == EINTR); - if (wait_pid == -1) + while (status == -1 && errno == EINTR); + if (status == -1) return -1; return wstatus; } diff --git a/posix/Makefile b/posix/Makefile index 0fa532396f..af5a5d9960 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -155,6 +155,7 @@ routines := \ spawn_faction_addtcsetpgrp_np \ spawn_faction_destroy \ spawn_faction_init \ + spawn_process \ spawn_valid_fd \ spawnattr_destroy \ spawnattr_getdefault \ @@ -350,7 +351,12 @@ tests += \ # tests endif +tests-static-internal := \ + tst-spawn_process-fd-exhaustion \ + # tests-static-internal + tests-internal := \ + $(tests-static-internal)\ bug-regex5 \ bug-regex20 \ bug-regex33 \ @@ -397,6 +403,7 @@ tests += \ endif tests-static = \ + $(tests-static-internal) \ tst-exec-static \ tst-libc-message \ tst-spawn-static \ @@ -803,3 +810,5 @@ tst-wordexp-reuse-ENV += MALLOC_TRACE=$(objpfx)tst-wordexp-reuse.mtrace \ $(objpfx)tst-wordexp-reuse-mem.out: $(objpfx)tst-wordexp-reuse.out $(common-objpfx)malloc/mtrace $(objpfx)tst-wordexp-reuse.mtrace > $@; \ $(evaluate-test) + +CFLAGS-tst-spawn_process-fd-exhaustion.c += -DOBJPFX=\"$(objpfx)\" diff --git a/posix/tst-spawn_process-fd-exhaustion.c b/posix/tst-spawn_process-fd-exhaustion.c new file mode 100644 index 0000000000..98c1fea515 --- /dev/null +++ b/posix/tst-spawn_process-fd-exhaustion.c @@ -0,0 +1,120 @@ +/* Check if __spawn_process_create works if there is no available + file descriptor. + Copyright (C) 2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static const char pidfile[] = OBJPFX "tst-spawn_process-fd-exhaustion.pid"; + +static int +do_test (void) +{ + struct rlimit rl; + int max_fd = 24; + + if (getrlimit (RLIMIT_NOFILE, &rl) == -1) + FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m"); + + max_fd = (rl.rlim_cur < max_fd ? rl.rlim_cur : max_fd); + rl.rlim_cur = max_fd; + + if (setrlimit (RLIMIT_NOFILE, &rl) == -1) + FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m"); + + /* Exhauste the file descriptor limit with temporary files. */ + int files[max_fd]; + int nfiles = 0; + for (; nfiles < max_fd; nfiles++) + { + int fd = create_temp_file ("tst-spawn_process-fd-exhaustion.pid.", NULL); + if (fd == -1) + { + if (errno != EMFILE) + FAIL_EXIT1 ("create_temp_file: %m"); + break; + } + int flags = fcntl (fd, F_GETFD, 0); + TEST_VERIFY_EXIT (flags != -1); + TEST_VERIFY_EXIT (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) != -1); + files[nfiles] = fd; + } + TEST_VERIFY_EXIT (nfiles != 0); + + process_create_id_t pid; + { + posix_spawn_file_actions_t fa; + TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0); + TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, STDOUT_FILENO, + pidfile, + O_WRONLY| O_CREAT + | O_TRUNC, + 0644), 0); + + TEST_COMPARE (posix_spawn_file_actions_adddup2 (&fa, STDOUT_FILENO, + STDERR_FILENO), 0); + char *spawn_argv[] = + { + (char *) _PATH_BSHELL, + (char *) "-c", + (char *) "echo $$", + NULL + }; + int r = __spawn_process_create (&pid, _PATH_BSHELL, &fa, NULL, + spawn_argv, NULL); + TEST_COMPARE (r, 0); + + int status; + TEST_COMPARE (__spawn_process_wait (pid, &status, 0), pid); + TEST_COMPARE (WIFEXITED (status), 1); + TEST_COMPARE (WEXITSTATUS (status), 0); + } + + for (int i=0; i= 0); + + /* We only expect to read the PID. */ + char *endp; + long int rpid = strtol (buf, &endp, 10); + TEST_VERIFY (*endp == '\n' && endp != buf); + + TEST_COMPARE (rpid, pid); + } + + return 0; +} + +#include diff --git a/posix/wordexp.c b/posix/wordexp.c index 4a8541add4..a4d7040612 100644 --- a/posix/wordexp.c +++ b/posix/wordexp.c @@ -804,10 +804,11 @@ parse_arith (char **word, size_t *word_length, size_t *max_length, #include /* Function called by child process in exec_comm() */ -static pid_t -exec_comm_child (char *comm, int *fildes, bool showerr, bool noexec) +static bool +exec_comm_child (process_create_id_t *procid, char *comm, int *fildes, + bool showerr, bool noexec) { - pid_t pid = -1; + bool r = false; /* Execute the command, or just check syntax? */ const char *args[] = { _PATH_BSHELL, noexec ? "-nc" : "-c", comm, NULL }; @@ -855,17 +856,17 @@ exec_comm_child (char *comm, int *fildes, bool showerr, bool noexec) goto out; } - /* pid is not set if posix_spawn fails, so it keep the original value - of -1. */ - __posix_spawn (&pid, _PATH_BSHELL, &fa, NULL, (char *const *) args, - recreate_env ? strlist_begin (&newenv) : __environ); + r = __spawn_process_create (procid, _PATH_BSHELL, &fa, NULL, + (char *const *) args, + recreate_env + ? strlist_begin (&newenv) : __environ) == 0; strlist_free (&newenv); out: __posix_spawn_file_actions_destroy (&fa); - return pid; + return r; } /* Function to execute a command and retrieve the results */ @@ -882,7 +883,7 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length, int status = 0; size_t maxnewlines = 0; char buffer[bufsize]; - pid_t pid; + process_create_id_t pid; bool noexec = false; /* Do nothing if command substitution should not succeed. */ @@ -897,9 +898,8 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length, return WRDE_NOSPACE; again: - pid = exec_comm_child (comm, fildes, noexec ? false : flags & WRDE_SHOWERR, - noexec); - if (pid < 0) + if (!exec_comm_child (&pid, comm, fildes, + noexec ? false : flags & WRDE_SHOWERR, noexec)) { __close (fildes[0]); __close (fildes[1]); @@ -908,7 +908,7 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length, /* If we are just testing the syntax, only wait. */ if (noexec) - return (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) == pid + return (TEMP_FAILURE_RETRY (__spawn_process_wait (pid, &status, 0)) == pid && status != 0) ? WRDE_SYNTAX : 0; __close (fildes[1]); @@ -925,8 +925,10 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length, /* If read returned 0 then the process has closed its stdout. Don't use WNOHANG in that case to avoid busy looping until the process eventually exits. */ - if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, - buflen == 0 ? 0 : WNOHANG)) + if (TEMP_FAILURE_RETRY (__spawn_process_wait (pid, + &status, + buflen == 0 + ? 0 : WNOHANG)) == 0) continue; if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer, @@ -960,8 +962,9 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length, /* If read returned 0 then the process has closed its stdout. Don't use WNOHANG in that case to avoid busy looping until the process eventually exits. */ - if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, - buflen == 0 ? 0 : WNOHANG)) + if (TEMP_FAILURE_RETRY (__spawn_process_wait (pid, &status, + buflen == 0 + ? 0 : WNOHANG)) == 0) continue; if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer, @@ -1094,7 +1097,7 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length, no_space: __kill (pid, SIGKILL); - TEMP_FAILURE_RETRY (__waitpid (pid, NULL, 0)); + TEMP_FAILURE_RETRY (__spawn_process_wait (pid, NULL, 0)); __close (fildes[0]); return WRDE_NOSPACE; } diff --git a/sysdeps/generic/spawn_process.c b/sysdeps/generic/spawn_process.c new file mode 100644 index 0000000000..5ca8d6260d --- /dev/null +++ b/sysdeps/generic/spawn_process.c @@ -0,0 +1,43 @@ +/* Internal implementation of __spawn_process*. Generic implementation. + Copyright (C) 2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +int +__spawn_process_create (process_create_id_t *procid, + const char *path, + const posix_spawn_file_actions_t *facts, + const posix_spawnattr_t *attr, + char *const argv[], + char *const envp[]) +{ + return __posix_spawn (procid, path, facts, attr, argv, envp); +} + +int +__spawn_process_kill (process_create_id_t procid, int signo) +{ + return __kill (procid, signo); +} + +process_create_id_t +__spawn_process_wait (process_create_id_t procid, int *wstatus, int options) +{ + return __waitpid (procid, wstatus, options | WEXITED); +} diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c index d01ee518ae..73fb3b3ec1 100644 --- a/sysdeps/posix/system.c +++ b/sysdeps/posix/system.c @@ -45,7 +45,7 @@ last thread will restore them. Cancellation handling is done with thread cancellation clean-up handlers - on waitpid call. */ + on __spawn_process_wait call. */ #ifdef _LIBC_REENTRANT static struct sigaction intr, quit; @@ -71,7 +71,7 @@ struct cancel_handler_args { struct sigaction *quit; struct sigaction *intr; - pid_t pid; + process_create_id_t pid; }; static void @@ -79,11 +79,11 @@ cancel_handler (void *arg) { struct cancel_handler_args *args = (struct cancel_handler_args *) (arg); - __kill_noerrno (args->pid, SIGKILL); + __spawn_process_kill (args->pid, SIGKILL); int state; __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state); - TEMP_FAILURE_RETRY (__waitpid (args->pid, NULL, 0)); + TEMP_FAILURE_RETRY (__spawn_process_wait (args->pid, NULL, 0)); __pthread_setcancelstate (state, NULL); DO_LOCK (); @@ -102,7 +102,7 @@ do_system (const char *line) { int status = -1; int ret; - pid_t pid; + process_create_id_t pid; struct sigaction sa; #ifndef _LIBC_REENTRANT struct sigaction intr, quit; @@ -144,12 +144,13 @@ do_system (const char *line) __posix_spawnattr_setflags (&spawn_attr, POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK); - ret = __posix_spawn (&pid, SHELL_PATH, NULL, &spawn_attr, - (char *const[]){ (char *) SHELL_NAME, - (char *) "-c", - (char *) "--", - (char *) line, NULL }, - __environ); + ret = __spawn_process_create (&pid, SHELL_PATH, NULL, &spawn_attr, + (char *const[]){ (char *) SHELL_NAME, + (char *) "-c", + (char *) "--", + (char *) line, + NULL }, + __environ); __posix_spawnattr_destroy (&spawn_attr); if (ret == 0) @@ -169,7 +170,7 @@ do_system (const char *line) /* Note the system() is a cancellation point. But since we call waitpid() which itself is a cancellation point we do not have to do anything here. */ - if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid) + if (TEMP_FAILURE_RETRY (__spawn_process_wait (pid, &status, 0)) != pid) status = -1; #if defined(_LIBC_REENTRANT) && defined(SIGCANCEL) __libc_cleanup_region_end (0); diff --git a/sysdeps/unix/sysv/linux/include/bits/spawn_ext.h b/sysdeps/unix/sysv/linux/include/bits/spawn_ext.h new file mode 100644 index 0000000000..a7da319287 --- /dev/null +++ b/sysdeps/unix/sysv/linux/include/bits/spawn_ext.h @@ -0,0 +1,11 @@ +#ifndef _SPAWN_EXT +# define _SPAWN_EXT + +#include_next + +# ifndef _ISOMAC +__typeof (pidfd_spawn) __pidfd_spawn; +libc_hidden_proto (__pidfd_spawn); +# endif + +#endif diff --git a/sysdeps/unix/sysv/linux/not-errno.h b/sysdeps/unix/sysv/linux/not-errno.h index dc484bd3f5..00992268eb 100644 --- a/sysdeps/unix/sysv/linux/not-errno.h +++ b/sysdeps/unix/sysv/linux/not-errno.h @@ -28,3 +28,14 @@ __kill_noerrno (pid_t pid, int sig) return INTERNAL_SYSCALL_ERRNO (res); return 0; } + +static inline int +__pidfd_send_signal_noerrno (int pidfd, int sig, siginfo_t *info, + unsigned int flags) +{ + int res; + res = INTERNAL_SYSCALL_CALL (pidfd_send_signal, pidfd, sig, info, flags); + if (INTERNAL_SYSCALL_ERROR_P (res)) + return INTERNAL_SYSCALL_ERRNO (res); + return 0; +} diff --git a/sysdeps/unix/sysv/linux/pidfd_spawn.c b/sysdeps/unix/sysv/linux/pidfd_spawn.c index a2e0a70017..fec1f29f89 100644 --- a/sysdeps/unix/sysv/linux/pidfd_spawn.c +++ b/sysdeps/unix/sysv/linux/pidfd_spawn.c @@ -20,11 +20,13 @@ #include "spawn_int.h" int -pidfd_spawn (int *pidfd, const char *path, - const posix_spawn_file_actions_t *file_actions, - const posix_spawnattr_t *attrp, char *const argv[], - char *const envp[]) +__pidfd_spawn (int *pidfd, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[]) { return __spawni (pidfd, path, file_actions, attrp, argv, envp, SPAWN_XFLAGS_RET_PIDFD); } +libc_hidden_def (__pidfd_spawn) +weak_alias (__pidfd_spawn, pidfd_spawn) diff --git a/sysdeps/unix/sysv/linux/spawn_process.c b/sysdeps/unix/sysv/linux/spawn_process.c new file mode 100644 index 0000000000..c525b9f8d7 --- /dev/null +++ b/sysdeps/unix/sysv/linux/spawn_process.c @@ -0,0 +1,121 @@ +/* Internal implementation of __spawn_process*. Linux implementation. + Copyright (C) 2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +#pragma GCC optimize ("O0") + +int +__spawn_process_create (process_create_id_t *procid, + const char *path, + const posix_spawn_file_actions_t *facts, + const posix_spawnattr_t *attr, + char *const argv[], + char *const envp[]) +{ + int r; + + if (__clone_pidfd_supported ()) + { + int pidfd; + r = __pidfd_spawn (&pidfd, path, facts, attr, argv, envp); + if (r == 0) + { + /* Both pidfd and pid_t do not allow negative values to describe a + new process, so use the MSB to set the identifier is for + pidfd. */ + *procid = pidfd | 0x80000000; + return 0; + } + + /* Fallback to posix_spawn if file descriptor limits is reached. */ + if (r != EMFILE && r != ENFILE) + return r; + } + + pid_t pid; + r = __posix_spawn (&pid, path, facts, attr, argv, envp); + if (r != 0) + return r; + *procid = pid; + return 0; +} + +int +__spawn_process_kill (process_create_id_t procid, int signo) +{ + return procid & 0x80000000 + ? __pidfd_send_signal_noerrno (procid & INT_MAX, signo, NULL, 0) + : __kill_noerrno (procid, signo); +} + +process_create_id_t +__spawn_process_wait (process_create_id_t procid, int *wstatus, int options) +{ + bool use_pidfd = procid & 0x80000000; + + siginfo_t info = { 0 }; + int waitid_opts = WEXITED; + if (options & WNOHANG) + waitid_opts |= WNOHANG; + if (options & WUNTRACED) + waitid_opts |= WSTOPPED; + if (options & WCONTINUED) + waitid_opts |= WCONTINUED; + + if (__waitid (use_pidfd ? P_PIDFD : P_PID, + use_pidfd ? procid & INT_MAX : procid, + &info, + waitid_opts) == -1) + return -1; + + /* Handle successful WNOHANG but without a child state change. */ + if (info.si_pid == 0) + return 0; + + if (wstatus != NULL) + { + int status = 0; + switch (info.si_code) + { + case CLD_EXITED: + status = (info.si_status & 0xff) << 8; + break; + case CLD_KILLED: + status = info.si_status & 0x7f; + break; + case CLD_DUMPED: + status = (info.si_status & 0x7f) | __WCOREFLAG; + break; + case CLD_STOPPED: + case CLD_TRAPPED: + status = ((info.si_status & 0xff) << 8) | 0x7f; + break; + case CLD_CONTINUED: + status = 0xffff; + break; + } + *wstatus = status; + } + + /* With P_PIDFD, waitid populates info.si_pid with the actual Process ID of + the child. */ + return use_pidfd ? procid : info.si_pid; +}