In Ubuntu 22, How to set the 1st parameter of inode_permission(struct mnt_idmap*, struct inode *inode, int mask)

I maintains an old Linux kernel library that have an interface as below, when I upgraded it to Ubuntu 22.04, the 1st parameter of function inode_permission() changed to “struct mnt_idmap *”, it seems set the 1st parameter inode_permission() to NULL doesn’t’ work.

int test_is_executable(struct dentry *dentry)
{
    struct inode *inode = dentry->d_inode;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)) // Ubuntu 22.04
    //struct user_namespace *ns = current_user_ns();
    struct mnt_idmap * mnt_idmap = NULL;  // alloc_mnt_idmap(ns);
    return inode_permission(mnt_idmap, inode, MAY_EXEC | MAY_READ | MAY_OPEN);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))  // Ubuntu 20.04
    struct user_namespace *ns = current_user_ns();
    return inode_permission(ns, inode, MAY_EXEC | MAY_READ | MAY_OPEN);
#else
    //const struct cred *cred = current_cred();
    return inode_permission(inode, MAY_EXEC | MAY_READ | MAY_OPEN);
#endif
}

This is because the kernel security model has changed, and you need a proper mount ID mapping context.

Why does NULL no longer work?

  • In newer Linux kernel versions (6.8+), the inode_permission() function was updated to take struct mnt_idmap * as the first parameter instead of struct user_namespace *.
  • Passing NULL to this new mnt_idmap parameter does not provide a valid mapping context, which is why it fails.

Here’s how you can fix it:

int test_is_executable(struct dentry *dentry)
{
    struct inode *inode = dentry->d_inode;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)) // Ubuntu 22.04
    struct user_namespace *ns = current_user_ns();
    struct mnt_idmap *mnt_idmap = &nop_mnt_idmap;  // Use the no-op idmap
    return inode_permission(mnt_idmap, inode, MAY_EXEC | MAY_READ | MAY_OPEN);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))  // Ubuntu 20.04
    struct user_namespace *ns = current_user_ns();
    return inode_permission(ns, inode, MAY_EXEC | MAY_READ | MAY_OPEN);
#else
    return inode_permission(inode, MAY_EXEC | MAY_READ | MAY_OPEN);
#endif
}

Why use &nop_mnt_idmap?

  • &nop_mnt_idmap is a global kernel-provided structure that acts as a no-op ID mapper. It ensures a default 1:1 mapping (i.e., no ID translation) and is safe to use for most filesystem operations when specific ID mappings are not required.
  • This avoids the need to allocate or manage a custom mnt_idmap using alloc_mnt_idmap().

The reason behind this change is that in newer kernels, the ID mapping functionality was moved from user namespaces to a dedicated mount ID mapping structure to support features like idmapped mounts. Using &nop_mnt_idmap provides a default 1:1 mapping that should work for most basic permission checks.

You might also need to include:

#include <linux/mnt_idmapping.h>

If you need more specific ID mapping behavior, you could use alloc_mnt_idmap() as you commented out, but for most cases, the no-op mapper should be sufficient.

2 Likes

It works, thanks for your support, :+1:

1 Like