Util-Linux package development
 help / color / mirror / Atom feed
* Re: lsblk: mount point in btrfs raid
From: Karel Zak @ 2024-07-10 10:39 UTC (permalink / raw)
  To: lukaro; +Cc: util-linux
In-Reply-To: <48C84F1A-9A8A-4E02-8E32-B171461DC0CC@lrose.de>

On Sat, Jul 06, 2024 at 04:17:30AM GMT, lukaro wrote:
> Hi everyone,
> 
> in lsblk, I'm used to seeing the mount point for all devices in an mdadm raid, like so:
> 
> sdb                8:16   0  16.4T  0 disk
> └─sdb1             8:17   0  16.4T  0 part
>   └─md127          9:127  0  32.8T  0 raid5 /store/RAID_A
> sdc                8:32   0  16.4T  0 disk
> └─sdc1             8:33   0  16.4T  0 part
>   └─md127          9:127  0  32.8T  0 raid5 /store/RAID_A
> sdd                8:48   0  16.4T  0 disk
> └─sdd1             8:49   0  16.4T  0 part
>   └─md127          9:127  0  32.8T  0 raid5 /store/RAID_A
> 
> However, with btrfs raid, the mount point is only shown for the first device:
> 
> sdf              8:80   1  7,3T  0 disk
> ├─sdf1           8:81   1  7,3T  0 part
> │ └─video_4b0  254:4    0  7,2T  0 crypt /mnt/video
> └─sdf2           8:82   1   28G  0 part
> sdg              8:96   1  7,3T  0 disk
> ├─sdg1           8:97   1  7,3T  0 part
> │ └─video_5f2  254:5    0  7,2T  0 crypt
> └─sdg2           8:98   1   28G  0 part
> sdh              8:112  1  7,3T  0 disk
> ├─sdh1           8:113  1  7,3T  0 part
> │ └─video_666  254:6    0  7,2T  0 crypt
> └─sdh2           8:114  1   28G  0 part
> sdi              8:128  1  7,3T  0 disk
> ├─sdi1           8:129  1  7,3T  0 part
> │ └─video_9b1  254:7    0  7,2T  0 crypt
> └─sdi2           8:130  1   28G  0 part
> 
> Where in the sysfs does lsblk obtain the mount point information?
>
> Is it possible that the btrfs module is lacking this information? I
> would like to understand why lsblk is lacking this information for
> btrfs.

The list of mount points can be found in /proc/self/mountinfo. When
searching for a mount point, lsblk uses the device number (major and
minor) or the device path.

The /proc/self/mountinfo contains a list of unique mount nodes. This
means that the Virtual File System (VFS) of Linux does not have
knowledge of how the filesystems internally implement the RAID.

The mdadm RAID maps all the disks to one virtual device, which is
visible to the system as a normal block device. However, for btrfs,
there is no such thing and all operations are handled internally
within the filesystem. 

It would probably be possible to add additional support for btrfs to
lsblk in order to request more details from the filesystem, but this
feature has not been implemented.

    Karel


-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* lsblk: mount point in btrfs raid
From: lukaro @ 2024-07-06  2:17 UTC (permalink / raw)
  To: util-linux

Hi everyone,

in lsblk, I'm used to seeing the mount point for all devices in an mdadm raid, like so:

sdb                8:16   0  16.4T  0 disk
└─sdb1             8:17   0  16.4T  0 part
  └─md127          9:127  0  32.8T  0 raid5 /store/RAID_A
sdc                8:32   0  16.4T  0 disk
└─sdc1             8:33   0  16.4T  0 part
  └─md127          9:127  0  32.8T  0 raid5 /store/RAID_A
sdd                8:48   0  16.4T  0 disk
└─sdd1             8:49   0  16.4T  0 part
  └─md127          9:127  0  32.8T  0 raid5 /store/RAID_A

However, with btrfs raid, the mount point is only shown for the first device:

sdf              8:80   1  7,3T  0 disk
├─sdf1           8:81   1  7,3T  0 part
│ └─video_4b0  254:4    0  7,2T  0 crypt /mnt/video
└─sdf2           8:82   1   28G  0 part
sdg              8:96   1  7,3T  0 disk
├─sdg1           8:97   1  7,3T  0 part
│ └─video_5f2  254:5    0  7,2T  0 crypt
└─sdg2           8:98   1   28G  0 part
sdh              8:112  1  7,3T  0 disk
├─sdh1           8:113  1  7,3T  0 part
│ └─video_666  254:6    0  7,2T  0 crypt
└─sdh2           8:114  1   28G  0 part
sdi              8:128  1  7,3T  0 disk
├─sdi1           8:129  1  7,3T  0 part
│ └─video_9b1  254:7    0  7,2T  0 crypt
└─sdi2           8:130  1   28G  0 part

Where in the sysfs does lsblk obtain the mount point information? Is it possible that the btrfs module is lacking this information? I would like to understand why lsblk is lacking this information for btrfs.

Thanks in advance,
lukaro

^ permalink raw reply

* [ANNOUNCE] util-linux v2.40.2
From: Karel Zak @ 2024-07-04  8:52 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, util-linux


The util-linux stable maintenance release v2.40.2 is now available at
 
  http://www.kernel.org/pub/linux/utils/util-linux/v2.40/

Feedback and bug reports, as always, are welcomed.
 
  Karel


util-linux v2.40.2 Release Notes
================================

Changes between v2.40.1 and v2.40.2
-----------------------------------

autotools:
   - Properly order install dependencies of pam_lastlog2  [Thomas Weißschuh]
   - make pam install path configurable  [Thomas Weißschuh]
bash-completion:
   - add logger --sd-* completions  [Ville Skyttä]
build-sys:
   - _PATH_VENDORDIR workaround  [Karel Zak]
cfdisk:
   - fix possible integer overflow [coverity scan]  [Karel Zak]
docs:
   - update AUTHORS file  [Karel Zak]
include/pidfd-utils:
   - provide ENOSYS stubs if pidfd functions are missing  [Thomas Weißschuh]
   - remove hardcoded syscall fallback  [Karel Zak]
lib/buffer:
   - introduce ul_buffer_get_string()  [Thomas Weißschuh]
lib/fileutils:
   - add ul_basename()  [Karel Zak]
lib/path:
   - Fix ul_path_read_buffer() [Daan De Meyer]
lib/sysfs:
   - abort device hierarchy walk at root of sysfs  [Thomas Weißschuh]
   - zero-terminate result of sysfs_blkdev_get_devchain()  [Thomas Weißschuh]
libmount:
   - fix syscall save function  [Karel Zak]
   - fix tree FD usage in subdir hook  [Karel Zak]
   - improving robustness in reading kernel messages  [Karel Zak]
   - add pidfs to pseudo fs list  [Mike Yuan]
libsmartcols:
   - fix reduction stages use  [Karel Zak]
   - ensure filter-scanner/paser.c file is newer than the .h file  [Chen Qi]
libuuid:
   - clear uuidd cache on fork()  [Thomas Weißschuh]
   - drop check for HAVE_TLS  [Thomas Weißschuh]
   - drop duplicate assignment liuuid_la_LDFLAGS  [Karel Zak]
   - split uuidd cache into dedicated struct  [Thomas Weißschuh]
   - Conditionally add uuid_time64 to sym. version map [Nicholas Vinson]
lscpu:
   - New Arm Cortex part numbers  [Jeremy Linton]
lsfd:
   - Refactor the pidfd logic into lsfd-pidfd.c  [Xi Ruoyao]
   - Support pidfs  [Xi Ruoyao]
   - test  Adapt test cases for pidfs  [Xi Ruoyao]
meson:
   - Correctly require the Python.h header for the python dependency  [Jordan Williams]
   - Fix build-python option  [Jordan Williams]
   - Only require Python module when building pylibmount  [Jordan Williams]
misc-utils:
   - uuidd  Use ul_sig_err instead of errx  [Cristian Rodríguez]
mkswap.8.adoc:
   - update note regarding swapfile creation  [Mike Yuan]
po:
   - merge changes  [Karel Zak]
   - update es.po (from translationproject.org)  [Antonio Ceballos Roa]
   - update ja.po (from translationproject.org)  [Hideki Yoshida]
po-man:
   - merge changes  [Karel Zak]
rename:
   - use ul_basename()  [Karel Zak]
sys-utils/setpgid:
   - make -f work  [Emanuele Torre]
wdctl:
   - always query device node when sysfs is unavailable  [Thomas Weißschuh]

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH v3 0/2] unshare: manage binfmt_misc mounts
From: Karel Zak @ 2024-06-20 10:25 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: util-linux
In-Reply-To: <20240619081951.656640-1-laurent@vivier.eu>

On Wed, Jun 19, 2024 at 10:19:49AM GMT, Laurent Vivier wrote:
>  include/pathnames.h      |  1 +
>  sys-utils/unshare.1.adoc | 21 ++++++++++++
>  sys-utils/unshare.c      | 71 +++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 92 insertions(+), 1 deletion(-)

Applied, thanks.

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH] umount, losetup: Document loop destroy behavior
From: Karel Zak @ 2024-06-20 10:24 UTC (permalink / raw)
  To: Stanislav Brabec; +Cc: util-linux
In-Reply-To: <20240619153133.4628-2-sbrabec@suse.cz>

On Wed, Jun 19, 2024 at 05:31:34PM GMT, Stanislav Brabec wrote:
>  sys-utils/losetup.8.adoc | 2 +-
>  sys-utils/umount.8.adoc  | 2 ++
>  2 files changed, 3 insertions(+), 1 deletion(-)

 Applied, thanks!

    Karel


-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* [PATCH] umount, losetup: Document loop destroy behavior
From: Stanislav Brabec @ 2024-06-19 15:31 UTC (permalink / raw)
  To: util-linux; +Cc: Stanislav Brabec

The loop device detach runs asynchronously in the background. It can cause
unexpected errors. Document the possible existence of the loop device after
exit of losetup -d or umount and need of "udevadm settle".

Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
---
 sys-utils/losetup.8.adoc | 2 +-
 sys-utils/umount.8.adoc  | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/sys-utils/losetup.8.adoc b/sys-utils/losetup.8.adoc
index 9ea9451ce..e22e650b8 100644
--- a/sys-utils/losetup.8.adoc
+++ b/sys-utils/losetup.8.adoc
@@ -54,7 +54,7 @@ The _size_ and _offset_ arguments may be followed by the multiplicative suffixes
 Show the status of all loop devices. Note that not all information is accessible for non-root users. See also *--list*. The old output format (as printed without *--list*) is deprecated.
 
 *-d*, *--detach* _loopdev_...::
-Detach the file or device associated with the specified loop device(s). Note that since Linux v3.7 kernel uses "lazy device destruction". The detach operation does not return *EBUSY* error anymore if device is actively used by system, but it is marked by autoclear flag and destroyed later.
+Detach the file or device associated with the specified loop device(s). Note that since Linux v3.7 kernel uses "lazy device destruction". The detach operation does not return *EBUSY* error anymore if device is actively used by system, but it is marked by autoclear flag and destroyed later. Even if the device is not used, the loop device can be destroyed later. If you need to wait for a complete removal of the loop device, call *udevadm settle* after *losetup*.
 
 *-D*, *--detach-all*::
 Detach all associated loop devices.
