* [PATCH 1/5] unshare: add global child_pid variable for signal forwarding
From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw)
To: util-linux; +Cc: Kiran Rangoon
In-Reply-To: <20260108183134.23980-1-kiranrangoon0@gmail.com>
Add a global variable to store the child process PID, which will be
used by the signal handler to forward SIGTERM/SIGINT to the child.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 5370ab981..6df53666a 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -74,6 +74,9 @@ static struct namespace_file {
static int npersists; /* number of persistent namespaces */
+/* Global PID of child process for signal forwarding */
+static volatile pid_t child_pid = 0;
+
enum {
SETGROUPS_NONE = -1,
SETGROUPS_DENY = 0,
--
2.47.3
^ permalink raw reply related
* [PATCH 2/5] unshare: add signal forwarding handler
From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw)
To: util-linux; +Cc: Kiran Rangoon
In-Reply-To: <20260108183134.23980-1-kiranrangoon0@gmail.com>
Add forward_signal() function that forwards SIGTERM/SIGINT from the
parent process to the child process. This will be installed as a
signal handler in the next step.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 6df53666a..3850e5f4a 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -122,6 +122,19 @@ static void setgroups_control(int action)
close(fd);
}
+/**
+ * forward_signal() - Forward signal to child process
+ * @sig: Signal number to forward
+ *
+ * Signal handler that forwards SIGTERM/SIGINT from parent to child.
+ * This allows the child to handle signals properly when using --fork.
+ */
+static void forward_signal(int sig)
+{
+ if (child_pid > 0)
+ kill(child_pid, sig);
+}
+
static void map_id(const char *file, uint32_t from, uint32_t to)
{
char *buf;
--
2.47.3
^ permalink raw reply related
* [PATCH 3/5] unshare: replace signal blocking with signal handlers
From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw)
To: util-linux; +Cc: Kiran Rangoon
In-Reply-To: <20260108183134.23980-1-kiranrangoon0@gmail.com>
Replace sigprocmask(SIG_BLOCK) with sigaction() to install signal
handlers for SIGTERM and SIGINT. This allows the parent to catch
signals and forward them to the child, instead of just blocking them.
The forward_signal() handler installed in the previous commit will
now be called when SIGTERM/SIGINT is received.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 3850e5f4a..9255ff4f8 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -1116,11 +1116,23 @@ int main(int argc, char *argv[])
settime(monotonic, CLOCK_MONOTONIC);
if (forkit) {
+ struct sigaction sa;
+
+ /* Set up signal handler to forward signals to child */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = forward_signal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(SIGTERM, &sa, NULL) == -1)
+ err(EXIT_FAILURE, _("sigaction SIGTERM failed"));
+ if (sigaction(SIGINT, &sa, NULL) == -1)
+ err(EXIT_FAILURE, _("sigaction SIGINT failed"));
+
+ /* Save old mask for child to restore */
if (sigemptyset(&sigset) != 0 ||
- sigaddset(&sigset, SIGINT) != 0 ||
- sigaddset(&sigset, SIGTERM) != 0 ||
- sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0)
- err(EXIT_FAILURE, _("sigprocmask block failed"));
+ sigprocmask(SIG_SETMASK, NULL, &oldsigset) != 0)
+ err(EXIT_FAILURE, _("sigprocmask failed"));
#ifdef HAVE_PIDFD_OPEN
if (kill_child_signo != 0) {
/* make a connection to the original process (parent) */
--
2.47.3
^ permalink raw reply related
* [PATCH 4/5] unshare: store child PID in global variable
From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw)
To: util-linux; +Cc: Kiran Rangoon
In-Reply-To: <20260108183134.23980-1-kiranrangoon0@gmail.com>
After fork(), store the child PID in the global child_pid variable
so the signal handler can forward signals to it. Clear child_pid in
the child process to prevent recursion.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 9255ff4f8..8bc805e05 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -1149,6 +1149,7 @@ int main(int argc, char *argv[])
case -1:
err(EXIT_FAILURE, _("fork failed"));
case 0: /* child */
+ child_pid = 0; /* Clear in child process */
if (sigprocmask(SIG_SETMASK, &oldsigset, NULL))
err(EXIT_FAILURE,
_("sigprocmask restore failed"));
@@ -1156,6 +1157,7 @@ int main(int argc, char *argv[])
close(fd_bind);
break;
default: /* parent */
+ child_pid = pid; /* Store child PID for signal handler */
break;
}
}
--
2.47.3
^ permalink raw reply related
* [PATCH 5/5] unshare: handle EINTR in waitpid loop
From: Kiran Rangoon @ 2026-01-08 18:31 UTC (permalink / raw)
To: util-linux; +Cc: Kiran Rangoon
In-Reply-To: <20260108183134.23980-1-kiranrangoon0@gmail.com>
Modify waitpid() to retry on EINTR. When the signal handler forwards
a signal to the child, waitpid() can be interrupted. We need to retry
in this case instead of failing.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 8bc805e05..2ced8b7a8 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -1172,7 +1172,14 @@ int main(int argc, char *argv[])
}
if (pid) {
- if (waitpid(pid, &status, 0) == -1)
+ int rc;
+
+ /* Wait for child, handling EINTR from signal forwarding */
+ do {
+ rc = waitpid(pid, &status, 0);
+ } while (rc == -1 && errno == EINTR);
+
+ if (rc == -1)
err(EXIT_FAILURE, _("waitpid failed"));
if (WIFEXITED(status))
--
2.47.3
^ permalink raw reply related
* Re: [PATCH 0/5] unshare: fix signal forwarding to child processes
From: Karel Zak @ 2026-01-12 14:05 UTC (permalink / raw)
To: Kiran Rangoon; +Cc: util-linux
In-Reply-To: <20260108183134.23980-1-kiranrangoon0@gmail.com>
On Thu, Jan 08, 2026 at 01:31:29PM -0500, Kiran Rangoon wrote:
> This series fixes a bug where unshare with --fork was not properly
> forwarding SIGTERM and SIGINT to child processes, causing premature
> termination during system shutdown.
Thanks.
Created PR https://github.com/util-linux/util-linux/pull/3961 to test
by CI on github.
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* Re: [PATCH 1/3] losetup: snip --verbose from bash-completion, and 'v' from options string
From: Karel Zak @ 2026-01-12 14:08 UTC (permalink / raw)
To: Benno Schulenberg; +Cc: util-linux
In-Reply-To: <6zqviy7rkkjvfubrunegtw5poeyq4tstg7wltnca36tnul3w2x@hs2oxaatxptl>
On Wed, Dec 17, 2025 at 12:00:48PM +0100, Karel Zak wrote:
> On Tue, Dec 09, 2025 at 04:02:20PM +0100, Benno Schulenberg wrote:
> > bash-completion/losetup | 1 -
> > sys-utils/losetup.c | 2 +-
> > 2 files changed, 1 insertion(+), 2 deletions(-)
>
> Submitted as a pull request to https://github.com/util-linux/util-linux/pull/3910
> for CI testing.
Merged a few days ago. Thanks!
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* [PATCH] mount: (manpage) add CephFS kernel client mount options
From: Viacheslav Dubeyko @ 2026-01-12 20:58 UTC (permalink / raw)
To: util-linux, kzak
Cc: ceph-devel, idryomov, linux-fsdevel, pdonnell, amarkuze,
Slava.Dubeyko, slava, vdubeyko, Pavan.Rallabhandi
From: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Currently, manpage for generic mount tool doesn't contain
explanation of CephFS kernel client mount options. This patch
adds the description of CephFS mount options into
file system specific mount options section.
Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
---
sys-utils/mount.8.adoc | 86 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc
index 4571bd2bfd16..191a3fabf501 100644
--- a/sys-utils/mount.8.adoc
+++ b/sys-utils/mount.8.adoc
@@ -853,6 +853,7 @@ This section lists options that are specific to particular filesystems. Where po
|===
|*Filesystem(s)* |*Manual page*
|btrfs |*btrfs*(5)
+|cephfs |*mount.ceph*(8)
|cifs |*mount.cifs*(8)
|ext2, ext3, ext4 |*ext4*(5)
|fuse |*fuse*(8)
@@ -913,6 +914,91 @@ Give blocksize. Allowed values are 512, 1024, 2048, 4096.
**grpquota**|**noquota**|**quota**|*usrquota*::
These options are accepted but ignored. (However, quota utilities may react to such strings in _/etc/fstab_.)
+=== Mount options for ceph
+
+CephFS is a POSIX-compliant distributed filesystem provided by Ceph. For more information, see the Linux kernel documentation at _Documentation/filesystems/ceph.rst_ or the Ceph documentation at _https://docs.ceph.com/_.
+
+**mon_addr=**__ip_address__[**:**__port__][**/**__ip_address__[**:**__port__]]::
+Monitor address(es) to bootstrap the connection to the Ceph cluster. Multiple monitor addresses can be specified, separated by forward slashes. If a port is not specified, the default port 6789 is used.
+
+**fsid=**__cluster-id__::
+The cluster FSID (unique identifier). This can be obtained via the *ceph fsid* command.
+
+**ip=**__A.B.C.D__[**:**__N__]::
+Specifies the local IP address and optionally the port that the client should bind to.
+
+**conf=**__path__::
+Path to a _ceph.conf_ configuration file. This can be used for auto-discovery of monitor addresses and authentication secrets.
+
+**secret=**__key__::
+The CephX secret key for authentication. This option is insecure because it exposes the secret on the command line. Use *secretfile* instead when possible.
+
+**secretfile=**__path__::
+Path to a file containing the CephX secret key. This is the preferred method for providing authentication credentials.
+
+**fs=**__name__ or **mds_namespace=**__name__::
+Specify a non-default Ceph filesystem to mount. The *mds_namespace* option is the older syntax.
+
+**mount_timeout=**__seconds__::
+Timeout value for mount operations in seconds. Default is 60 seconds.
+
+**wsize=**__bytes__::
+Maximum write size in bytes. Default is 67108864 (64 MB).
+
+**rsize=**__bytes__::
+Maximum read size in bytes. Default is 67108864 (64 MB).
+
+**rasize=**__bytes__::
+Maximum readahead size in bytes. Default is 8388608 (8 MB).
+
+**caps_max=**__number__::
+Maximum number of capabilities (caps) to retain. When this limit is exceeded, unused caps are released. Default is 0 (no limit).
+
+*rbytes*::
+When *stat*(2) is called on a directory, set the *st_size* field to 'rbytes', the summation of file sizes over all files nested beneath that directory. This is the default behavior.
+
+*norbytes*::
+When *stat*(2) is called on a directory, set the *st_size* field to the number of entries in that directory instead of the recursive byte count.
+
+*dcache*::
+Enable directory entry cache (dcache) for negative lookup caching and readdir operations. This is the default behavior.
+
+*nodcache*::
+Disable directory entry cache usage. This disables negative lookup caching and dcache-assisted readdir operations.
+
+*noasyncreaddir*::
+Disable asynchronous readdir operations that use the dcache.
+
+*nocrc*::
+Disable CRC32C calculation for data writes. If set, the storage nodes must rely on TCP's error correction to detect data corruption in the data payload.
+
+**snapdirname=**__name__::
+Sets the name of the hidden snapshots directory. Default is _.snap_.
+
+*dirstat*::
+Enable reading of directory stats via *cat* on the directory.
+
+*nodirstat*::
+Disable reading of directory stats via *cat* on the directory.
+
+*noquotadf*::
+Report overall filesystem usage in statfs instead of the quota for the root directory.
+
+*nocopyfrom*::
+Disable the use of RADOS copy-from operations in *copy_file_range*(2). The RADOS copy-from operation allows the copy to be performed server-side, which can be more efficient.
+
+**recover_session=**{**no**|*clean*}::
+Control the auto-reconnect behavior when the client has been blocklisted. The default is *no*, which prevents reconnection. The *clean* option (available since Linux kernel 5.4) reconnects automatically when blocklisted, but discards any dirty data and invalidates all caches. This can result in data loss.
+
+**ms_mode=**{**legacy**|**crc**|**secure**|**prefer-crc**|*prefer-secure*}::
+Select the connection transport protocol. *legacy* uses the v1 protocol. *crc* uses the v2 protocol without encryption. *secure* uses the v2 protocol with encryption. *prefer-crc* and *prefer-secure* indicate a preference but will fall back if the preferred mode is not available.
+
+*wsync*::
+Execute namespace operations (file/directory creations, deletions, etc.) synchronously.
+
+*nowsync*::
+Execute namespace operations asynchronously. This is the default behavior since Linux kernel 5.7.
+
=== Mount options for debugfs
The debugfs filesystem is a pseudo filesystem, traditionally mounted on _/sys/kernel/debug_. As of kernel version 3.4, debugfs has the following options:
--
2.52.0
^ permalink raw reply related
* [Patch V2 0/4] unshare: fix signal forwarding to child processes
From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <3tysdpabjulwlmr3hkm4pr7romwtf6cofhghth7buyuxnmwr5r@pliehv2xicmy>
Changes in V2 (responding to feedback from karelzak):
- Made signal forwarding opt-in via new --forward-signals flag
- Preserves default behavior (signals blocked) for backward compatibility
- Addresses concerns from PR #1087 about "impatient parent" problem
This adds --forward-signals to enable graceful shutdown of child processes
during system reboot. Since util-linux 2.36, unshare blocks SIGTERM/SIGINT
to prevent premature parent exit, but this prevents children from receiving
shutdown signals for cleanup.
The new flag installs signal handlers instead of blocking, forwards signals
to the child, and properly handles EINTR in waitpid(). Includes tests for
basic forwarding and --kill-child compatibility.
Kiran Rangoon (4):
unshare: add --forward-signals option to argument parser
unshare: implement signal forwarding when --forward-signals is used
unshare: document --forward-signals in man page
tests: add tests for unshare --forward-signals
sys-utils/unshare.1.adoc | 13 ++++
sys-utils/unshare.c | 67 +++++++++++++++++--
tests/expected/unshare/forward-signals | 1 +
.../unshare/forward-signals-kill-child | 1 +
tests/ts/unshare/forward-signals | 55 +++++++++++++++
tests/ts/unshare/forward-signals-kill-child | 56 ++++++++++++++++
6 files changed, 186 insertions(+), 7 deletions(-)
create mode 100644 tests/expected/unshare/forward-signals
create mode 100644 tests/expected/unshare/forward-signals-kill-child
create mode 100755 tests/ts/unshare/forward-signals
create mode 100755 tests/ts/unshare/forward-signals-kill-child
--
2.47.3
^ permalink raw reply
* [V2 1/4] unshare: add --forward-signals option to argument parser
From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260113172942.10678-1-kiranrangoon0@gmail.com>
Add a new --forward-signals command-line option that will allow
unshare to forward SIGTERM and SIGINT signals from the parent
process to the forked child process.
This commit adds the option parsing infrastructure but does not
implement the signal forwarding logic yet. The flag defaults to 0
(disabled) to maintain backward compatibility.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 5370ab981..7e94b9148 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -801,6 +801,7 @@ int main(int argc, char *argv[])
OPT_MAPAUTO,
OPT_MAPSUBIDS,
OPT_OWNER,
+ OPT_FORWARD_SIGNALS,
};
static const struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
@@ -817,6 +818,7 @@ int main(int argc, char *argv[])
{ "fork", no_argument, NULL, 'f' },
{ "kill-child", optional_argument, NULL, OPT_KILLCHILD },
+ { "forward-signals", no_argument, NULL, OPT_FORWARD_SIGNALS },
{ "mount-proc", optional_argument, NULL, OPT_MOUNTPROC },
{ "mount-binfmt", optional_argument, NULL, OPT_MOUNTBINFMT },
{ "map-user", required_argument, NULL, OPT_MAPUSER },
@@ -843,7 +845,7 @@ int main(int argc, char *argv[])
int setgrpcmd = SETGROUPS_NONE;
int unshare_flags = 0;
- int c, forkit = 0;
+ int c, forkit = 0, forward_signals = 0;
uid_t mapuser = -1, owneruser = -1;
gid_t mapgroup = -1, ownergroup = -1;
struct map_range *usermap = NULL;
@@ -1015,6 +1017,10 @@ int main(int argc, char *argv[])
keepcaps = 1;
cap_last_cap(); /* Force last cap to be cached before we fork. */
break;
+ case OPT_FORWARD_SIGNALS:
+ forkit = 1;
+ forward_signals = 1;
+ break;
case 'S':
uid = strtoul_or_err(optarg, _("failed to parse uid"));
force_uid = 1;
--
2.47.3
^ permalink raw reply related
* [V2 2/4] unshare: implement signal forwarding when --forward-signals is used
From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260113172942.10678-1-kiranrangoon0@gmail.com>
When --forward-signals is specified, install signal handlers for
SIGTERM and SIGINT that forward these signals to the child process.
This allows child processes with cleanup handlers to execute gracefully during shutdown scenarios like
system reboot.
Also fix waitpid() to handle EINTR properly when signals are being
forwarded, ensuring the parent waits for the child to complete
signal handling before exiting.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 59 ++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 7e94b9148..efcb57cba 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -74,6 +74,9 @@ static struct namespace_file {
static int npersists; /* number of persistent namespaces */
+/* Global PID of child process for signal forwarding */
+static volatile pid_t child_pid = 0;
+
enum {
SETGROUPS_NONE = -1,
SETGROUPS_DENY = 0,
@@ -135,6 +138,19 @@ static void map_id(const char *file, uint32_t from, uint32_t to)
close(fd);
}
+/**
+ * forward_signal() - Forward signal to child process
+ * @sig: Signal number to forward
+ *
+ * Signal handler that forwards SIGTERM/SIGINT from parent to child.
+ * Only used when --forward-signals flag is enabled.
+ */
+static void forward_signal(int sig)
+{
+ if (child_pid > 0)
+ kill(child_pid, sig);
+}
+
static unsigned long parse_propagation(const char *str)
{
size_t i;
@@ -1106,11 +1122,32 @@ int main(int argc, char *argv[])
settime(monotonic, CLOCK_MONOTONIC);
if (forkit) {
- if (sigemptyset(&sigset) != 0 ||
- sigaddset(&sigset, SIGINT) != 0 ||
- sigaddset(&sigset, SIGTERM) != 0 ||
- sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0)
- err(EXIT_FAILURE, _("sigprocmask block failed"));
+ if (forward_signals) {
+ /* Install signal handlers to forward signals to child */
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = forward_signal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(SIGTERM, &sa, NULL) == -1)
+ err(EXIT_FAILURE, _("sigaction SIGTERM failed"));
+ if (sigaction(SIGINT, &sa, NULL) == -1)
+ err(EXIT_FAILURE, _("sigaction SIGINT failed"));
+
+ /* Save old mask for child to restore */
+ if (sigemptyset(&sigset) != 0 ||
+ sigprocmask(SIG_SETMASK, NULL, &oldsigset) != 0)
+ err(EXIT_FAILURE, _("sigprocmask failed"));
+ } else {
+ /* Block signals to prevent "impatient parent" problem */
+ if (sigemptyset(&sigset) != 0 ||
+ sigaddset(&sigset, SIGINT) != 0 ||
+ sigaddset(&sigset, SIGTERM) != 0 ||
+ sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0)
+ err(EXIT_FAILURE, _("sigprocmask block failed"));
+ }
#ifdef HAVE_PIDFD_OPEN
if (kill_child_signo != 0) {
/* make a connection to the original process (parent) */
@@ -1127,6 +1164,7 @@ int main(int argc, char *argv[])
case -1:
err(EXIT_FAILURE, _("fork failed"));
case 0: /* child */
+ child_pid = 0; /* Clear in child process */
if (sigprocmask(SIG_SETMASK, &oldsigset, NULL))
err(EXIT_FAILURE,
_("sigprocmask restore failed"));
@@ -1134,6 +1172,8 @@ int main(int argc, char *argv[])
close(fd_bind);
break;
default: /* parent */
+ if (forward_signals)
+ child_pid = pid; /* Store for signal handler */
break;
}
}
@@ -1148,7 +1188,14 @@ int main(int argc, char *argv[])
}
if (pid) {
- if (waitpid(pid, &status, 0) == -1)
+ int rc;
+
+ /* Wait for child, handling EINTR from signal forwarding */
+ do {
+ rc = waitpid(pid, &status, 0);
+ } while (rc == -1 && errno == EINTR);
+
+ if (rc == -1)
err(EXIT_FAILURE, _("waitpid failed"));
if (WIFEXITED(status))
--
2.47.3
^ permalink raw reply related
* [V2 3/4] unshare: document --forward-signals in man page
From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260113172942.10678-1-kiranrangoon0@gmail.com>
Add documentation for the new --forward-signals option, explaining
its use case (graceful shutdown during reboot/system management)
and interaction with the --fork option.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.1.adoc | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index 85d00af3c..fa0b61df1 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -81,6 +81,19 @@ Create a new time namespace. If _file_ is specified, then the namespace is made
*-f*, *--fork*::
Fork the specified _program_ as a child process of *unshare* rather than running it directly. This is useful when creating a new PID namespace. Note that when *unshare* is waiting for the child process, then it ignores *SIGINT* and *SIGTERM* and does not forward any signals to the child. It is necessary to send signals to the child process.
+*--forward-signals*::
+Forward *SIGTERM* and *SIGINT* signals received by the parent *unshare* process to the child process.
+When this option is not specified, *unshare* ignores these signals while waiting for the child process
+to exit (the default behavior since util-linux 2.36). This allows the parent to remain alive while
+the child process handles the signals.
++
+This option is useful when the parent *unshare* process will receive *SIGTERM* or *SIGINT* signals
+(for example, during system reboot or from a process manager), and you want the child process to
+be notified of graceful shutdown requests so it can perform cleanup operations. If your child
+process has signal handlers (such as shell trap handlers), enabling this option allows them to execute.
++
+This option implies *--fork*.
+
*--keep-caps*::
When the *--user* option is given, ensure that capabilities granted in the user namespace are preserved in the child process.
--
2.47.3
^ permalink raw reply related
* [V2 4/4] tests: add tests for unshare --forward-signals
From: Kiran Rangoon @ 2026-01-13 17:29 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260113172942.10678-1-kiranrangoon0@gmail.com>
Add two test cases for the new --forward-signals option:
- forward-signals: verifies SIGTERM is forwarded to child
- forward-signals-kill-child: verifies compatibility with --kill-child
Both tests use test_sigreceive which exits with the signal number
received, confirming proper signal forwarding.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
tests/expected/unshare/forward-signals | 1 +
.../unshare/forward-signals-kill-child | 1 +
tests/ts/unshare/forward-signals | 55 ++++++++++++++++++
tests/ts/unshare/forward-signals-kill-child | 56 +++++++++++++++++++
4 files changed, 113 insertions(+)
create mode 100644 tests/expected/unshare/forward-signals
create mode 100644 tests/expected/unshare/forward-signals-kill-child
create mode 100755 tests/ts/unshare/forward-signals
create mode 100755 tests/ts/unshare/forward-signals-kill-child
diff --git a/tests/expected/unshare/forward-signals b/tests/expected/unshare/forward-signals
new file mode 100644
index 000000000..868dab6e1
--- /dev/null
+++ b/tests/expected/unshare/forward-signals
@@ -0,0 +1 @@
+SIGTERM forwarded successfully
diff --git a/tests/expected/unshare/forward-signals-kill-child b/tests/expected/unshare/forward-signals-kill-child
new file mode 100644
index 000000000..d4e1224da
--- /dev/null
+++ b/tests/expected/unshare/forward-signals-kill-child
@@ -0,0 +1 @@
+SIGTERM forwarded successfully with kill-child
diff --git a/tests/ts/unshare/forward-signals b/tests/ts/unshare/forward-signals
new file mode 100755
index 000000000..24aea8563
--- /dev/null
+++ b/tests/ts/unshare/forward-signals
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2026 util-linux contributors
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file 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 General Public License for more details.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="forward signals to child"
+
+. "$TS_TOPDIR/functions.sh"
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_UNSHARE"
+ts_check_test_command "$TS_HELPER_SIGRECEIVE"
+
+ts_skip_nonroot
+
+# Start unshare with --forward-signals and a child process that exits
+# with the signal number it receives
+"$TS_CMD_UNSHARE" --user --map-root-user \
+ --pid --mount-proc \
+ --fork --forward-signals \
+ "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG &
+
+UNSHARE_PID=$!
+
+# Give child time to set up signal handlers
+sleep 2
+
+# Send SIGTERM to parent unshare process
+kill -TERM $UNSHARE_PID
+
+# Wait for completion
+wait $UNSHARE_PID
+EXIT_CODE=$?
+
+# test_sigreceive exits with the signal number it receives (15 = SIGTERM)
+if [ $EXIT_CODE -eq 15 ]; then
+ echo "SIGTERM forwarded successfully" >> $TS_OUTPUT
+else
+ echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT
+fi
+
+ts_finalize
diff --git a/tests/ts/unshare/forward-signals-kill-child b/tests/ts/unshare/forward-signals-kill-child
new file mode 100755
index 000000000..3065f1052
--- /dev/null
+++ b/tests/ts/unshare/forward-signals-kill-child
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2026 util-linux contributors
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file 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 General Public License for more details.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="forward signals with kill-child"
+
+. "$TS_TOPDIR/functions.sh"
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_UNSHARE"
+ts_check_test_command "$TS_HELPER_SIGRECEIVE"
+
+ts_skip_nonroot
+
+# Start unshare with both --forward-signals and --kill-child
+# The child should receive SIGTERM from forwarding first
+"$TS_CMD_UNSHARE" --user --map-root-user \
+ --pid --mount-proc \
+ --fork --forward-signals --kill-child \
+ "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG &
+
+UNSHARE_PID=$!
+
+# Give child time to set up signal handlers
+sleep 2
+
+# Send SIGTERM to parent unshare process
+kill -TERM $UNSHARE_PID
+
+# Wait for completion
+wait $UNSHARE_PID
+EXIT_CODE=$?
+
+# test_sigreceive exits with the signal number it receives (15 = SIGTERM)
+# With --kill-child, it should still receive SIGTERM first via forwarding
+if [ $EXIT_CODE -eq 15 ]; then
+ echo "SIGTERM forwarded successfully with kill-child" >> $TS_OUTPUT
+else
+ echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT
+fi
+
+ts_finalize
--
2.47.3
^ permalink raw reply related
* Re: [PATCH] mount: (manpage) add CephFS kernel client mount options
From: Karel Zak @ 2026-01-13 19:14 UTC (permalink / raw)
To: Viacheslav Dubeyko
Cc: util-linux, ceph-devel, idryomov, linux-fsdevel, pdonnell,
amarkuze, Slava.Dubeyko, vdubeyko, Pavan.Rallabhandi
In-Reply-To: <20260112205837.975869-2-slava@dubeyko.com>
On Mon, Jan 12, 2026 at 12:58:38PM -0800, Viacheslav Dubeyko wrote:
> From: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
>
> Currently, manpage for generic mount tool doesn't contain
> explanation of CephFS kernel client mount options. This patch
> adds the description of CephFS mount options into
> file system specific mount options section.
>
> Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
> ---
> sys-utils/mount.8.adoc | 86 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 86 insertions(+)
>
> diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc
> index 4571bd2bfd16..191a3fabf501 100644
> --- a/sys-utils/mount.8.adoc
> +++ b/sys-utils/mount.8.adoc
> @@ -853,6 +853,7 @@ This section lists options that are specific to particular filesystems. Where po
> |===
> |*Filesystem(s)* |*Manual page*
> |btrfs |*btrfs*(5)
> +|cephfs |*mount.ceph*(8)
It seems that all we need is this change.
> |cifs |*mount.cifs*(8)
> |ext2, ext3, ext4 |*ext4*(5)
> |fuse |*fuse*(8)
> @@ -913,6 +914,91 @@ Give blocksize. Allowed values are 512, 1024, 2048, 4096.
> **grpquota**|**noquota**|**quota**|*usrquota*::
> These options are accepted but ignored. (However, quota utilities may react to such strings in _/etc/fstab_.)
>
> +=== Mount options for ceph
If mount.ceph(8) exists, we do not need to repeat the mount option in
mount(8). The ideal solution is to keep filesystem-specific mount
options in the filesystem-specific man page maintained by the
filesystem developer :-) See btrfs, extN, XFS, etc.
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* RE: [PATCH] mount: (manpage) add CephFS kernel client mount options
From: Viacheslav Dubeyko @ 2026-01-13 19:29 UTC (permalink / raw)
To: Karel Zak, slava@dubeyko.com
Cc: Alex Markuze, util-linux@vger.kernel.org,
ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
idryomov@gmail.com, Pavan Rallabhandi, Patrick Donnelly
In-Reply-To: <binwryzqlbprj2t3ybxb5kychdeenhtmadbe23hov44urszvn5@kpbbv3qks47c>
On Tue, 2026-01-13 at 20:14 +0100, Karel Zak wrote:
> On Mon, Jan 12, 2026 at 12:58:38PM -0800, Viacheslav Dubeyko wrote:
> > From: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
> >
> > Currently, manpage for generic mount tool doesn't contain
> > explanation of CephFS kernel client mount options. This patch
> > adds the description of CephFS mount options into
> > file system specific mount options section.
> >
> > Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
> > ---
> > sys-utils/mount.8.adoc | 86 ++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 86 insertions(+)
> >
> > diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc
> > index 4571bd2bfd16..191a3fabf501 100644
> > --- a/sys-utils/mount.8.adoc
> > +++ b/sys-utils/mount.8.adoc
> > @@ -853,6 +853,7 @@ This section lists options that are specific to particular filesystems. Where po
> > |===
> > |*Filesystem(s)* |*Manual page*
> > |btrfs |*btrfs*(5)
> > +|cephfs |*mount.ceph*(8)
>
> It seems that all we need is this change.
>
> > |cifs |*mount.cifs*(8)
> > |ext2, ext3, ext4 |*ext4*(5)
> > |fuse |*fuse*(8)
> > @@ -913,6 +914,91 @@ Give blocksize. Allowed values are 512, 1024, 2048, 4096.
> > **grpquota**|**noquota**|**quota**|*usrquota*::
> > These options are accepted but ignored. (However, quota utilities may react to such strings in _/etc/fstab_.)
> >
> > +=== Mount options for ceph
>
> If mount.ceph(8) exists, we do not need to repeat the mount option in
> mount(8). The ideal solution is to keep filesystem-specific mount
> options in the filesystem-specific man page maintained by the
> filesystem developer :-) See btrfs, extN, XFS, etc.
>
My assumption was that if other file systems have description of specific mount
options in this man page, then CephFS should have too. :) But if it is not right
strategy, then you are correct here.
Let me shrink my patch then.
Thanks,
Slava.
^ permalink raw reply
* [PATCH v2] mount: (manpage) add CephFS filesystem-specific manual page
From: Viacheslav Dubeyko @ 2026-01-13 20:16 UTC (permalink / raw)
To: util-linux, kzak
Cc: ceph-devel, idryomov, linux-fsdevel, pdonnell, amarkuze,
Slava.Dubeyko, slava, vdubeyko, Pavan.Rallabhandi
From: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Currently, manpage for generic mount tool doesn't contain
mentioning of CephFS kernel client filesystem-specific
manual page. This patch adds the mount.ceph(8) mentioning into
file system specific mount options section.
Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
---
sys-utils/mount.8.adoc | 1 +
1 file changed, 1 insertion(+)
diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc
index 4571bd2bfd16..43d2ef9a58a4 100644
--- a/sys-utils/mount.8.adoc
+++ b/sys-utils/mount.8.adoc
@@ -853,6 +853,7 @@ This section lists options that are specific to particular filesystems. Where po
|===
|*Filesystem(s)* |*Manual page*
|btrfs |*btrfs*(5)
+|cephfs |*mount.ceph*(8)
|cifs |*mount.cifs*(8)
|ext2, ext3, ext4 |*ext4*(5)
|fuse |*fuse*(8)
--
2.52.0
^ permalink raw reply related
* Re: [PATCH] mount: (manpage) add CephFS kernel client mount options
From: Karel Zak @ 2026-01-14 9:29 UTC (permalink / raw)
To: Viacheslav Dubeyko
Cc: slava@dubeyko.com, Alex Markuze, util-linux@vger.kernel.org,
ceph-devel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
idryomov@gmail.com, Pavan Rallabhandi, Patrick Donnelly
In-Reply-To: <c15ce83bf9ee6c5c37db193c33a77b52f0594564.camel@ibm.com>
On Tue, Jan 13, 2026 at 07:29:00PM +0000, Viacheslav Dubeyko wrote:
> My assumption was that if other file systems have description of specific mount
> options in this man page, then CephFS should have too. :)
Unfortunately, some filesystems lack specific man pages, so mount.8
serves as a fallback solution for them. In an ideal world, mount.8
would not include filesystem-specific mount options.
I have added a link to mount.ceph in mount.8
https://github.com/util-linux/util-linux/commit/de7973f9906da09b86f221b030ec836c1e91fd22
Thank you for the suggestion!
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* Re: [PATCH v2 0/3] blkpr: add read-keys and read-reservations commands
From: Karel Zak @ 2026-01-14 9:30 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: util-linux, kwolf, hare, pizhenwei
In-Reply-To: <20251217182607.179232-1-stefanha@redhat.com>
On Wed, Dec 17, 2025 at 01:26:04PM -0500, Stefan Hajnoczi wrote:
> sys-utils/blkpr.c | 106 ++++++++++++++++++++++++++++++++++++++---
> sys-utils/blkpr.8.adoc | 3 +-
> 2 files changed, 101 insertions(+), 8 deletions(-)
Applied, thanks.
https://github.com/util-linux/util-linux/pull/3965
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* [PATCH V3 0/4] unshare: add --forward-signals option
From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260113172942.10678-5-kiranrangoon0@gmail.com>
This series adds a new --forward-signals option to unshare that forwards
SIGTERM and SIGINT from the parent process to the child. This allows child
processes to handle graceful shutdown during system reboot or service
manager termination.
Changes in V3:
- Add bash completion for --forward-signals
- Add usage text in help output
- Fix code indentation to match project style
- Improve test synchronization (replace fixed sleep with adaptive polling)
- Add namespace capability checks in tests to prevent CI failures
Kiran Rangoon (4):
unshare: add --forward-signals option to argument parser
unshare: implement signal forwarding when --forward-signals is used
unshare: document --forward-signals in man page
tests: add tests for unshare --forward-signals
bash-completion/unshare | 1 +
sys-utils/unshare.1.adoc | 13 ++++
sys-utils/unshare.c | 64 ++++++++++++++--
tests/expected/unshare/forward-signals | 1 +
.../unshare/forward-signals-kill-child | 1 +
tests/ts/unshare/forward-signals | 73 ++++++++++++++++++
tests/ts/unshare/forward-signals-kill-child | 74 +++++++++++++++++++
7 files changed, 222 insertions(+), 5 deletions(-)
create mode 100644 tests/expected/unshare/forward-signals
create mode 100644 tests/expected/unshare/forward-signals-kill-child
create mode 100755 tests/ts/unshare/forward-signals
create mode 100755 tests/ts/unshare/forward-signals-kill-child
--
2.47.3
^ permalink raw reply
* [Patch V3 1/4] unshare: add --forward-signals option to argument parser
From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260116170648.26439-1-kiranrangoon0@gmail.com>
Add a new --forward-signals command-line option that will allow
unshare to forward SIGTERM and SIGINT signals from the parent
process to the forked child process.
This commit adds the option parsing infrastructure but does not
implement the signal forwarding logic yet. The flag defaults to 0
(disabled) to maintain backward compatibility.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
bash-completion/unshare | 1 +
sys-utils/unshare.c | 9 ++++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/bash-completion/unshare b/bash-completion/unshare
index 19eeb8f08..fdd40e5cb 100644
--- a/bash-completion/unshare
+++ b/bash-completion/unshare
@@ -28,6 +28,7 @@ _unshare_module()
--cgroup
--time
--fork
+ --forward-signals
--kill-child
--keep-caps
--mount-proc
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 5370ab981..8a7a26df2 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -769,6 +769,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -f, --fork fork before launching <program>\n"), out);
fputs(_(" --kill-child[=<signame>] when dying, kill the forked child (implies --fork)\n"
" defaults to SIGKILL\n"), out);
+ fputs(_(" --forward-signals forward SIGTERM and SIGINT to child (implies --fork)\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_(" --setgroups allow|deny control the setgroups syscall in user namespaces\n"), out);
fputs(_(" --keep-caps retain capabilities granted in user namespaces\n"), out);
@@ -801,6 +802,7 @@ int main(int argc, char *argv[])
OPT_MAPAUTO,
OPT_MAPSUBIDS,
OPT_OWNER,
+ OPT_FORWARD_SIGNALS,
};
static const struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
@@ -817,6 +819,7 @@ int main(int argc, char *argv[])
{ "fork", no_argument, NULL, 'f' },
{ "kill-child", optional_argument, NULL, OPT_KILLCHILD },
+ { "forward-signals", no_argument, NULL, OPT_FORWARD_SIGNALS },
{ "mount-proc", optional_argument, NULL, OPT_MOUNTPROC },
{ "mount-binfmt", optional_argument, NULL, OPT_MOUNTBINFMT },
{ "map-user", required_argument, NULL, OPT_MAPUSER },
@@ -843,7 +846,7 @@ int main(int argc, char *argv[])
int setgrpcmd = SETGROUPS_NONE;
int unshare_flags = 0;
- int c, forkit = 0;
+ int c, forkit = 0, forward_signals = 0;
uid_t mapuser = -1, owneruser = -1;
gid_t mapgroup = -1, ownergroup = -1;
struct map_range *usermap = NULL;
@@ -1015,6 +1018,10 @@ int main(int argc, char *argv[])
keepcaps = 1;
cap_last_cap(); /* Force last cap to be cached before we fork. */
break;
+ case OPT_FORWARD_SIGNALS:
+ forkit = 1;
+ forward_signals = 1;
+ break;
case 'S':
uid = strtoul_or_err(optarg, _("failed to parse uid"));
force_uid = 1;
--
2.47.3
^ permalink raw reply related
* [Patch V3 2/4] unshare: implement signal forwarding when --forward-signals is used
From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260116170648.26439-1-kiranrangoon0@gmail.com>
When --forward-signals is specified, install signal handlers for
SIGTERM and SIGINT that forward these signals to the child process.
This allows child processes with cleanup handlers to execute gracefully during shutdown scenarios like
system reboot.
Also fix waitpid() to handle EINTR properly when signals are being
forwarded, ensuring the parent waits for the child to complete
signal handling before exiting.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.c | 55 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 51 insertions(+), 4 deletions(-)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 8a7a26df2..a4d28892d 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -74,6 +74,9 @@ static struct namespace_file {
static int npersists; /* number of persistent namespaces */
+/* Global PID of child process for signal forwarding */
+static volatile pid_t child_pid = 0;
+
enum {
SETGROUPS_NONE = -1,
SETGROUPS_DENY = 0,
@@ -135,6 +138,19 @@ static void map_id(const char *file, uint32_t from, uint32_t to)
close(fd);
}
+/**
+ * forward_signal() - Forward signal to child process
+ * @sig: Signal number to forward
+ *
+ * Signal handler that forwards SIGTERM/SIGINT from parent to child.
+ * Only used when --forward-signals flag is enabled.
+ */
+static void forward_signal(int sig)
+{
+ if (child_pid > 0)
+ kill(child_pid, sig);
+}
+
static unsigned long parse_propagation(const char *str)
{
size_t i;
@@ -1107,11 +1123,32 @@ int main(int argc, char *argv[])
settime(monotonic, CLOCK_MONOTONIC);
if (forkit) {
+ if (forward_signals) {
+ /* Install signal handlers to forward signals to child */
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = forward_signal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(SIGTERM, &sa, NULL) == -1)
+ err(EXIT_FAILURE, _("sigaction SIGTERM failed"));
+ if (sigaction(SIGINT, &sa, NULL) == -1)
+ err(EXIT_FAILURE, _("sigaction SIGINT failed"));
+
+ /* Save old mask for child to restore */
+ if (sigemptyset(&sigset) != 0 ||
+ sigprocmask(SIG_SETMASK, NULL, &oldsigset) != 0)
+ err(EXIT_FAILURE, _("sigprocmask failed"));
+ } else {
+ /* Block signals to prevent "impatient parent" problem */
if (sigemptyset(&sigset) != 0 ||
- sigaddset(&sigset, SIGINT) != 0 ||
- sigaddset(&sigset, SIGTERM) != 0 ||
- sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0)
+ sigaddset(&sigset, SIGINT) != 0 ||
+ sigaddset(&sigset, SIGTERM) != 0 ||
+ sigprocmask(SIG_BLOCK, &sigset, &oldsigset) != 0)
err(EXIT_FAILURE, _("sigprocmask block failed"));
+ }
#ifdef HAVE_PIDFD_OPEN
if (kill_child_signo != 0) {
/* make a connection to the original process (parent) */
@@ -1128,6 +1165,7 @@ int main(int argc, char *argv[])
case -1:
err(EXIT_FAILURE, _("fork failed"));
case 0: /* child */
+ child_pid = 0; /* Clear in child process */
if (sigprocmask(SIG_SETMASK, &oldsigset, NULL))
err(EXIT_FAILURE,
_("sigprocmask restore failed"));
@@ -1135,6 +1173,8 @@ int main(int argc, char *argv[])
close(fd_bind);
break;
default: /* parent */
+ if (forward_signals)
+ child_pid = pid; /* Store for signal handler */
break;
}
}
@@ -1149,7 +1189,14 @@ int main(int argc, char *argv[])
}
if (pid) {
- if (waitpid(pid, &status, 0) == -1)
+ int rc;
+
+ /* Wait for child, handling EINTR from signal forwarding */
+ do {
+ rc = waitpid(pid, &status, 0);
+ } while (rc == -1 && errno == EINTR);
+
+ if (rc == -1)
err(EXIT_FAILURE, _("waitpid failed"));
if (WIFEXITED(status))
--
2.47.3
^ permalink raw reply related
* [Patch V3 3/4] unshare: document --forward-signals in man page
From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260116170648.26439-1-kiranrangoon0@gmail.com>
Add documentation for the new --forward-signals option, explaining
its use case (graceful shutdown during reboot/system management)
and interaction with the --fork option.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
sys-utils/unshare.1.adoc | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index 1d1fd68f8..888928147 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -81,6 +81,19 @@ Create a new time namespace. If _file_ is specified, then the namespace is made
*-f*, *--fork*::
Fork the specified _program_ as a child process of *unshare* rather than running it directly. This is useful when creating a new PID namespace. Note that when *unshare* is waiting for the child process, then it ignores *SIGINT* and *SIGTERM* and does not forward any signals to the child. It is necessary to send signals to the child process.
+*--forward-signals*::
+Forward *SIGTERM* and *SIGINT* signals received by the parent *unshare* process to the child process.
+When this option is not specified, *unshare* ignores these signals while waiting for the child process
+to exit (the default behavior since util-linux 2.36). This allows the parent to remain alive while
+the child process handles the signals.
++
+This option is useful when the parent *unshare* process will receive *SIGTERM* or *SIGINT* signals
+(for example, during system reboot or from a process manager), and you want the child process to
+be notified of graceful shutdown requests so it can perform cleanup operations. If your child
+process has signal handlers (such as shell trap handlers), enabling this option allows them to execute.
++
+This option implies *--fork*.
+
*--keep-caps*::
When the *--user* option is given, ensure that capabilities granted in the user namespace are preserved in the child process.
--
2.47.3
^ permalink raw reply related
* [Patch V3 4/4] tests: add tests for unshare --forward-signals
From: Kiran Rangoon @ 2026-01-16 17:06 UTC (permalink / raw)
To: util-linux; +Cc: kzak, Kiran Rangoon
In-Reply-To: <20260116170648.26439-1-kiranrangoon0@gmail.com>
Add two test cases for the new --forward-signals option:
- forward-signals: verifies SIGTERM is forwarded to child
- forward-signals-kill-child: verifies compatibility with --kill-child
Both tests use test_sigreceive which exits with the signal number
received, confirming proper signal forwarding.
Signed-off-by: Kiran Rangoon <kiranrangoon0@gmail.com>
---
tests/expected/unshare/forward-signals | 1 +
.../unshare/forward-signals-kill-child | 1 +
tests/ts/unshare/forward-signals | 73 ++++++++++++++++++
tests/ts/unshare/forward-signals-kill-child | 74 +++++++++++++++++++
4 files changed, 149 insertions(+)
create mode 100644 tests/expected/unshare/forward-signals
create mode 100644 tests/expected/unshare/forward-signals-kill-child
create mode 100755 tests/ts/unshare/forward-signals
create mode 100755 tests/ts/unshare/forward-signals-kill-child
diff --git a/tests/expected/unshare/forward-signals b/tests/expected/unshare/forward-signals
new file mode 100644
index 000000000..868dab6e1
--- /dev/null
+++ b/tests/expected/unshare/forward-signals
@@ -0,0 +1 @@
+SIGTERM forwarded successfully
diff --git a/tests/expected/unshare/forward-signals-kill-child b/tests/expected/unshare/forward-signals-kill-child
new file mode 100644
index 000000000..d4e1224da
--- /dev/null
+++ b/tests/expected/unshare/forward-signals-kill-child
@@ -0,0 +1 @@
+SIGTERM forwarded successfully with kill-child
diff --git a/tests/ts/unshare/forward-signals b/tests/ts/unshare/forward-signals
new file mode 100755
index 000000000..042ee0a58
--- /dev/null
+++ b/tests/ts/unshare/forward-signals
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2026 util-linux contributors
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file 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 General Public License for more details.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="forward signals to child"
+
+. "$TS_TOPDIR/functions.sh"
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_UNSHARE"
+ts_check_test_command "$TS_HELPER_SIGRECEIVE"
+
+ts_skip_nonroot
+
+# Verify that user namespaces work
+"$TS_CMD_UNSHARE" --user --map-root-user /bin/true &> /dev/null || \
+ ts_skip "user namespaces not supported"
+
+# Start unshare with --forward-signals and a child process that exits
+# with the signal number it receives
+"$TS_CMD_UNSHARE" --user --map-root-user \
+ --pid --mount-proc \
+ --fork --forward-signals \
+ "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG &
+
+UNSHARE_PID=$!
+
+# Wait for child to be ready - poll for up to 10 seconds
+# The child process tree should exist
+TIMEOUT=10
+ELAPSED=0
+while [ $ELAPSED -lt $TIMEOUT ]; do
+ # Check if the process still exists
+ if ! kill -0 $UNSHARE_PID 2>/dev/null; then
+ ts_skip "unshare process died prematurely"
+ fi
+ # Give it a bit more time to set up namespaces and signal handlers
+ if [ $ELAPSED -ge 2 ]; then
+ break
+ fi
+ sleep 0.5
+ ELAPSED=$((ELAPSED + 1))
+done
+
+# Send SIGTERM to parent unshare process
+kill -TERM $UNSHARE_PID 2>/dev/null
+
+# Wait for completion with timeout
+wait $UNSHARE_PID
+EXIT_CODE=$?
+
+# test_sigreceive exits with the signal number it receives (15 = SIGTERM)
+if [ $EXIT_CODE -eq 15 ]; then
+ echo "SIGTERM forwarded successfully" >> $TS_OUTPUT
+else
+ echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT
+fi
+
+ts_finalize
diff --git a/tests/ts/unshare/forward-signals-kill-child b/tests/ts/unshare/forward-signals-kill-child
new file mode 100755
index 000000000..766f9fe56
--- /dev/null
+++ b/tests/ts/unshare/forward-signals-kill-child
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2026 util-linux contributors
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file 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 General Public License for more details.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="forward signals with kill-child"
+
+. "$TS_TOPDIR/functions.sh"
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_UNSHARE"
+ts_check_test_command "$TS_HELPER_SIGRECEIVE"
+
+ts_skip_nonroot
+
+# Verify that user namespaces work
+"$TS_CMD_UNSHARE" --user --map-root-user /bin/true &> /dev/null || \
+ ts_skip "user namespaces not supported"
+
+# Start unshare with both --forward-signals and --kill-child
+# The child should receive SIGTERM from forwarding first
+"$TS_CMD_UNSHARE" --user --map-root-user \
+ --pid --mount-proc \
+ --fork --forward-signals --kill-child \
+ "$TS_HELPER_SIGRECEIVE" < /dev/null >> $TS_OUTPUT 2>> $TS_ERRLOG &
+
+UNSHARE_PID=$!
+
+# Wait for child to be ready - poll for up to 10 seconds
+# The child process tree should exist
+TIMEOUT=10
+ELAPSED=0
+while [ $ELAPSED -lt $TIMEOUT ]; do
+ # Check if the process still exists
+ if ! kill -0 $UNSHARE_PID 2>/dev/null; then
+ ts_skip "unshare process died prematurely"
+ fi
+ # Give it a bit more time to set up namespaces and signal handlers
+ if [ $ELAPSED -ge 2 ]; then
+ break
+ fi
+ sleep 0.5
+ ELAPSED=$((ELAPSED + 1))
+done
+
+# Send SIGTERM to parent unshare process
+kill -TERM $UNSHARE_PID 2>/dev/null
+
+# Wait for completion with timeout
+wait $UNSHARE_PID
+EXIT_CODE=$?
+
+# test_sigreceive exits with the signal number it receives (15 = SIGTERM)
+# With --kill-child, it should still receive SIGTERM first via forwarding
+if [ $EXIT_CODE -eq 15 ]; then
+ echo "SIGTERM forwarded successfully with kill-child" >> $TS_OUTPUT
+else
+ echo "UNEXPECTED EXIT CODE: $EXIT_CODE" >> $TS_OUTPUT
+fi
+
+ts_finalize
--
2.47.3
^ permalink raw reply related
* Re: [PATCH V3 0/4] unshare: add --forward-signals option
From: Karel Zak @ 2026-01-21 8:37 UTC (permalink / raw)
To: Kiran Rangoon; +Cc: util-linux
In-Reply-To: <20260116170648.26439-1-kiranrangoon0@gmail.com>
On Fri, Jan 16, 2026 at 12:06:44PM -0500, Kiran Rangoon wrote:
> bash-completion/unshare | 1 +
> sys-utils/unshare.1.adoc | 13 ++++
> sys-utils/unshare.c | 64 ++++++++++++++--
> tests/expected/unshare/forward-signals | 1 +
> .../unshare/forward-signals-kill-child | 1 +
> tests/ts/unshare/forward-signals | 73 ++++++++++++++++++
> tests/ts/unshare/forward-signals-kill-child | 74 +++++++++++++++++++
> 7 files changed, 222 insertions(+), 5 deletions(-)
> create mode 100644 tests/expected/unshare/forward-signals
> create mode 100644 tests/expected/unshare/forward-signals-kill-child
> create mode 100755 tests/ts/unshare/forward-signals
> create mode 100755 tests/ts/unshare/forward-signals-kill-child
Applied, thanks.
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* Security issue in util-linux: out-of-bounds read in ul_parse_size()
From: Yashashree Gund @ 2026-02-01 14:47 UTC (permalink / raw)
To: kzak@redhat.com, util-linux@vger.kernel.org
[-- Attachment #1.1: Type: text/plain, Size: 2060 bytes --]
Hello util-linux maintainers,
I would like to report a security issue in util-linux involving a memory safety flaw in ul_parse_size() that is reachable via libmount during mount option processing.
The issue is an out-of-bounds read caused by unchecked lookahead accesses when parsing malformed decimal size strings.
The crash is reliably triggered by inputs such as sizelimit=00000.000, which cause ul_parse_size() to read past the end of the input buffer.
This occurs even when the input string is properly NUL-terminated. The problem was initially discovered via libFuzzer while fuzzing libmount and was later confirmed with a minimal standalone reproducer that directly calls ul_parse_size(), demonstrating that the issue is intrinsic to the function and not dependent on higher-level parsing behavior.
Impact:
This bug can lead to process crashes and out-of-bounds memory reads in privileged contexts where util-linux or libmount parse attacker-influenced mount options (e.g., mount helpers, containers, or namespace-aware tools). The issue is therefore security-relevant.
Attachments included:
* A minimized reproducer input that triggers the crash
* The full AddressSanitizer crash report
* Execution-path notes describing how the input reaches ul_parse_size()
* A minimal standalone reproducer (verify.c)
Standalone reproducer details:
The attached verify-repro.c is a small test program that directly invokes ul_parse_size() with a crafted decimal size string and checks the result.
When built with AddressSanitizer enabled and linked against the util-linux source tree, running the program reliably triggers an out-of-bounds read in ul_parse_size(), confirming the issue independently of libmount or fuzzing infrastructure.
Please let me know if you would like the reproducer adapted further or if additional information would be helpful. I am happy to assist with validation or testing of a fix.
Thank you for your time and for maintaining util-linux.
Best Regards
Yashashree Gund
[-- Attachment #1.2: Type: text/html, Size: 7639 bytes --]
[-- Attachment #2: ul_parse_size_asan_report.txt --]
[-- Type: text/plain, Size: 8049 bytes --]
=================================================================
==290472==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5020008924fa at pc 0xc25d0090fcdc bp 0xffffdcfa9630 sp 0xffffdcfa9628
READ of size 1 at 0x5020008924fa thread T0
#0 0xc25d0090fcd8 in ul_parse_size /home/parallels/util-linux/lib/strutils.c:116:6
#1 0xc25d008d1bd0 in setup_loopdev /home/parallels/util-linux/libmount/src/hook_loopdev.c:170:7
#2 0xc25d008d1bd0 in hook_prepare_loopdev /home/parallels/util-linux/libmount/src/hook_loopdev.c:541:7
#3 0xc25d008b1440 in mnt_context_call_hooks /home/parallels/util-linux/libmount/src/hooks.c:369:9
#4 0xc25d00878384 in mnt_context_prepare_srcpath /home/parallels/util-linux/libmount/src/context.c:2002:7
#5 0xc25d0088adf4 in mnt_context_prepare_mount /home/parallels/util-linux/libmount/src/context_mount.c:792:8
#6 0xc25d0082ab44 in LLVMFuzzerTestOneInput /home/parallels/util-linux/libmount_fuzzer.c:40:27
#7 0xc25d00735c0c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/parallels/util-linux/libmount_fuzzer_deep+0x225c0c) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#8 0xc25d00735364 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/parallels/util-linux/libmount_fuzzer_deep+0x225364) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#9 0xc25d00736b30 in fuzzer::Fuzzer::MutateAndTestOne() (/home/parallels/util-linux/libmount_fuzzer_deep+0x226b30) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#10 0xc25d00737894 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/parallels/util-linux/libmount_fuzzer_deep+0x227894) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#11 0xc25d00725730 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/parallels/util-linux/libmount_fuzzer_deep+0x215730) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#12 0xc25d0074e594 in main (/home/parallels/util-linux/libmount_fuzzer_deep+0x23e594) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#13 0xe8fca88884c0 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#14 0xe8fca8888594 in __libc_start_main csu/../csu/libc-start.c:360:3
#15 0xc25d0071ba6c in _start (/home/parallels/util-linux/libmount_fuzzer_deep+0x20ba6c) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
0x5020008924fa is located 0 bytes after 10-byte region [0x5020008924f0,0x5020008924fa)
allocated by thread T0 here:
#0 0xc25d007ed0d4 in malloc (/home/parallels/util-linux/libmount_fuzzer_deep+0x2dd0d4) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#1 0xc25d00763b98 in strndup (/home/parallels/util-linux/libmount_fuzzer_deep+0x253b98) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#2 0xc25d0096e6bc in optlist_new_opt /home/parallels/util-linux/libmount/src/optlist.c:438:16
#3 0xc25d00963424 in optlist_add_optstr /home/parallels/util-linux/libmount/src/optlist.c:524:9
#4 0xc25d00880f84 in apply_fs /home/parallels/util-linux/libmount/src/context.c:2460:8
#5 0xc25d00882a24 in mnt_context_apply_fstab /home/parallels/util-linux/libmount/src/context.c:2610:9
#6 0xc25d0088aa50 in mnt_context_prepare_mount /home/parallels/util-linux/libmount/src/context_mount.c:782:7
#7 0xc25d0082ab44 in LLVMFuzzerTestOneInput /home/parallels/util-linux/libmount_fuzzer.c:40:27
#8 0xc25d00735c0c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/parallels/util-linux/libmount_fuzzer_deep+0x225c0c) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#9 0xc25d00735364 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/parallels/util-linux/libmount_fuzzer_deep+0x225364) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#10 0xc25d00736b30 in fuzzer::Fuzzer::MutateAndTestOne() (/home/parallels/util-linux/libmount_fuzzer_deep+0x226b30) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#11 0xc25d00737894 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/parallels/util-linux/libmount_fuzzer_deep+0x227894) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#12 0xc25d00725730 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/parallels/util-linux/libmount_fuzzer_deep+0x215730) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#13 0xc25d0074e594 in main (/home/parallels/util-linux/libmount_fuzzer_deep+0x23e594) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
#14 0xe8fca88884c0 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#15 0xe8fca8888594 in __libc_start_main csu/../csu/libc-start.c:360:3
#16 0xc25d0071ba6c in _start (/home/parallels/util-linux/libmount_fuzzer_deep+0x20ba6c) (BuildId: cd366d821202ca98528bf79b9941e431153e4cc3)
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/parallels/util-linux/lib/strutils.c:116:6 in ul_parse_size
Shadow bytes around the buggy address:
0x502000892200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000892280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000892300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000892380: fa fa fa fa fa fa fd fa fa fa fa fa fa fa fa fa
0x502000892400: fa fa fa fa fa fa fd fa fa fa fa fa fa fa fa fa
=>0x502000892480: fa fa fa fa fa fa fd fd fa fa fa fa fa fa 00[02]
0x502000892500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000892580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000892600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000892680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000892700: fa fa fa fa fa fa fa fa fa fa fd fd fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==290472==ABORTING
MS: 10 ManualDict-Custom-CopyPart-Custom-ChangeByte-Custom-ShuffleBytes-Custom-CrossOver-Custom- DE: "iversion"-; base unit: aed04bcf8f04c9b420bd06e9bd0054341e9ad91d
0x69,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x31,0x37,0x33,0x20,0x30,0x35,0x64,0x30,0xa,0x37,0x20,0x32,0x35,0x20,0x6e,0x31,0x37,0x33,0x20,0x30,0x35,0x64,0x30,0xa,0x37,0x20,0x72,0xbf,0x67,0x65,0x78,0x65,0xa8,0x20,0x2d,0x20,0x6e,0x66,0x73,0x38,0x20,0xa,0x30,0x35,0x30,0x72,0x2e,0x2e,0x2f,0x2e,0x2f,0x6d,0x28,0x31,0xe4,0x20,0x2f,0x20,0xb2,0x6d,0x70,0x66,0x73,0x20,0xf2,0x2c,0x72,0x77,0x3d,0x22,0x22,0x2c,0x75,0x22,0x22,0x2c,0x2c,0x72,0x77,0x3d,0x22,0x22,0x2c,0x75,0x73,0x65,0x72,0x2c,0x6e,0x6f,0x61,0x75,0x74,0x6f,0x2c,0x58,0x75,0x6d,0x61,0x6b,0x65,0x2d,0x73,0x68,0x61,0x72,0x65,0x64,0x22,0x22,0x2c,0x73,0x69,0x7a,0x65,0x6c,0x69,0x6d,0x69,0x74,0x3d,0x30,0x30,0x30,0x30,0x30,0x2e,0x30,0x30,0x30,0xa,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x30,0x33,0x30,0x36,0x32,0x32,0x36,0x31,0x36,0x2c,0x68,0x65,0x6c,0x70,0x2c,0x64,0x69,0x32,
iversion173 05d0\0127 25 n173 05d0\0127 r\277gexe\250 - nfs8 \012050r.././m(1\344 / \262mpfs \362,rw=\"\",u\"\",,rw=\"\",user,noauto,Xumake-shared\"\",sizelimit=00000.000\012000000001030622616,help,di2
artifact_prefix='./'; Test unit written to ./crash-eecdf3b60e9e72795b3cea62b63d59a61b770c42
Base64: aXZlcnNpb24xNzMgMDVkMAo3IDI1IG4xNzMgMDVkMAo3IHK/Z2V4ZaggLSBuZnM4IAowNTByLi4vLi9tKDHkIC8gsm1wZnMg8ixydz0iIix1IiIsLHJ3PSIiLHVzZXIsbm9hdXRvLFh1bWFrZS1zaGFyZWQiIixzaXplbGltaXQ9MDAwMDAuMDAwCjAwMDAwMDAwMTAzMDYyMjYxNixoZWxwLGRpMg==
[-- Attachment #3: verify-repro.c --]
[-- Type: text/plain, Size: 293 bytes --]
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include "strutils.h"
int main(void)
{
uintmax_t res;
const char *s = "00000.000";
int rc = ul_parse_size(s, &res, NULL);
printf("input='%s' rc=%d errno=%d res=%ju\n",
s, rc, errno, res);
return 0;
}
[-- Attachment #4: ul_parse_size_execution_path.txt --]
[-- Type: text/plain, Size: 1342 bytes --]
Execution path notes â ul_parse_size heap OOB read
Overview
The attached reproducer input triggers a heap out-of-bounds read in
ul_parse_size() during mount option parsing in libmount.
High-level flow
The reproducer input is consumed by libmount as part of mount option
processing (e.g. via options such as sizelimit=).
libmount parses mount options into an option list.
Size-related options are passed to ul_parse_size() for numeric parsing.
Call path (trimmed)
mnt_context_prepare_mount()
ââ mnt_context_apply_fstab()
ââ apply_fs()
ââ optlist_add_optstr()
ââ optlist_new_opt()
ââ ul_parse_size()
Fault details
Input value example: sizelimit=00000.000
Inside ul_parse_size():
strtoumax() parses the integer part and sets end to the decimal point.
Decimal parsing advances the pointer p to the last fractional digit.
The suffix-checking logic then unconditionally dereferences p + 1
(and potentially further offsets).
When p points to the final byte of a heap-allocated string (allocated via
strndup()), dereferencing p + 1 results in a heap out-of-bounds read.
Result
AddressSanitizer reports a reproducible heap buffer overflow (read of size 1)
in ul_parse_size(). The issue is input-driven and does not depend on
uninitialized memory or use-after-free conditions.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox