@@ -4771,6 +4771,7 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path,
size_t, usize)
{
int err;
+ bool check_fields;
struct path target;
struct mount_attr attr;
struct mount_kattr kattr;
@@ -4783,11 +4784,27 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path,
AT_NO_AUTOMOUNT))
return -EINVAL;
+ check_fields = usize & CHECK_FIELDS;
+ usize &= ~CHECK_FIELDS;
+
if (unlikely(usize > PAGE_SIZE))
return -E2BIG;
if (unlikely(usize < MOUNT_ATTR_SIZE_VER0))
return -EINVAL;
+ if (unlikely(check_fields)) {
+ memset(&attr, 0, sizeof(attr));
+ attr = (struct mount_attr) {
+ .attr_set = MOUNT_SETATTR_VALID_FLAGS,
+ .attr_clr = MOUNT_SETATTR_VALID_FLAGS,
+ .propagation = MOUNT_SETATTR_PROPAGATION_FLAGS,
+ .userns_fd = 0xFFFFFFFFFFFFFFFF,
+ };
+
+ err = copy_struct_to_user(uattr, usize, &attr, sizeof(attr), NULL);
+ return err ?: -EEXTSYS_NOOP;
+ }
+
if (!may_mount())
return -EPERM;
As with openat2(2), this allows userspace to easily figure out what flags and fields are supported by mount_setattr(2). As with clone3(2), for fields which are not flag-based, we simply set every bit in the field so that a naive bitwise-and would show that any value of the field is valid. The intended way of using this interface to get feature information looks something like the following: static bool mountattr_nosymfollow_supported; static bool mountattr_idmap_supported; int check_clone3_support(void) { int err; struct mount_attr attr = {}; err = mount_attr(-EBADF, "", 0, &args, CHECK_FIELDS | sizeof(args)); assert(err < 0); switch (errno) { case EFAULT: case E2BIG: /* Old kernel... */ check_support_the_old_way(); break; case EEXTSYS_NOOP: mountattr_nosymfollow_supported = ((attr.attr_clr | attr.attr_set) & MOUNT_ATTR_NOSYMFOLLOW); mountattr_idmap_supported = ((attr.attr_clr | attr.attr_set) & MOUNT_ATTR_IDMAP) && (attr.userns_fd != 0); break; } } Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> --- fs/namespace.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)