diff --git a/sys-utils/umount.8.adoc b/sys-utils/umount.8.adoc
index f9b96e1ff..b9a3ff963 100644
--- a/sys-utils/umount.8.adoc
+++ b/sys-utils/umount.8.adoc
@@ -127,6 +127,8 @@ The *umount* command will automatically detach loop device previously initialize
 
 In this case the device is initialized with "autoclear" flag (see *losetup*(8) output for more details), otherwise it's necessary to use the option *--detach-loop* or call *losetup -d* _device_. The autoclear feature is supported since Linux 2.6.25.
 
+Note that since Linux v3.7 kernel uses "lazy device destruction". The system just marks the loop device by autoclear flag and destroys it later. If you need to wait for a complete removal of the loop device, call *udevadm settle* after *umount*.
+
 == EXIT STATUS
 
 *umount* has the following exit status values (the bits can be ORed):
-- 
2.45.0


^ permalink raw reply related

* Re: [PATCH v3 0/2] unshare: manage binfmt_misc mounts
From: Karel Zak @ 2024-06-19 10:43 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: util-linux
In-Reply-To: <20240619081951.656640-1-laurent@vivier.eu>

On Wed, Jun 19, 2024 at 10:19:49AM +0200, Laurent Vivier wrote:
>  include/pathnames.h      |  1 +
>  sys-utils/unshare.1.adoc | 21 ++++++++++++
>  sys-utils/unshare.c      | 71 +++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 92 insertions(+), 1 deletion(-)

Looks good, thank you. I will merge it once the changes in
https://github.com/util-linux/util-linux/pull/3094 pass our CI.

    Karel


-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* [PATCH v3 2/2] unshare: load binfmt_misc interpreter
From: Laurent Vivier @ 2024-06-19  8:19 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier
In-Reply-To: <20240619081951.656640-1-laurent@vivier.eu>

add -l, --load-interp <file> to load a binfmt_misc interpreter at startup.

The interpreter is loaded from the initial fileystem if the 'F' flags is
provided, otherwise from inside the new namespace
This makes possible to start a chroot of another architecture without
being root.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---

Notes:
    v3:
      - update man page
      - load interpreter before the chroot only if there is the a chroot
    
    v2:
      - use <binfmt_mnt>/register rather than _PATH_PROC_BINFMT_MISC_REGISTER
        to load the interpreter

 sys-utils/unshare.1.adoc | 18 ++++++++++++++
 sys-utils/unshare.c      | 52 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index 48d1a5579282..6396c4892ddf 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -138,6 +138,10 @@ Set the user ID which will be used in the entered namespace.
 *-G*, *--setgid* _gid_::
 Set the group ID which will be used in the entered namespace and drop supplementary groups.
 
+*-l*, **--load-interp=**__string__::
+Load binfmt_misc definition in the namespace (implies *--mount-binfmt*). The __string__ argument is ``:name:type:offset:magic:mask:interpreter:flags``. For more details about new binary type registration see  https://www.kernel.org/doc/Documentation/admin-guide/binfmt-misc.rst.
+To manage the F flag in ``flags`` with **--root** parameter, binfmt_misc is mounted twice, once before the chroot to load the interpreter from the caller filesystem and once after to make it available from the chroot userspace.
+
 *--monotonic* _offset_::
 Set the offset of *CLOCK_MONOTONIC* which will be used in the entered time namespace. This option requires unsharing a time namespace with *--time*.
 
@@ -256,6 +260,20 @@ up 21 hours, 30 minutes
 up 9 years, 28 weeks, 1 day, 2 hours, 50 minutes
 ....
 
+The following example execute a chroot into the directory /chroot/powerpc/jessie and install the interpreter /bin/qemu-ppc-static to execute the powerpc binaries.
+
+....
+$  unshare --map-root-user --fork --pid --load-interp=":qemu-ppc:M::\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-ppc-static:OCF" --root=/chroot/powerpc/jessie /bin/bash -l
+....
+
+The ``load-interp`` parameter can be read as following::
+``qemu-ppc``::: is the name of the new file created below ``/proc/sys/fs/binfmt_misc`` to register the interpreter
+``M``::: defines the interpreter for a given type of magic number
+``\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x1``::: is the magic number to recognize the file to interpret (in this case, the ELF header for PPC32)
+``\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff``::: the mask to apply to the magic number
+``/bin/qemu-ppc-static``::: the interpreter to use with the file
+``OCF``::: the file is open by the kernel with credential and security tokens of the file itself and loaded as soon as we register it.
+
 == AUTHORS
 
 mailto:dottedmag@dottedmag.net[Mikhail Gusarov],
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index d79aa1125955..e48e4c9f55a5 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -725,6 +725,35 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser,
 	exit(EXIT_SUCCESS);
 }
 
+static int is_fixed(const char *interp)
+{
+	const char *flags;
+
+	flags = strrchr(interp, ':');
+
+	return strchr(flags, 'F') != NULL;
+}
+
+static void load_interp(const char *binfmt_mnt, const char *interp)
+{
+	int dirfd, fd;
+
+	dirfd = open(binfmt_mnt, O_PATH | O_DIRECTORY);
+	if (dirfd < 0)
+		err(EXIT_FAILURE, _("cannot open %s"), binfmt_mnt);
+
+	fd = openat(dirfd, "register", O_WRONLY);
+	if (fd < 0)
+		err(EXIT_FAILURE, _("cannot open %s/register"), binfmt_mnt);
+
+	if (write_all(fd, interp, strlen(interp)))
+		err(EXIT_FAILURE, _("write failed %s/register"), binfmt_mnt);
+
+	close(fd);
+
+	close(dirfd);
+}
+
 static void __attribute__((__noreturn__)) usage(void)
 {
 	FILE *out = stdout;
@@ -772,6 +801,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" -G, --setgid <gid>        set gid in entered namespace\n"), out);
 	fputs(_(" --monotonic <offset>      set clock monotonic offset (seconds) in time namespaces\n"), out);
 	fputs(_(" --boottime <offset>       set clock boottime offset (seconds) in time namespaces\n"), out);
+	fputs(_(" -l, --load-interp <file>  load binfmt definition in the namespace (implies --mount-binfmt)\n"), out);
 
 	fputs(USAGE_SEPARATOR, out);
 	fprintf(out, USAGE_HELP_OPTIONS(27));
@@ -830,6 +860,7 @@ int main(int argc, char *argv[])
 		{ "wd",		   required_argument, NULL, 'w'		    },
 		{ "monotonic",     required_argument, NULL, OPT_MONOTONIC   },
 		{ "boottime",      required_argument, NULL, OPT_BOOTTIME    },
+		{ "load-interp",   required_argument, NULL, 'l'		    },
 		{ NULL, 0, NULL, 0 }
 	};
 
@@ -846,6 +877,7 @@ int main(int argc, char *argv[])
 	const char *newroot = NULL;
 	const char *newdir = NULL;
 	pid_t pid_bind = 0, pid_idmap = 0;
+	const char *newinterp = NULL;
 	pid_t pid = 0;
 #ifdef UL_HAVE_PIDFD
 	int fd_parent_pid = -1;
@@ -868,7 +900,7 @@ int main(int argc, char *argv[])
 	textdomain(PACKAGE);
 	close_stdout_atexit();
 
