From: William Roberts <william.c.roberts@xxxxxxxxx> This corrects issues in client filesystems, like sysfs, where LSMs missed the opportunity to initialize inodes. In the case of SELinux this prevented newly created files from inheriting their parent's label. This has posed many issues on Android where transient sysfs files were defaulting to the sysfs wide label, rather than their parent, which is the normal behavior for other filesystems. Without this, access controls for these files had to be granted on the file system wide label, rather then a file specific label. The result of which over-privilege the caller but also can cause CTS failures. Signed-off-by: William Roberts <william.c.roberts@xxxxxxxxx> --- fs/kernfs/dir.c | 2 +- fs/kernfs/inode.c | 34 ++++++++++++++++++++++++++++++---- fs/kernfs/kernfs-internal.h | 26 +++++++++++++++++++++++++- fs/kernfs/mount.c | 2 +- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index fffca95..096379e 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -809,7 +809,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, dentry->d_fsdata = kn; /* attach dentry and inode */ - inode = kernfs_get_inode(dir->i_sb, kn); + inode = kernfs_get_inode(dir->i_sb, kn, dentry->d_parent); if (!inode) { ret = ERR_PTR(-ENOMEM); goto out_unlock; diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 2da8493..d37b8ff 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -281,8 +281,10 @@ int kernfs_iop_getattr(struct vfsmount *mnt, struct dentry *dentry, return 0; } -static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode) +static int kernfs_init_inode(struct kernfs_node *kn, struct inode *inode, + struct dentry *parent) { + int error = 0; kernfs_get(kn); inode->i_private = kn; inode->i_mapping->a_ops = &kernfs_aops; @@ -291,6 +293,8 @@ static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode) set_default_inode_attr(inode, kn->mode); kernfs_refresh_inode(kn, inode); + WARN_ON(kn->parent && !parent); + /* initialize inode according to type */ switch (kernfs_type(kn)) { case KERNFS_DIR: @@ -308,31 +312,53 @@ static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode) BUG(); } + if (parent) + error = security_inode_init_security(inode, parent->d_inode, + &parent->d_name, NULL, NULL); + unlock_new_inode(inode); + return error; } /** * kernfs_get_inode - get inode for kernfs_node * @sb: super block * @kn: kernfs_node to allocate inode for + * @parent:The parent dentry or NULL if root node * * Get inode for @kn. If such inode doesn't exist, a new inode is * allocated and basics are initialized. New inode is returned * locked. * + * WARN: If parent is null but @kn is not the root node. + * * LOCKING: * Kernel thread context (may sleep). * * RETURNS: * Pointer to allocated inode on success, NULL on failure. */ -struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn) +struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn, + struct dentry *parent) { + int error; struct inode *inode; inode = iget_locked(sb, kn->ino); - if (inode && (inode->i_state & I_NEW)) - kernfs_init_inode(kn, inode); + if (inode && (inode->i_state & I_NEW)) { + error = kernfs_init_inode(kn, inode, parent); + if (unlikely(error)) { + /* + * XXX + * What is the proper error sequence here? + * Should we kernfs_put() and iput() or + * just run the kernfs_evict_inode() + * routine. + */ + kernfs_evict_inode(inode); + return NULL; + } + } return inode; } diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index af9fa74..b2ead66 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -76,7 +76,8 @@ extern struct kmem_cache *kernfs_node_cache; /* * inode.c */ -struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn); +struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn, + struct dentry *parent); void kernfs_evict_inode(struct inode *inode); int kernfs_iop_permission(struct inode *inode, int mask); int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr); @@ -89,6 +90,29 @@ ssize_t kernfs_iop_getxattr(struct dentry *dentry, const char *name, void *buf, size_t size); ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size); +/** + * kernfs_get_root_inode - get root inode for kernfs_node + * @sb: super block + * @kn: kernfs_node to allocate inode for + * + * SEE: kernfs_get_inode() for more details + * + * WARN: if @kn is not the root node, ie has a parent. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * Pointer to allocated inode on success, NULL on failure. + */ +static inline struct inode *kernfs_get_root_inode(struct super_block *sb, + struct kernfs_node *kn) +{ + WARN_ON(kn->parent); + return kernfs_get_inode(sb, kn, NULL); +} + + /* * dir.c */ diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 8eaf417..930044f 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -77,7 +77,7 @@ static int kernfs_fill_super(struct super_block *sb, unsigned long magic) /* get root inode, initialize and unlock it */ mutex_lock(&kernfs_mutex); - inode = kernfs_get_inode(sb, info->root->kn); + inode = kernfs_get_root_inode(sb, info->root->kn); mutex_unlock(&kernfs_mutex); if (!inode) { pr_debug("kernfs: could not get root inode\n"); -- 1.9.1 _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.