-	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:c", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:cl:", longopts, NULL)) != -1) {
 		switch (c) {
 		case 'f':
 			forkit = 1;
@@ -1011,6 +1043,15 @@ int main(int argc, char *argv[])
 			boottime = strtos64_or_err(optarg, _("failed to parse boottime offset"));
 			force_boottime = 1;
 			break;
+		case 'l':
+			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
+			if (!binfmt_mnt) {
+				if (!procmnt)
+					procmnt = "/proc";
+				binfmt_mnt = _PATH_PROC_BINFMT_MISC;
+			}
+			newinterp = optarg;
+			break;
 
 		case 'h':
 			usage();
@@ -1165,6 +1206,13 @@ int main(int argc, char *argv[])
 	if ((unshare_flags & CLONE_NEWNS) && propagation)
 		set_propagation(propagation);
 
+	if (newinterp && is_fixed(newinterp) && newroot) {
+		if (mount("binfmt_misc", _PATH_PROC_BINFMT_MISC, "binfmt_misc",
+			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
+			err(EXIT_FAILURE, _("mount %s failed"), _PATH_PROC_BINFMT_MISC);
+		load_interp(_PATH_PROC_BINFMT_MISC, newinterp);
+	}
+
 	if (newroot) {
 		if (chroot(newroot) != 0)
 			err(EXIT_FAILURE,
@@ -1196,6 +1244,8 @@ int main(int argc, char *argv[])
 			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
 			err(EXIT_FAILURE, _("mount %s failed"), binfmt_mnt);
 	}
+	if (newinterp && !(is_fixed(newinterp) && newroot))
+		load_interp(binfmt_mnt, newinterp);
 
 	if (force_gid) {
 		if (setgroups(0, NULL) != 0)	/* drop supplementary groups */
-- 
2.45.2


^ permalink raw reply related

* [PATCH v3 0/2] unshare: manage binfmt_misc mounts
From: Laurent Vivier @ 2024-06-19  8:19 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier

Since linux v6.7 and
commit 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts"),
binfmt_misc can be mountable in a non-initial user namespace by
a non privileged user.

Extend unshare to manage it:

- add --mount-binfmt[=<dir>] to mount binfmt_misc filesystem, this
  results in clearing inherited interpreters from the previous namespace

- add -l, --load-interp <file> to load a binfmt_misc interpreter at startup.

  The interpreter is loaded from the initial fileystem if the 'F' flags is
  provided, otherwise from inside the new namespace
  This makes possible to start a chroot of another architecture without
  being root.

For instance:

  With 'F' flag, load the interpreter from the initial namespace:

    $ /bin/qemu-m68k-static --version
    qemu-m68k version 8.2.2 (qemu-8.2.2-1.fc40)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers
    $ unshare --map-root-user --fork --pid --load-interp=":qemu-m68k:M::\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x04:\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-m68k-static:OCF" --root=chroot/m68k/sid
    # QEMU_VERSION= ls
    qemu-m68k version 8.2.2 (qemu-8.2.2-1.fc40)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers
    # /qemu-m68k  --version
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

  Without 'F' flag, from inside the namespace:

    $ unshare --map-root-user --fork --pid --load-interp=":qemu-m68k:M::\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x04:\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/qemu-m68k:OC" --root=chroot/m68k/sid
    # QEMU_VERSION= ls
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
    # /qemu-m68k  --version
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

v3:
  - update man page
  - load interpreter before the chroot only if there is the a chroot

v2:
  - use <binfmt_mnt>/register rather than _PATH_PROC_BINFMT_MISC_REGISTER to load the interpreter

Laurent Vivier (2):
  unshare: mount binfmt_misc
  unshare: load binfmt_misc interpreter

 include/pathnames.h      |  1 +
 sys-utils/unshare.1.adoc | 21 ++++++++++++
 sys-utils/unshare.c      | 71 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 92 insertions(+), 1 deletion(-)

-- 
2.45.2


^ permalink raw reply

* [PATCH v3 1/2] unshare: mount binfmt_misc
From: Laurent Vivier @ 2024-06-19  8:19 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier
In-Reply-To: <20240619081951.656640-1-laurent@vivier.eu>

add --mount-binfmt[=<dir>] to mount binfmt_misc filesystem,
this results in clearing inherited interpreters from the previous namespace

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---

Notes:
    v2:
      - remove definition of _PATH_PROC_BINFMT_MISC_REGISTER

 include/pathnames.h      |  1 +
 sys-utils/unshare.1.adoc |  3 +++
 sys-utils/unshare.c      | 19 +++++++++++++++++++
 3 files changed, 23 insertions(+)

diff --git a/include/pathnames.h b/include/pathnames.h
index 81fa405f63c7..569bef17f982 100644
--- a/include/pathnames.h
+++ b/include/pathnames.h
@@ -204,6 +204,7 @@
 /* sysctl fs paths */
 #define _PATH_PROC_SYS_FS	"/proc/sys/fs"
 #define _PATH_PROC_PIPE_MAX_SIZE	_PATH_PROC_SYS_FS "/pipe-max-size"
+#define _PATH_PROC_BINFMT_MISC	_PATH_PROC_SYS_FS "/binfmt_misc"
 
 /* irqtop paths */
 #define _PATH_PROC_INTERRUPTS	"/proc/interrupts"
diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index e6201e28fffd..48d1a5579282 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -90,6 +90,9 @@ When *unshare* terminates, have _signame_ be sent to the forked child process. C
 *--mount-proc*[**=**__mountpoint__]::
 Just before running the program, mount the proc filesystem at _mountpoint_ (default is _/proc_). This is useful when creating a new PID namespace. It also implies creating a new mount namespace since the _/proc_ mount would otherwise mess up existing programs on the system. The new proc filesystem is explicitly mounted as private (with *MS_PRIVATE*|*MS_REC*).
 
+*--mount-binfmt*[**=**__mountpoint__]::
+Just before running the program, mount the binfmt_misc filesystem at _mountpoint_ (default is /proc/sys/fs/binfmt_misc).  It also implies creating a new mount namespace since the binfmt_misc mount would otherwise mess up existing programs on the system.  The new binfmt_misc filesystem is explicitly mounted as private (with *MS_PRIVATE*|*MS_REC*).
+
 **--map-user=**__uid|name__::
 Run the program only after the current effective user ID has been mapped to _uid_. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--user*.
 
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 57f3b8744fb5..d79aa1125955 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -760,6 +760,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" --kill-child[=<signame>]  when dying, kill the forked child (implies --fork)\n"
 		"                             defaults to SIGKILL\n"), out);
 	fputs(_(" --mount-proc[=<dir>]      mount proc filesystem first (implies --mount)\n"), out);
+	fputs(_(" --mount-binfmt[=<dir>]    mount binfmt filesystem first (implies --user and --mount)\n"), out);
 	fputs(_(" --propagation slave|shared|private|unchanged\n"
 	        "                           modify mount propagation in mount namespace\n"), out);
 	fputs(_(" --setgroups allow|deny    control the setgroups syscall in user namespaces\n"), out);
@@ -783,6 +784,7 @@ int main(int argc, char *argv[])
 {
 	enum {
 		OPT_MOUNTPROC = CHAR_MAX + 1,
+		OPT_MOUNTBINFMT,
 		OPT_PROPAGATION,
 		OPT_SETGROUPS,
 		OPT_KILLCHILD,
@@ -811,6 +813,7 @@ int main(int argc, char *argv[])
 		{ "fork",          no_argument,       NULL, 'f'             },
 		{ "kill-child",    optional_argument, NULL, OPT_KILLCHILD   },
 		{ "mount-proc",    optional_argument, NULL, OPT_MOUNTPROC   },
+		{ "mount-binfmt",  optional_argument, NULL, OPT_MOUNTBINFMT },
 		{ "map-user",      required_argument, NULL, OPT_MAPUSER     },
 		{ "map-users",     required_argument, NULL, OPT_MAPUSERS    },
 		{ "map-group",     required_argument, NULL, OPT_MAPGROUP    },
@@ -839,6 +842,7 @@ int main(int argc, char *argv[])
 	struct map_range *groupmap = NULL;
 	int kill_child_signo = 0; /* 0 means --kill-child was not used */
 	const char *procmnt = NULL;
+	const char *binfmt_mnt = NULL;
 	const char *newroot = NULL;
 	const char *newdir = NULL;
 	pid_t pid_bind = 0, pid_idmap = 0;
@@ -913,6 +917,15 @@ int main(int argc, char *argv[])
 			unshare_flags |= CLONE_NEWNS;
 			procmnt = optarg ? optarg : "/proc";
 			break;
+		case OPT_MOUNTBINFMT:
+			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
+			binfmt_mnt = optarg;
+			if (!binfmt_mnt) {
+				if (!procmnt)
+					procmnt = "/proc";
+				binfmt_mnt = _PATH_PROC_BINFMT_MISC;
+			}
+			break;
 		case OPT_MAPUSER:
 			unshare_flags |= CLONE_NEWUSER;
 			mapuser = get_user(optarg, _("failed to parse uid"));
@@ -1178,6 +1191,12 @@ int main(int argc, char *argv[])
 			err(EXIT_FAILURE, _("mount %s failed"), procmnt);
 	}
 
+	if (binfmt_mnt) {
+		if (mount("binfmt_misc", binfmt_mnt, "binfmt_misc",
+			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
+			err(EXIT_FAILURE, _("mount %s failed"), binfmt_mnt);
+	}
+
 	if (force_gid) {
 		if (setgroups(0, NULL) != 0)	/* drop supplementary groups */
 			err(EXIT_FAILURE, _("setgroups failed"));
-- 
2.45.2


^ permalink raw reply related

* Re: [PATCH v2 2/2] unshare: load binfmt_misc interpreter
From: Karel Zak @ 2024-06-18 11:58 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: util-linux
In-Reply-To: <e875d06e-a0c5-47c3-b368-06f398e676ab@vivier.eu>

On Tue, Jun 18, 2024 at 12:13:50PM +0200, Laurent Vivier wrote:
> Le 18/06/2024 à 11:51, Karel Zak a écrit :
> > 
> >   Hi Laurent,
> > 
> 
> Hi Karel,
> 
> > On Tue, Jun 11, 2024 at 10:43:14AM +0200, Laurent Vivier wrote:
> > > +*-l*, **--load-interp=**__file__::
> > > +Load binfmt_misc definition in the namespace (implies *--mount-binfmt*).
> > 
> > Is it actually a file, or does the argument have a more complex
> > format? If there is something more that it should be described here.
> > It fine describe in the man page more about the interpreters.
> 
> Your right the format here is not actually a file, but it defines how to use
> the file provided in the parameter as an interpreter.
> 
> We provide here what we will write in /proc/sys/fs/binfmt_misc/register and
> the format is described in
> https://www.kernel.org/doc/Documentation/admin-guide/binfmt-misc.rst:
> 
> "To actually register a new binary type, you have to set up a string looking
> like ``:name:type:offset:magic:mask:interpreter:flags``

 I guess we can use something like:

 -l*, **--load-interp=**string

 Load binfmt_misc definition in the namespace. The __string__ argument
 is ``:name:type:offset:magic:mask:interpreter:flags``. For more
 details about new binary type registration see
 https://www.kernel.org/doc/Documentation/admin-guide/binfmt-misc.rst.


> > As an uneducated reader, I am confused by the flags. Where is the 'F'
> > flag? Perhaps you could provide more explanation to make it easier for
> > readers to understand.
> 
> I think this option should be used by educated user that is aware of binfmt_misc format.
> 
> Do you want I copy a part of the binfmt_misc documentation in the unshare documentation?

It's probably overkill copy all the text.

> I'll update the man page accordingly.

Thanks!

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH v2 2/2] unshare: load binfmt_misc interpreter
From: Laurent Vivier @ 2024-06-18 10:13 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux
In-Reply-To: <20240618095151.2vidavjn47xgbxf4@ws.net.home>

Le 18/06/2024 à 11:51, Karel Zak a écrit :
> 
>   Hi Laurent,
> 

Hi Karel,

> On Tue, Jun 11, 2024 at 10:43:14AM +0200, Laurent Vivier wrote:
>> +*-l*, **--load-interp=**__file__::
>> +Load binfmt_misc definition in the namespace (implies *--mount-binfmt*).
> 
> Is it actually a file, or does the argument have a more complex
> format? If there is something more that it should be described here.
> It fine describe in the man page more about the interpreters.

Your right the format here is not actually a file, but it defines how to use the file provided in 
the parameter as an interpreter.

We provide here what we will write in /proc/sys/fs/binfmt_misc/register and the format is described 
in https://www.kernel.org/doc/Documentation/admin-guide/binfmt-misc.rst:

"To actually register a new binary type, you have to set up a string looking like 
``:name:type:offset:magic:mask:interpreter:flags``
[...]
- ``name``
    is an identifier string. A new /proc file will be created with this
    name below ``/proc/sys/fs/binfmt_misc``
- ``type``
    is the type of recognition. Give ``M`` for magic and ``E`` for extension.
- ``offset``
    is the offset of the magic/mask in the file
- ``magic``
    is the byte sequence binfmt_misc is matching for.
- ``mask``
    is an (optional, defaults to all 0xff) mask.
- ``interpreter``
    is the program that should be invoked with the binary as first
    argument
- ``flags``
    is an optional field that controls several aspects of the invocation
    of the interpreter.
    ``P`` - preserve-argv[0]
             Legacy behavior of binfmt_misc is to overwrite
             the original argv[0] with the full path to the binary. When this
             flag is included, binfmt_misc will add an argument to the argument
             vector for this purpose, thus preserving the original ``argv[0]``.
     ``O`` - open-binary
	    Legacy behavior of binfmt_misc is to pass the full path
             of the binary to the interpreter as an argument. When this flag is
             included, binfmt_misc will open the file for reading and pass its
             descriptor as an argument
     ``C`` - credentials
             Currently, the behavior of binfmt_misc is to calculate
             the credentials and security token of the new process according to
             the interpreter. When this flag is included, these attributes are
             calculated according to the binary
``F`` - fix binary
             The usual behaviour of binfmt_misc is to spawn the
	    binary lazily when the misc format file is invoked.  However,
	    this doesn't work very well in the face of mount namespaces and
	    changeroots, so the ``F`` mode opens the binary as soon as the
	    emulation is installed and uses the opened image to spawn the
	    emulator"
> 
>> +
>>   *--monotonic* _offset_::
>>   Set the offset of *CLOCK_MONOTONIC* which will be used in the entered time namespace. This option requires unsharing a time namespace with *--time*.
>>   
>> @@ -256,6 +259,13 @@ up 21 hours, 30 minutes
>>   up 9 years, 28 weeks, 1 day, 2 hours, 50 minutes
>>   ....
>>   
>> +The following example execute a chroot into the directory /chroot/powerpc/jessie and install the interpreter /bin/qemu-ppc-static to execute the powerpc binaries.
>> +If the interpreter is defined with the flag F, the interpreter is loaded before the chroot otherwise the interpreter is loaded from inside the chroot.
>> +
>> +....
>> +$  unshare --map-root-user --fork --pid --load-interp=":qemu-ppc:M::\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-ppc-static:OCF" --root=/chroot/powerpc/jessie /bin/bash -l
>> +....
> 
> As an uneducated reader, I am confused by the flags. Where is the 'F'
> flag? Perhaps you could provide more explanation to make it easier for
> readers to understand.

I think this option should be used by educated user that is aware of binfmt_misc format.

Do you want I copy a part of the binfmt_misc documentation in the unshare documentation?

> 
>>   == AUTHORS
>>   
>>   mailto:dottedmag@dottedmag.net[Mikhail Gusarov],
>> diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
>> index d79aa1125955..f8e1141840ca 100644
>> --- a/sys-utils/unshare.c
>> +++ b/sys-utils/unshare.c
>> @@ -725,6 +725,35 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser,
>>   	exit(EXIT_SUCCESS);
>>   }
>>   
>> +static int is_fixed(const char *interp)
>> +{
>> +	const char *flags;
>> +
>> +	flags = strrchr(interp, ':');
>> +
>> +	return strchr(flags, 'F') != NULL;
>> +}
>> +
>> +static void load_interp(const char *binfmt_mnt, const char *interp)
>> +{
>> +	int dirfd, fd;
>> +
>> +	dirfd = open(binfmt_mnt, O_PATH | O_DIRECTORY);
>> +	if (dirfd < 0)
>> +		err(EXIT_FAILURE, _("cannot open %s"), binfmt_mnt);
>> +
>> +	fd = openat(dirfd, "register", O_WRONLY);
>> +	if (fd < 0)
>> +		err(EXIT_FAILURE, _("cannot open %s/register"), binfmt_mnt);
>> +
>> +	if (write_all(fd, interp, strlen(interp)))
>> +		err(EXIT_FAILURE, _("write failed %s/register"), binfmt_mnt);
>> +
>> +	close(fd);
>> +
>> +	close(dirfd);
>> +}
>> +
>>   static void __attribute__((__noreturn__)) usage(void)
>>   {
>>   	FILE *out = stdout;
>> @@ -772,6 +801,7 @@ static void __attribute__((__noreturn__)) usage(void)
>>   	fputs(_(" -G, --setgid <gid>        set gid in entered namespace\n"), out);
>>   	fputs(_(" --monotonic <offset>      set clock monotonic offset (seconds) in time namespaces\n"), out);
>>   	fputs(_(" --boottime <offset>       set clock boottime offset (seconds) in time namespaces\n"), out);
>> +	fputs(_(" -l, --load-interp <file>  load binfmt definition in the namespace (implies --mount-binfmt)\n"), out);
>>   
>>   	fputs(USAGE_SEPARATOR, out);
>>   	fprintf(out, USAGE_HELP_OPTIONS(27));
>> @@ -830,6 +860,7 @@ int main(int argc, char *argv[])
>>   		{ "wd",		   required_argument, NULL, 'w'		    },
>>   		{ "monotonic",     required_argument, NULL, OPT_MONOTONIC   },
>>   		{ "boottime",      required_argument, NULL, OPT_BOOTTIME    },
>> +		{ "load-interp",   required_argument, NULL, 'l'		    },
>>   		{ NULL, 0, NULL, 0 }
>>   	};
>>   
>> @@ -846,6 +877,7 @@ int main(int argc, char *argv[])
>>   	const char *newroot = NULL;
>>   	const char *newdir = NULL;
>>   	pid_t pid_bind = 0, pid_idmap = 0;
>> +	const char *newinterp = NULL;
>>   	pid_t pid = 0;
>>   #ifdef UL_HAVE_PIDFD
>>   	int fd_parent_pid = -1;
>> @@ -868,7 +900,7 @@ int main(int argc, char *argv[])
>>   	textdomain(PACKAGE);
>>   	close_stdout_atexit();
>>   
>> -	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:c", longopts, NULL)) != -1) {
>> +	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:cl:", longopts, NULL)) != -1) {
>>   		switch (c) {
>>   		case 'f':
>>   			forkit = 1;
>> @@ -1011,6 +1043,15 @@ int main(int argc, char *argv[])
>>   			boottime = strtos64_or_err(optarg, _("failed to parse boottime offset"));
>>   			force_boottime = 1;
>>   			break;
>> +		case 'l':
>> +			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
>> +			if (!binfmt_mnt) {
>> +				if (!procmnt)
>> +					procmnt = "/proc";
>> +				binfmt_mnt = _PATH_PROC_BINFMT_MISC;
>> +			}
>> +			newinterp = optarg;
>> +			break;
>>   
>>   		case 'h':
>>   			usage();
>> @@ -1165,6 +1206,13 @@ int main(int argc, char *argv[])
>>   	if ((unshare_flags & CLONE_NEWNS) && propagation)
>>   		set_propagation(propagation);
>>   
>> +	if (newinterp && is_fixed(newinterp)) {
>> +		if (mount("binfmt_misc", _PATH_PROC_BINFMT_MISC, "binfmt_misc",
>> +			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
>> +			err(EXIT_FAILURE, _("mount %s failed"), _PATH_PROC_BINFMT_MISC);
>> +		load_interp(_PATH_PROC_BINFMT_MISC, newinterp);
>> +	}
> 
> If I understand correctly, using --load-interp with 'F' calls
> mount(binfmt_misc) twice:
> 
> 1) before chroot
> 2) after chroot() and after mount(/proc) (implies --mount-binfmt and
>     --mount-proc too)

Yes, it's needed before chroot to load the interpreter from the caller filesystem.
it's not needed after the chroot in this case, it's only there for consistency to have it in the 
chroot as we asked it on the command line. I think it can be removed if you prefer.
> 
> I believe it would be helpful to include this information in the man
> page.

I'll update the man page accordingly.

Thanks,
Laurent
>      Karel
> 


^ permalink raw reply

* Re: [PATCH v2 2/2] unshare: load binfmt_misc interpreter
From: Karel Zak @ 2024-06-18  9:51 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: util-linux
In-Reply-To: <20240611084314.183913-3-laurent@vivier.eu>


 Hi Laurent,

On Tue, Jun 11, 2024 at 10:43:14AM +0200, Laurent Vivier wrote:
> +*-l*, **--load-interp=**__file__::
> +Load binfmt_misc definition in the namespace (implies *--mount-binfmt*).

Is it actually a file, or does the argument have a more complex
format? If there is something more that it should be described here.
It fine describe in the man page more about the interpreters.

> +
>  *--monotonic* _offset_::
>  Set the offset of *CLOCK_MONOTONIC* which will be used in the entered time namespace. This option requires unsharing a time namespace with *--time*.
>  
> @@ -256,6 +259,13 @@ up 21 hours, 30 minutes
>  up 9 years, 28 weeks, 1 day, 2 hours, 50 minutes
>  ....
>  
> +The following example execute a chroot into the directory /chroot/powerpc/jessie and install the interpreter /bin/qemu-ppc-static to execute the powerpc binaries.
> +If the interpreter is defined with the flag F, the interpreter is loaded before the chroot otherwise the interpreter is loaded from inside the chroot.
> +
> +....
> +$  unshare --map-root-user --fork --pid --load-interp=":qemu-ppc:M::\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-ppc-static:OCF" --root=/chroot/powerpc/jessie /bin/bash -l
> +....

As an uneducated reader, I am confused by the flags. Where is the 'F'
flag? Perhaps you could provide more explanation to make it easier for
readers to understand.

>  == AUTHORS
>  
>  mailto:dottedmag@dottedmag.net[Mikhail Gusarov],
> diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
> index d79aa1125955..f8e1141840ca 100644
> --- a/sys-utils/unshare.c
> +++ b/sys-utils/unshare.c
> @@ -725,6 +725,35 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser,
>  	exit(EXIT_SUCCESS);
>  }
>  
> +static int is_fixed(const char *interp)
> +{
> +	const char *flags;
> +
> +	flags = strrchr(interp, ':');
> +
> +	return strchr(flags, 'F') != NULL;
> +}
> +
> +static void load_interp(const char *binfmt_mnt, const char *interp)
> +{
> +	int dirfd, fd;
> +
> +	dirfd = open(binfmt_mnt, O_PATH | O_DIRECTORY);
> +	if (dirfd < 0)
> +		err(EXIT_FAILURE, _("cannot open %s"), binfmt_mnt);
> +
> +	fd = openat(dirfd, "register", O_WRONLY);
> +	if (fd < 0)
> +		err(EXIT_FAILURE, _("cannot open %s/register"), binfmt_mnt);
> +
> +	if (write_all(fd, interp, strlen(interp)))
> +		err(EXIT_FAILURE, _("write failed %s/register"), binfmt_mnt);
> +
> +	close(fd);
> +
> +	close(dirfd);
> +}
> +
>  static void __attribute__((__noreturn__)) usage(void)
>  {
>  	FILE *out = stdout;
> @@ -772,6 +801,7 @@ static void __attribute__((__noreturn__)) usage(void)
>  	fputs(_(" -G, --setgid <gid>        set gid in entered namespace\n"), out);
>  	fputs(_(" --monotonic <offset>      set clock monotonic offset (seconds) in time namespaces\n"), out);
>  	fputs(_(" --boottime <offset>       set clock boottime offset (seconds) in time namespaces\n"), out);
> +	fputs(_(" -l, --load-interp <file>  load binfmt definition in the namespace (implies --mount-binfmt)\n"), out);
>  
>  	fputs(USAGE_SEPARATOR, out);
>  	fprintf(out, USAGE_HELP_OPTIONS(27));
> @@ -830,6 +860,7 @@ int main(int argc, char *argv[])
>  		{ "wd",		   required_argument, NULL, 'w'		    },
>  		{ "monotonic",     required_argument, NULL, OPT_MONOTONIC   },
>  		{ "boottime",      required_argument, NULL, OPT_BOOTTIME    },
> +		{ "load-interp",   required_argument, NULL, 'l'		    },
>  		{ NULL, 0, NULL, 0 }
>  	};
>  
> @@ -846,6 +877,7 @@ int main(int argc, char *argv[])
>  	const char *newroot = NULL;
>  	const char *newdir = NULL;
>  	pid_t pid_bind = 0, pid_idmap = 0;
> +	const char *newinterp = NULL;
>  	pid_t pid = 0;
>  #ifdef UL_HAVE_PIDFD
>  	int fd_parent_pid = -1;
> @@ -868,7 +900,7 @@ int main(int argc, char *argv[])
>  	textdomain(PACKAGE);
>  	close_stdout_atexit();
>  
> -	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:c", longopts, NULL)) != -1) {
> +	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:cl:", longopts, NULL)) != -1) {
>  		switch (c) {
>  		case 'f':
>  			forkit = 1;
> @@ -1011,6 +1043,15 @@ int main(int argc, char *argv[])
>  			boottime = strtos64_or_err(optarg, _("failed to parse boottime offset"));
>  			force_boottime = 1;
>  			break;
> +		case 'l':
> +			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
> +			if (!binfmt_mnt) {
> +				if (!procmnt)
> +					procmnt = "/proc";
> +				binfmt_mnt = _PATH_PROC_BINFMT_MISC;
> +			}
> +			newinterp = optarg;
> +			break;
>  
>  		case 'h':
>  			usage();
> @@ -1165,6 +1206,13 @@ int main(int argc, char *argv[])
>  	if ((unshare_flags & CLONE_NEWNS) && propagation)
>  		set_propagation(propagation);
>  
> +	if (newinterp && is_fixed(newinterp)) {
> +		if (mount("binfmt_misc", _PATH_PROC_BINFMT_MISC, "binfmt_misc",
> +			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
> +			err(EXIT_FAILURE, _("mount %s failed"), _PATH_PROC_BINFMT_MISC);
> +		load_interp(_PATH_PROC_BINFMT_MISC, newinterp);
> +	}

If I understand correctly, using --load-interp with 'F' calls
mount(binfmt_misc) twice:

1) before chroot
2) after chroot() and after mount(/proc) (implies --mount-binfmt and
   --mount-proc too)

I believe it would be helpful to include this information in the man
page.

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH] mount, losetup: Document loop destroy behavior
From: Karel Zak @ 2024-06-17 12:54 UTC (permalink / raw)
  To: Stanislav Brabec; +Cc: util-linux@vger.kernel.org
In-Reply-To: <8c649c26-93de-4edb-8dec-c87c243ee45d@suse.cz>

On Tue, Jun 11, 2024 at 02:15:36AM +0200, Stanislav Brabec wrote:
> The loop device detach runs asynchronously in the background. It can cause
> unexpected errors. Document the possible existence of the loop device after
> exit of losetup -d or umount and need of "udevadm settle".
> 
> Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
> ---
>  sys-utils/losetup.8.adoc | 2 +-
>  sys-utils/umount.8.adoc  | 2 ++
>  2 files changed, 3 insertions(+), 1 deletion(-)

It failed to apply the patch. Did you send it through git-send-email?

    Karel

Warning: Patch sent with format=flowed; space at the end of lines might be lost.
Applying: mount, losetup: Document loop destroy behavior
error: corrupt patch at line 11
Patch failed at 0001 mount, losetup: Document loop destroy behavior

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH] mount, losetup: Document loop destroy behavior
From: Stanislav Brabec @ 2024-06-12 10:04 UTC (permalink / raw)
  To: util-linux@vger.kernel.org
In-Reply-To: <8c649c26-93de-4edb-8dec-c87c243ee45d@suse.cz>

It should be [PATCH] umount, losetup: Document loop destroy behavior.

-- 
Best Regards / S pozdravem,

Stanislav Brabec
software developer
---------------------------------------------------------------------
SUSE LINUX, s. r. o.                         e-mail: sbrabec@suse.com
Křižíkova 148/34 (Corso IIa)                    tel: +420 284 084 060
186 00 Praha 8-Karlín                          fax:  +420 284 084 001
Czech Republic                                    http://www.suse.cz/
PGP: 830B 40D5 9E05 35D8 5E27 6FA3 717C 209F A04F CD76


^ permalink raw reply

* [PATCH v2 2/2] unshare: load binfmt_misc interpreter
From: Laurent Vivier @ 2024-06-11  8:43 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier
In-Reply-To: <20240611084314.183913-1-laurent@vivier.eu>

add -l, --load-interp <file> to load a binfmt_misc interpreter at startup.

The interpreter is loaded from the initial fileystem if the 'F' flags is
provided, otherwise from inside the new namespace
This makes possible to start a chroot of another architecture without
being root.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---

Notes:
    v2:
      - use <binfmt_mnt>/register rather than _PATH_PROC_BINFMT_MISC_REGISTER
        to load the interpreter

 sys-utils/unshare.1.adoc | 10 ++++++++
 sys-utils/unshare.c      | 52 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index 48d1a5579282..24ac6fb01867 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -138,6 +138,9 @@ Set the user ID which will be used in the entered namespace.
 *-G*, *--setgid* _gid_::
 Set the group ID which will be used in the entered namespace and drop supplementary groups.
 
+*-l*, **--load-interp=**__file__::
+Load binfmt_misc definition in the namespace (implies *--mount-binfmt*).
+
 *--monotonic* _offset_::
 Set the offset of *CLOCK_MONOTONIC* which will be used in the entered time namespace. This option requires unsharing a time namespace with *--time*.
 
@@ -256,6 +259,13 @@ up 21 hours, 30 minutes
 up 9 years, 28 weeks, 1 day, 2 hours, 50 minutes
 ....
 
+The following example execute a chroot into the directory /chroot/powerpc/jessie and install the interpreter /bin/qemu-ppc-static to execute the powerpc binaries.
+If the interpreter is defined with the flag F, the interpreter is loaded before the chroot otherwise the interpreter is loaded from inside the chroot.
+
+....
+$  unshare --map-root-user --fork --pid --load-interp=":qemu-ppc:M::\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-ppc-static:OCF" --root=/chroot/powerpc/jessie /bin/bash -l
+....
+
 == AUTHORS
 
 mailto:dottedmag@dottedmag.net[Mikhail Gusarov],
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index d79aa1125955..f8e1141840ca 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -725,6 +725,35 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser,
 	exit(EXIT_SUCCESS);
 }
 
+static int is_fixed(const char *interp)
+{
+	const char *flags;
+
+	flags = strrchr(interp, ':');
+
+	return strchr(flags, 'F') != NULL;
+}
+
+static void load_interp(const char *binfmt_mnt, const char *interp)
+{
+	int dirfd, fd;
+
+	dirfd = open(binfmt_mnt, O_PATH | O_DIRECTORY);
+	if (dirfd < 0)
+		err(EXIT_FAILURE, _("cannot open %s"), binfmt_mnt);
+
+	fd = openat(dirfd, "register", O_WRONLY);
+	if (fd < 0)
+		err(EXIT_FAILURE, _("cannot open %s/register"), binfmt_mnt);
+
+	if (write_all(fd, interp, strlen(interp)))
+		err(EXIT_FAILURE, _("write failed %s/register"), binfmt_mnt);
+
+	close(fd);
+
+	close(dirfd);
+}
+
 static void __attribute__((__noreturn__)) usage(void)
 {
 	FILE *out = stdout;
@@ -772,6 +801,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" -G, --setgid <gid>        set gid in entered namespace\n"), out);
 	fputs(_(" --monotonic <offset>      set clock monotonic offset (seconds) in time namespaces\n"), out);
 	fputs(_(" --boottime <offset>       set clock boottime offset (seconds) in time namespaces\n"), out);
+	fputs(_(" -l, --load-interp <file>  load binfmt definition in the namespace (implies --mount-binfmt)\n"), out);
 
 	fputs(USAGE_SEPARATOR, out);
 	fprintf(out, USAGE_HELP_OPTIONS(27));
@@ -830,6 +860,7 @@ int main(int argc, char *argv[])
 		{ "wd",		   required_argument, NULL, 'w'		    },
 		{ "monotonic",     required_argument, NULL, OPT_MONOTONIC   },
 		{ "boottime",      required_argument, NULL, OPT_BOOTTIME    },
+		{ "load-interp",   required_argument, NULL, 'l'		    },
 		{ NULL, 0, NULL, 0 }
 	};
 
@@ -846,6 +877,7 @@ int main(int argc, char *argv[])
 	const char *newroot = NULL;
 	const char *newdir = NULL;
 	pid_t pid_bind = 0, pid_idmap = 0;
+	const char *newinterp = NULL;
 	pid_t pid = 0;
 #ifdef UL_HAVE_PIDFD
 	int fd_parent_pid = -1;
@@ -868,7 +900,7 @@ int main(int argc, char *argv[])
 	textdomain(PACKAGE);
 	close_stdout_atexit();
 
-	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:c", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:cl:", longopts, NULL)) != -1) {
 		switch (c) {
 		case 'f':
 			forkit = 1;
@@ -1011,6 +1043,15 @@ int main(int argc, char *argv[])
 			boottime = strtos64_or_err(optarg, _("failed to parse boottime offset"));
 			force_boottime = 1;
 			break;
+		case 'l':
+			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
+			if (!binfmt_mnt) {
+				if (!procmnt)
+					procmnt = "/proc";
+				binfmt_mnt = _PATH_PROC_BINFMT_MISC;
+			}
+			newinterp = optarg;
+			break;
 
 		case 'h':
 			usage();
@@ -1165,6 +1206,13 @@ int main(int argc, char *argv[])
 	if ((unshare_flags & CLONE_NEWNS) && propagation)
 		set_propagation(propagation);
 
+	if (newinterp && is_fixed(newinterp)) {
+		if (mount("binfmt_misc", _PATH_PROC_BINFMT_MISC, "binfmt_misc",
+			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
+			err(EXIT_FAILURE, _("mount %s failed"), _PATH_PROC_BINFMT_MISC);
+		load_interp(_PATH_PROC_BINFMT_MISC, newinterp);
+	}
+
 	if (newroot) {
 		if (chroot(newroot) != 0)
 			err(EXIT_FAILURE,
@@ -1196,6 +1244,8 @@ int main(int argc, char *argv[])
 			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
 			err(EXIT_FAILURE, _("mount %s failed"), binfmt_mnt);
 	}
+	if (newinterp && !is_fixed(newinterp))
+		load_interp(binfmt_mnt, newinterp);
 
 	if (force_gid) {
 		if (setgroups(0, NULL) != 0)	/* drop supplementary groups */
-- 
2.45.2


^ permalink raw reply related

* [PATCH v2 1/2] unshare: mount binfmt_misc
From: Laurent Vivier @ 2024-06-11  8:43 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier
In-Reply-To: <20240611084314.183913-1-laurent@vivier.eu>

add --mount-binfmt[=<dir>] to mount binfmt_misc filesystem,
this results in clearing inherited interpreters from the previous namespace

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---

Notes:
    v2:
      - remove definition of _PATH_PROC_BINFMT_MISC_REGISTER

 include/pathnames.h      |  1 +
 sys-utils/unshare.1.adoc |  3 +++
 sys-utils/unshare.c      | 19 +++++++++++++++++++
 3 files changed, 23 insertions(+)

diff --git a/include/pathnames.h b/include/pathnames.h
index 81fa405f63c7..569bef17f982 100644
--- a/include/pathnames.h
+++ b/include/pathnames.h
@@ -204,6 +204,7 @@
 /* sysctl fs paths */
 #define _PATH_PROC_SYS_FS	"/proc/sys/fs"
 #define _PATH_PROC_PIPE_MAX_SIZE	_PATH_PROC_SYS_FS "/pipe-max-size"
+#define _PATH_PROC_BINFMT_MISC	_PATH_PROC_SYS_FS "/binfmt_misc"
 
 /* irqtop paths */
 #define _PATH_PROC_INTERRUPTS	"/proc/interrupts"
diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index e6201e28fffd..48d1a5579282 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -90,6 +90,9 @@ When *unshare* terminates, have _signame_ be sent to the forked child process. C
 *--mount-proc*[**=**__mountpoint__]::
 Just before running the program, mount the proc filesystem at _mountpoint_ (default is _/proc_). This is useful when creating a new PID namespace. It also implies creating a new mount namespace since the _/proc_ mount would otherwise mess up existing programs on the system. The new proc filesystem is explicitly mounted as private (with *MS_PRIVATE*|*MS_REC*).
 
+*--mount-binfmt*[**=**__mountpoint__]::
+Just before running the program, mount the binfmt_misc filesystem at _mountpoint_ (default is /proc/sys/fs/binfmt_misc).  It also implies creating a new mount namespace since the binfmt_misc mount would otherwise mess up existing programs on the system.  The new binfmt_misc filesystem is explicitly mounted as private (with *MS_PRIVATE*|*MS_REC*).
+
 **--map-user=**__uid|name__::
 Run the program only after the current effective user ID has been mapped to _uid_. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--user*.
 
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 57f3b8744fb5..d79aa1125955 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -760,6 +760,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" --kill-child[=<signame>]  when dying, kill the forked child (implies --fork)\n"
 		"                             defaults to SIGKILL\n"), out);
 	fputs(_(" --mount-proc[=<dir>]      mount proc filesystem first (implies --mount)\n"), out);
+	fputs(_(" --mount-binfmt[=<dir>]    mount binfmt filesystem first (implies --user and --mount)\n"), out);
 	fputs(_(" --propagation slave|shared|private|unchanged\n"
 	        "                           modify mount propagation in mount namespace\n"), out);
 	fputs(_(" --setgroups allow|deny    control the setgroups syscall in user namespaces\n"), out);
@@ -783,6 +784,7 @@ int main(int argc, char *argv[])
 {
 	enum {
 		OPT_MOUNTPROC = CHAR_MAX + 1,
+		OPT_MOUNTBINFMT,
 		OPT_PROPAGATION,
 		OPT_SETGROUPS,
 		OPT_KILLCHILD,
@@ -811,6 +813,7 @@ int main(int argc, char *argv[])
 		{ "fork",          no_argument,       NULL, 'f'             },
 		{ "kill-child",    optional_argument, NULL, OPT_KILLCHILD   },
 		{ "mount-proc",    optional_argument, NULL, OPT_MOUNTPROC   },
+		{ "mount-binfmt",  optional_argument, NULL, OPT_MOUNTBINFMT },
 		{ "map-user",      required_argument, NULL, OPT_MAPUSER     },
 		{ "map-users",     required_argument, NULL, OPT_MAPUSERS    },
 		{ "map-group",     required_argument, NULL, OPT_MAPGROUP    },
@@ -839,6 +842,7 @@ int main(int argc, char *argv[])
 	struct map_range *groupmap = NULL;
 	int kill_child_signo = 0; /* 0 means --kill-child was not used */
 	const char *procmnt = NULL;
+	const char *binfmt_mnt = NULL;
 	const char *newroot = NULL;
 	const char *newdir = NULL;
 	pid_t pid_bind = 0, pid_idmap = 0;
@@ -913,6 +917,15 @@ int main(int argc, char *argv[])
 			unshare_flags |= CLONE_NEWNS;
 			procmnt = optarg ? optarg : "/proc";
 			break;
+		case OPT_MOUNTBINFMT:
+			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
+			binfmt_mnt = optarg;
+			if (!binfmt_mnt) {
+				if (!procmnt)
+					procmnt = "/proc";
+				binfmt_mnt = _PATH_PROC_BINFMT_MISC;
+			}
+			break;
 		case OPT_MAPUSER:
 			unshare_flags |= CLONE_NEWUSER;
 			mapuser = get_user(optarg, _("failed to parse uid"));
@@ -1178,6 +1191,12 @@ int main(int argc, char *argv[])
 			err(EXIT_FAILURE, _("mount %s failed"), procmnt);
 	}
 
+	if (binfmt_mnt) {
+		if (mount("binfmt_misc", binfmt_mnt, "binfmt_misc",
+			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
+			err(EXIT_FAILURE, _("mount %s failed"), binfmt_mnt);
+	}
+
 	if (force_gid) {
 		if (setgroups(0, NULL) != 0)	/* drop supplementary groups */
 			err(EXIT_FAILURE, _("setgroups failed"));
-- 
2.45.2


^ permalink raw reply related

* [PATCH v2 0/2] unshare: manage binfmt_misc mounts
From: Laurent Vivier @ 2024-06-11  8:43 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier

Since linux v6.7 and
commit 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts"),
binfmt_misc can be mountable in a non-initial user namespace by
a non privileged user.

Extend unshare to manage it:

- add --mount-binfmt[=<dir>] to mount binfmt_misc filesystem, this
  results in clearing inherited interpreters from the previous namespace

- add -l, --load-interp <file> to load a binfmt_misc interpreter at startup.

  The interpreter is loaded from the initial fileystem if the 'F' flags is
  provided, otherwise from inside the new namespace
  This makes possible to start a chroot of another architecture without
  being root.

For instance:

  With 'F' flag, load the interpreter from the initial namespace:

    $ /bin/qemu-m68k-static --version
    qemu-m68k version 8.2.2 (qemu-8.2.2-1.fc40)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers
    $ unshare --map-root-user --fork --pid --load-interp=":qemu-m68k:M::\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x04:\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-m68k-static:OCF" --root=chroot/m68k/sid
    # QEMU_VERSION= ls
    qemu-m68k version 8.2.2 (qemu-8.2.2-1.fc40)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers
    # /qemu-m68k  --version
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

  Without 'F' flag, from inside the namespace:

    $ unshare --map-root-user --fork --pid --load-interp=":qemu-m68k:M::\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x04:\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/qemu-m68k:OC" --root=chroot/m68k/sid
    # QEMU_VERSION= ls
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
    # /qemu-m68k  --version
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

v2:
  - use <binfmt_mnt>/register rather than _PATH_PROC_BINFMT_MISC_REGISTER to load the interpreter

Laurent Vivier (2):
  unshare: mount binfmt_misc
  unshare: load binfmt_misc interpreter

 include/pathnames.h      |  1 +
 sys-utils/unshare.1.adoc | 13 ++++++++
 sys-utils/unshare.c      | 71 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 84 insertions(+), 1 deletion(-)

-- 
2.45.2


^ permalink raw reply

* [PATCH] mount, losetup: Document loop destroy behavior
From: Stanislav Brabec @ 2024-06-11  0:15 UTC (permalink / raw)
  To: util-linux@vger.kernel.org

The loop device detach runs asynchronously in the background. It can cause
unexpected errors. Document the possible existence of the loop device after
exit of losetup -d or umount and need of "udevadm settle".

Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
---
  sys-utils/losetup.8.adoc | 2 +-
  sys-utils/umount.8.adoc  | 2 ++
  2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/sys-utils/losetup.8.adoc b/sys-utils/losetup.8.adoc
index 9ea9451ce..e22e650b8 100644
--- a/sys-utils/losetup.8.adoc
+++ b/sys-utils/losetup.8.adoc
@@ -54,7 +54,7 @@ The _size_ and _offset_ arguments may be followed by 
the multiplicative suffixes
  Show the status of all loop devices. Note that not all information is 
accessible for non-root users. See also *--list*. The old output format 
(as printed without *--list*) is deprecated.

  *-d*, *--detach* _loopdev_...::
-Detach the file or device associated with the specified loop device(s). 
Note that since Linux v3.7 kernel uses "lazy device destruction". The 
detach operation does not return *EBUSY* error anymore if device is 
actively used by system, but it is marked by autoclear flag and 
destroyed later.
+Detach the file or device associated with the specified loop device(s). 
Note that since Linux v3.7 kernel uses "lazy device destruction". The 
detach operation does not return *EBUSY* error anymore if device is 
actively used by system, but it is marked by autoclear flag and 
destroyed later. Even if the device is not used, the loop device can be 
destroyed later. If you need to wait for a complete removal of the loop 
device, call *udevadm settle* after *losetup*.

  *-D*, *--detach-all*::
  Detach all associated loop devices.
diff --git a/sys-utils/umount.8.adoc b/sys-utils/umount.8.adoc
index f9b96e1ff..b9a3ff963 100644
--- a/sys-utils/umount.8.adoc
+++ b/sys-utils/umount.8.adoc
@@ -127,6 +127,8 @@ The *umount* command will automatically detach loop 
device previously initialize

  In this case the device is initialized with "autoclear" flag (see 
*losetup*(8) output for more details), otherwise it's necessary to use 
the option *--detach-loop* or call *losetup -d* _device_. The autoclear 
feature is supported since Linux 2.6.25.

+Note that since Linux v3.7 kernel uses "lazy device destruction". The 
system just marks the loop device by autoclear flag and destroys it 
later. If you need to wait for a complete removal of the loop device, 
call *udevadm settle* after *umount*.
+
  == EXIT STATUS

  *umount* has the following exit status values (the bits can be ORed):
-- 
2.45.0

-- 
Best Regards / S pozdravem,

Stanislav Brabec
software developer
---------------------------------------------------------------------
SUSE LINUX, s. r. o.                         e-mail: sbrabec@suse.com
Křižíkova 148/34 (Corso IIa)                    tel: +420 284 084 060
186 00 Praha 8-Karlín                          fax:  +420 284 084 001
Czech Republic                                    http://www.suse.cz/
PGP: 830B 40D5 9E05 35D8 5E27 6FA3 717C 209F A04F CD76


^ permalink raw reply related

* [PATCH 2/2] unshare: load binfmt_misc interpreter
From: Laurent Vivier @ 2024-06-10 17:33 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier
In-Reply-To: <20240610173349.115913-1-laurent@vivier.eu>

add -l, --load-interp <file> to load a binfmt_misc interpreter at startup.

The interpreter is loaded from the initial fileystem if the 'F' flags is
provided, otherwise from inside the new namespace
This makes possible to start a chroot of another architecture without
being root.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 sys-utils/unshare.1.adoc | 10 +++++++++
 sys-utils/unshare.c      | 48 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index 48d1a5579282..24ac6fb01867 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -138,6 +138,9 @@ Set the user ID which will be used in the entered namespace.
 *-G*, *--setgid* _gid_::
 Set the group ID which will be used in the entered namespace and drop supplementary groups.
 
+*-l*, **--load-interp=**__file__::
+Load binfmt_misc definition in the namespace (implies *--mount-binfmt*).
+
 *--monotonic* _offset_::
 Set the offset of *CLOCK_MONOTONIC* which will be used in the entered time namespace. This option requires unsharing a time namespace with *--time*.
 
@@ -256,6 +259,13 @@ up 21 hours, 30 minutes
 up 9 years, 28 weeks, 1 day, 2 hours, 50 minutes
 ....
 
+The following example execute a chroot into the directory /chroot/powerpc/jessie and install the interpreter /bin/qemu-ppc-static to execute the powerpc binaries.
+If the interpreter is defined with the flag F, the interpreter is loaded before the chroot otherwise the interpreter is loaded from inside the chroot.
+
+....
+$  unshare --map-root-user --fork --pid --load-interp=":qemu-ppc:M::\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-ppc-static:OCF" --root=/chroot/powerpc/jessie /bin/bash -l
+....
+
 == AUTHORS
 
 mailto:dottedmag@dottedmag.net[Mikhail Gusarov],
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 06a9a427c524..7b7b24138056 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -725,6 +725,31 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser,
 	exit(EXIT_SUCCESS);
 }
 
+static int is_fixed(const char *interp)
+{
+	const char *flags;
+
+	flags = strrchr(interp, ':');
+
+	return strchr(flags, 'F') != NULL;
+}
+
+static void load_interp(const char *interp)
+{
+	int fd;
+
+	fd = open(_PATH_PROC_BINFMT_MISC_REGISTER, O_WRONLY);
+	if (fd < 0)
+		err(EXIT_FAILURE, _("cannot open %s"),
+		    _PATH_PROC_BINFMT_MISC_REGISTER);
+
+	if (write_all(fd, interp, strlen(interp)))
+		err(EXIT_FAILURE, _("write failed %s"),
+		    _PATH_PROC_BINFMT_MISC_REGISTER);
+
+	close(fd);
+}
+
 static void __attribute__((__noreturn__)) usage(void)
 {
 	FILE *out = stdout;
@@ -772,6 +797,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" -G, --setgid <gid>        set gid in entered namespace\n"), out);
 	fputs(_(" --monotonic <offset>      set clock monotonic offset (seconds) in time namespaces\n"), out);
 	fputs(_(" --boottime <offset>       set clock boottime offset (seconds) in time namespaces\n"), out);
+	fputs(_(" -l, --load-interp <file>  load binfmt definition in the namespace (implies --mount-binfmt)\n"), out);
 
 	fputs(USAGE_SEPARATOR, out);
 	fprintf(out, USAGE_HELP_OPTIONS(27));
@@ -830,6 +856,7 @@ int main(int argc, char *argv[])
 		{ "wd",		   required_argument, NULL, 'w'		    },
 		{ "monotonic",     required_argument, NULL, OPT_MONOTONIC   },
 		{ "boottime",      required_argument, NULL, OPT_BOOTTIME    },
+		{ "load-interp",   required_argument, NULL, 'l'		    },
 		{ NULL, 0, NULL, 0 }
 	};
 
@@ -846,6 +873,7 @@ int main(int argc, char *argv[])
 	const char *newroot = NULL;
 	const char *newdir = NULL;
 	pid_t pid_bind = 0, pid_idmap = 0;
+	const char *newinterp = NULL;
 	pid_t pid = 0;
 #ifdef UL_HAVE_PIDFD
 	int fd_parent_pid = -1;
@@ -868,7 +896,7 @@ int main(int argc, char *argv[])
 	textdomain(PACKAGE);
 	close_stdout_atexit();
 
-	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:c", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:cl:", longopts, NULL)) != -1) {
 		switch (c) {
 		case 'f':
 			forkit = 1;
@@ -1008,6 +1036,15 @@ int main(int argc, char *argv[])
 			boottime = strtos64_or_err(optarg, _("failed to parse boottime offset"));
 			force_boottime = 1;
 			break;
+		case 'l':
+			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
+			if (!binfmt_mnt) {
+				if (!procmnt)
+					procmnt = "/proc";
+				binfmt_mnt = _PATH_PROC_BINFMT_MISC;
+			}
+			newinterp = optarg;
+			break;
 
 		case 'h':
 			usage();
@@ -1162,6 +1199,13 @@ int main(int argc, char *argv[])
 	if ((unshare_flags & CLONE_NEWNS) && propagation)
 		set_propagation(propagation);
 
+	if (newinterp && is_fixed(newinterp)) {
+		if (mount("binfmt_misc", _PATH_PROC_BINFMT_MISC, "binfmt_misc",
+			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
+			err(EXIT_FAILURE, _("mount %s failed"), _PATH_PROC_BINFMT_MISC);
+		load_interp(newinterp);
+	}
+
 	if (newroot) {
 		if (chroot(newroot) != 0)
 			err(EXIT_FAILURE,
@@ -1193,6 +1237,8 @@ int main(int argc, char *argv[])
 			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
 			err(EXIT_FAILURE, _("mount %s failed"), binfmt_mnt);
 	}
+	if (newinterp && !is_fixed(newinterp))
+		load_interp(newinterp);
 
 	if (force_gid) {
 		if (setgroups(0, NULL) != 0)	/* drop supplementary groups */
-- 
2.45.2


^ permalink raw reply related

* [PATCH 1/2] unshare: mount binfmt_misc
From: Laurent Vivier @ 2024-06-10 17:33 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier
In-Reply-To: <20240610173349.115913-1-laurent@vivier.eu>

add --mount-binfmt[=<dir>] to mount binfmt_misc filesystem,
this results in clearing inherited interpreters from the previous namespace

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 include/pathnames.h      |  2 ++
 sys-utils/unshare.1.adoc |  3 +++
 sys-utils/unshare.c      | 16 ++++++++++++++++
 3 files changed, 21 insertions(+)

diff --git a/include/pathnames.h b/include/pathnames.h
index 81fa405f63c7..6a7f506951d5 100644
--- a/include/pathnames.h
+++ b/include/pathnames.h
@@ -204,6 +204,8 @@
 /* sysctl fs paths */
 #define _PATH_PROC_SYS_FS	"/proc/sys/fs"
 #define _PATH_PROC_PIPE_MAX_SIZE	_PATH_PROC_SYS_FS "/pipe-max-size"
+#define _PATH_PROC_BINFMT_MISC	_PATH_PROC_SYS_FS "/binfmt_misc"
+#define _PATH_PROC_BINFMT_MISC_REGISTER	_PATH_PROC_BINFMT_MISC "/register"
 
 /* irqtop paths */
 #define _PATH_PROC_INTERRUPTS	"/proc/interrupts"
diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index e6201e28fffd..48d1a5579282 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -90,6 +90,9 @@ When *unshare* terminates, have _signame_ be sent to the forked child process. C
 *--mount-proc*[**=**__mountpoint__]::
 Just before running the program, mount the proc filesystem at _mountpoint_ (default is _/proc_). This is useful when creating a new PID namespace. It also implies creating a new mount namespace since the _/proc_ mount would otherwise mess up existing programs on the system. The new proc filesystem is explicitly mounted as private (with *MS_PRIVATE*|*MS_REC*).
 
+*--mount-binfmt*[**=**__mountpoint__]::
+Just before running the program, mount the binfmt_misc filesystem at _mountpoint_ (default is /proc/sys/fs/binfmt_misc).  It also implies creating a new mount namespace since the binfmt_misc mount would otherwise mess up existing programs on the system.  The new binfmt_misc filesystem is explicitly mounted as private (with *MS_PRIVATE*|*MS_REC*).
+
 **--map-user=**__uid|name__::
 Run the program only after the current effective user ID has been mapped to _uid_. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--user*.
 
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 57f3b8744fb5..06a9a427c524 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -760,6 +760,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" --kill-child[=<signame>]  when dying, kill the forked child (implies --fork)\n"
 		"                             defaults to SIGKILL\n"), out);
 	fputs(_(" --mount-proc[=<dir>]      mount proc filesystem first (implies --mount)\n"), out);
+	fputs(_(" --mount-binfmt[=<dir>]    mount binfmt filesystem first (implies --user and --mount)\n"), out);
 	fputs(_(" --propagation slave|shared|private|unchanged\n"
 	        "                           modify mount propagation in mount namespace\n"), out);
 	fputs(_(" --setgroups allow|deny    control the setgroups syscall in user namespaces\n"), out);
@@ -783,6 +784,7 @@ int main(int argc, char *argv[])
 {
 	enum {
 		OPT_MOUNTPROC = CHAR_MAX + 1,
+		OPT_MOUNTBINFMT,
 		OPT_PROPAGATION,
 		OPT_SETGROUPS,
 		OPT_KILLCHILD,
@@ -811,6 +813,7 @@ int main(int argc, char *argv[])
 		{ "fork",          no_argument,       NULL, 'f'             },
 		{ "kill-child",    optional_argument, NULL, OPT_KILLCHILD   },
 		{ "mount-proc",    optional_argument, NULL, OPT_MOUNTPROC   },
+		{ "mount-binfmt",  optional_argument, NULL, OPT_MOUNTBINFMT },
 		{ "map-user",      required_argument, NULL, OPT_MAPUSER     },
 		{ "map-users",     required_argument, NULL, OPT_MAPUSERS    },
 		{ "map-group",     required_argument, NULL, OPT_MAPGROUP    },
@@ -839,6 +842,7 @@ int main(int argc, char *argv[])
 	struct map_range *groupmap = NULL;
 	int kill_child_signo = 0; /* 0 means --kill-child was not used */
 	const char *procmnt = NULL;
+	const char *binfmt_mnt = NULL;
 	const char *newroot = NULL;
 	const char *newdir = NULL;
 	pid_t pid_bind = 0, pid_idmap = 0;
@@ -913,6 +917,12 @@ int main(int argc, char *argv[])
 			unshare_flags |= CLONE_NEWNS;
 			procmnt = optarg ? optarg : "/proc";
 			break;
+		case OPT_MOUNTBINFMT:
+			unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
+			binfmt_mnt = optarg ? optarg : _PATH_PROC_BINFMT_MISC;
+			if (!optarg && !procmnt)
+				procmnt = "/proc";
+			break;
 		case OPT_MAPUSER:
 			unshare_flags |= CLONE_NEWUSER;
 			mapuser = get_user(optarg, _("failed to parse uid"));
@@ -1178,6 +1188,12 @@ int main(int argc, char *argv[])
 			err(EXIT_FAILURE, _("mount %s failed"), procmnt);
 	}
 
+	if (binfmt_mnt) {
+		if (mount("binfmt_misc", binfmt_mnt, "binfmt_misc",
+			  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
+			err(EXIT_FAILURE, _("mount %s failed"), binfmt_mnt);
+	}
+
 	if (force_gid) {
 		if (setgroups(0, NULL) != 0)	/* drop supplementary groups */
 			err(EXIT_FAILURE, _("setgroups failed"));
-- 
2.45.2


^ permalink raw reply related

* [PATCH 0/2] unshare: manage binfmt_misc mounts
From: Laurent Vivier @ 2024-06-10 17:33 UTC (permalink / raw)
  To: util-linux; +Cc: Laurent Vivier

Since linux v6.7 and
commit 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts"),
binfmt_misc can be mountable in a non-initial user namespace by
a non privileged user.

Extend unshare to manage it:

- add --mount-binfmt[=<dir>] to mount binfmt_misc filesystem, this
  results in clearing inherited interpreters from the previous namespace

- add -l, --load-interp <file> to load a binfmt_misc interpreter at startup.

  The interpreter is loaded from the initial fileystem if the 'F' flags is
  provided, otherwise from inside the new namespace
  This makes possible to start a chroot of another architecture without
  being root.

For instance:

  With 'F' flag, load the interpreter from the initial namespace:

    $ /bin/qemu-m68k-static --version
    qemu-m68k version 8.2.2 (qemu-8.2.2-1.fc40)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers
    $ unshare --map-root-user --fork --pid --load-interp=":qemu-m68k:M::\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x04:\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-m68k-static:OCF" --root=chroot/m68k/sid
    # QEMU_VERSION= ls
    qemu-m68k version 8.2.2 (qemu-8.2.2-1.fc40)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers
    # /qemu-m68k  --version
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

  Without 'F' flag, from inside the namespace:

    $ unshare --map-root-user --fork --pid --load-interp=":qemu-m68k:M::\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x04:\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/qemu-m68k:OC" --root=chroot/m68k/sid
    # QEMU_VERSION= ls
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
    # /qemu-m68k  --version
    qemu-m68k version 8.0.50 (v8.0.0-340-gb1cff5e2da95)
    Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

Laurent Vivier (2):
  unshare: mount binfmt_misc
  unshare: load binfmt_misc interpreter

 include/pathnames.h      |  2 ++
 sys-utils/unshare.1.adoc | 13 ++++++++
 sys-utils/unshare.c      | 64 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 78 insertions(+), 1 deletion(-)

-- 
2.45.2


^ permalink raw reply

* Re: [PATCH v1] Call prctl(2) with long integers, specify 5 arguments, and avoid casts
From: Karel Zak @ 2024-06-06  7:21 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: util-linux, Xi Ruoyao
In-Reply-To: <20240601093150.16912-1-alx@kernel.org>

On Sat, Jun 01, 2024 at 11:31:56AM +0200, Alejandro Colomar wrote:
> Since libc's prctl(2) wrapper is a variadic function, arguments must
> have the right width.  Otherwise, the behavior is undefined.

Created PR to test it by CI:
https://github.com/util-linux/util-linux/pull/3085

    Karel


-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH] libmount: provide tree fd even when a mount helper is used
From: Karel Zak @ 2024-06-03 11:20 UTC (permalink / raw)
  To: Linus Heckemann; +Cc: util-linux
In-Reply-To: <20240531094125.r2nvmio47itrtzep@ws.net.home>

On Fri, May 31, 2024 at 11:41:28AM +0200, Karel Zak wrote:
> On Thu, May 30, 2024 at 08:00:34PM +0200, Linus Heckemann wrote:
> > Previously, the X-mount.subdir option would fail (mount exited with
> > code 0, but the target was not mounted) when a helper was used.
> 
> I will think about it :-)

 https://github.com/util-linux/util-linux/pull/3075

 Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH v1] Call prctl(2) with long integers, specify 5 arguments, and avoid casts
From: Alejandro Colomar @ 2024-06-01 19:32 UTC (permalink / raw)
  To: Thomas Weißschuh; +Cc: util-linux, Xi Ruoyao, libc-alpha
In-Reply-To: <mbvtkssza7bvvu45wqivbfd2astvpsu3t7u223a222oizrqznm@rv4rxvvkxzct>

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

On Sat, Jun 01, 2024 at 02:24:03PM GMT, Alejandro Colomar wrote:
> Hi Thomas,
> 
> On Sat, Jun 01, 2024 at 01:05:02PM GMT, Thomas Weißschuh wrote:
> > On 2024-06-01 11:31:56+0000, Alejandro Colomar wrote:
> > > Since libc's prctl(2) wrapper is a variadic function, arguments must
> > > have the right width.  Otherwise, the behavior is undefined.
> > 
> > Ack.
> > 
> > > Also, the 5 arguments must be specified always, or the behavior is also
> > > undefined.  libc reads 5 values and passes them all to the kernel, so if
> > > one is uninitialized, the kernel will receive garbagge, which could
> > > result in EINVAL (most likely), or worse, a different action.
> > 
> > This seems surprising.
> > 
> > The kernel should only check the arguments it documents and not more.
> 
> Hmmm, some prctl(2) calls don't document a need for passing 0 (probably
> for legacy compatibility; you're right.  Only newer prctl(2)s check
> those args.
> 
> And see for example these kernel commit:
> 
> 	commit e9d1b4f3c60997fe197bf0243cb4a41a44387a88
> 	Author: Dave Hansen <dave.hansen@linux.intel.com>
> 	Date:   Thu Jan 8 14:30:22 2015 -0800
> 
> 	    x86, mpx: Strictly enforce empty prctl() args
> 	    
> 	    Description from Michael Kerrisk.  He suggested an identical patch
> 	    to one I had already coded up and tested.
> 	    
> 	    commit fe3d197f8431 "x86, mpx: On-demand kernel allocation of bounds
> 	    tables" added two new prctl() operations, PR_MPX_ENABLE_MANAGEMENT and
> 	    PR_MPX_DISABLE_MANAGEMENT.  However, no checks were included to ensure
> 	    that unused arguments are zero, as is done in many existing prctl()s
> 	    and as should be done for all new prctl()s. This patch adds the
> 	    required checks.
> 	    
> 	    Suggested-by: Andy Lutomirski <luto@amacapital.net>
> 	    Suggested-by: Michael Kerrisk <mtk.manpages@gmail.com>
> 	    Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
> 	    Cc: Dave Hansen <dave@sr71.net>
> 	    Link: http://lkml.kernel.org/r/20150108223022.7F56FD13@viggo.jf.intel.com
> 	    Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> 
> 	diff --git a/kernel/sys.c b/kernel/sys.c
> 	index a8c9f5a7dda6..ea9c88109894 100644
> 	--- a/kernel/sys.c
> 	+++ b/kernel/sys.c
> 	@@ -2210,9 +2210,13 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
> 			up_write(&me->mm->mmap_sem);
> 			break;
> 		case PR_MPX_ENABLE_MANAGEMENT:
> 	+               if (arg2 || arg3 || arg4 || arg5)
> 	+                       return -EINVAL;
> 			error = MPX_ENABLE_MANAGEMENT(me);
> 			break;
> 		case PR_MPX_DISABLE_MANAGEMENT:
> 	+               if (arg2 || arg3 || arg4 || arg5)
> 	+                       return -EINVAL;
> 			error = MPX_DISABLE_MANAGEMENT(me);
> 			break;
> 		default:
> 
> And this one too:
> 
> 	commit 3e91ec89f527b9870fe42dcbdb74fd389d123a95
> 	Author: Catalin Marinas <catalin.marinas@arm.com>
> 	Date:   Thu Aug 15 16:44:00 2019 +0100
> 
> 	    arm64: Tighten the PR_{SET, GET}_TAGGED_ADDR_CTRL prctl() unused arguments
> 	    
> 	    Require that arg{3,4,5} of the PR_{SET,GET}_TAGGED_ADDR_CTRL prctl and
> 	    arg2 of the PR_GET_TAGGED_ADDR_CTRL prctl() are zero rather than ignored
> 	    for future extensions.
> 	    
> 	    Acked-by: Andrey Konovalov <andreyknvl@google.com>
> 	    Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> 	    Signed-off-by: Will Deacon <will@kernel.org>
> 
> 	diff --git a/kernel/sys.c b/kernel/sys.c
> 	index c6c4d5358bd3..ec48396b4943 100644
> 	--- a/kernel/sys.c
> 	+++ b/kernel/sys.c
> 	@@ -2499,9 +2499,13 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
> 			error = PAC_RESET_KEYS(me, arg2);
> 			break;
> 		case PR_SET_TAGGED_ADDR_CTRL:
> 	+               if (arg3 || arg4 || arg5)
> 	+                       return -EINVAL;
> 			error = SET_TAGGED_ADDR_CTRL(arg2);
> 			break;
> 		case PR_GET_TAGGED_ADDR_CTRL:
> 	+               if (arg2 || arg3 || arg4 || arg5)
> 	+                       return -EINVAL;
> 			error = GET_TAGGED_ADDR_CTRL();
> 			break;
> 		default:
> 
> In the few calls that util-linux makes without specifying all 5 args,
> the kernel seems to not do the checks (in some old prctl(2)s they didn't
> have that check, and nobody seems to have cared enough to add it), so
> it's more like we're lucky (or unlucky, depending on how you see it).
> 
> > glibc itself doesn't even specify all five arguments in its own calls to
> > prctl().
> 
> glibc itself is wrong.  I'm even surprised that the PR_* macros from the
> kernel UAPI for arg2 work without specifying the L suffix on them, but
> it's probably just luck.
> 
> <https://lore.kernel.org/linux-api/20240528114750.106187-1-alx@kernel.org/T/#u>
> 
> > If all five arguments are really required then prctl() wouldn't need to
> > be variadic.
> 
> Indeed.  I guess that's for historic reasons, rather than actual
> necessity; but I don't know for sure.
> 
> > How is random non-zero data less valid than a essentially random zero?
> > And if the kernel actually validates this, how has it ever worked before?
> 
> They only added validation for (all) new prctl(2) calls, plus maybe some
> old ones, but not all.  In the ones used in util-linux that don't
> specify zero, I've checked now that the kernel doesn't validate.
> 
> However, a call such as
> 
> 	prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
> 
> (this call exists in util-linux)
> actually means
> 
> 	prctl(PR_SET_NO_NEW_PRIVS, 1L, 0L, random, random)
> 
> and it supposedly has been working so far.  Those random bits are
> probably 0 most of the time, for some reason.  And the kernel does check
> this one:
> 
> 	$ sed -n /PR_SET_NO_NEW_PRIVS/,+2p <kernel/sys.c
> 		case PR_SET_NO_NEW_PRIVS:
> 			if (arg2 != 1 || arg3 || arg4 || arg5)
> 				return -EINVAL;
> 
> > Other popular software like systemd or opendjk also don't specify unused arguments.
> 
> I've also checked that the ones that systemd uses without specifying all
> 5 args, they are not checked by the kernel.
> 
> > So it doesn't really seem "broken".
> > If the patch is more about "being on the safe side", then this should be
> > spelled out.
> 
> Still, libc reads those values (on x32) which results in Undefined
> Behavior inside glibc.  Which is a bad thing.  Not broken, because the
> compiler has little information to exploit that UB, but not a good thing
> either.
> 
> 	$ grepc __prctl .
> 	./include/sys/prctl.h:extern int __prctl (int __option, ...);
> 	./sysdeps/unix/sysv/linux/x86_64/x32/prctl.c:int
> 	__prctl (int option, ...)
> 	{
> 	  va_list arg;
> 	  va_start (arg, option);
> 	  unsigned long int arg2 = va_arg (arg, unsigned long int);
> 	  unsigned long int arg3 = va_arg (arg, unsigned long int);
> 	  unsigned long int arg4 = va_arg (arg, unsigned long int);
> 	  unsigned long int arg5 = va_arg (arg, unsigned long int);
> 	  va_end (arg);
> 	  return INLINE_SYSCALL_CALL (prctl, option, arg2, arg3, arg4, arg5);
> 	}

Or one could say this is not a user problem, and just an implementation
detail of libc.  For those calls that the kernel ignores the argument,
omitting the argument sounds reasonable as a user.  I guess the kernel
won't expand those APIs, since that would be dangerous (for this precise
reason).

> 
> It's arguably less broken than the missing 'L', though.
> 
> > (Plus the cases where documented, required arguments are missing)
> 
> None of the cases where we omit the arguments are checked by the kernel.

-- 
<https://www.alejandro-colomar.es/>

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

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox