ANDROID: sdcardfs: Protect set_top
authorDaniel Rosenberg <drosen@google.com>
Fri, 2 Feb 2018 00:52:22 +0000 (16:52 -0800)
committerStricted <info@stricted.net>
Thu, 11 Oct 2018 16:03:47 +0000 (18:03 +0200)
If the top is changed while we're attempting to use it, it's
possible that the reference will be put while we are in the
process of grabbing a reference.

Now we grab a spinlock to protect grabbing our reference count.

Additionally, we now set the inode_info's top value to point to
it's own data when initializing, which makes tracking changes
easier.

Change-Id: If15748c786ce4c0480ab8c5051a92523aff284d2
Signed-off-by: Daniel Rosenberg <drosen@google.com>
fs/sdcardfs/derived_perm.c
fs/sdcardfs/main.c
fs/sdcardfs/sdcardfs.h
fs/sdcardfs/super.c

index 75beeff149f0b784b67ddc1f40bbb14e944302f9..46af20db62c816587f55e0110f80d2c423ed568e 100644 (file)
@@ -32,23 +32,20 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
        ci->data->under_android = pi->data->under_android;
        ci->data->under_cache = pi->data->under_cache;
        ci->data->under_obb = pi->data->under_obb;
-       set_top(ci, pi->top_data);
 }
 
 /* helper function for derived state */
 void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
-                                       uid_t uid, bool under_android,
-                                       struct sdcardfs_inode_data *top)
+                                       uid_t uid)
 {
        struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
 
        info->data->perm = perm;
        info->data->userid = userid;
        info->data->d_uid = uid;
-       info->data->under_android = under_android;
+       info->data->under_android = false;
        info->data->under_cache = false;
        info->data->under_obb = false;
-       set_top(info, top);
 }
 
 /* While renaming, there is a point where we want the path from dentry,
@@ -58,8 +55,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
                                const struct qstr *name)
 {
        struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
-       struct sdcardfs_inode_data *parent_data =
-                       SDCARDFS_I(parent->d_inode)->data;
+       struct sdcardfs_inode_info *parent_info = SDCARDFS_I(parent->d_inode);
+       struct sdcardfs_inode_data *parent_data = parent_info->data;
        appid_t appid;
        unsigned long user_num;
        int err;
@@ -80,13 +77,15 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
        inherit_derived_state(parent->d_inode, dentry->d_inode);
 
        /* Files don't get special labels */
-       if (!S_ISDIR(dentry->d_inode->i_mode))
+       if (!S_ISDIR(dentry->d_inode->i_mode)) {
+               set_top(info, parent_info);
                return;
+       }
        /* Derive custom permissions based on parent and current node */
        switch (parent_data->perm) {
        case PERM_INHERIT:
        case PERM_ANDROID_PACKAGE_CACHE:
-               /* Already inherited above */
+               set_top(info, parent_info);
                break;
        case PERM_PRE_ROOT:
                /* Legacy internal layout places users at top level */
@@ -96,7 +95,6 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
                        info->data->userid = 0;
                else
                        info->data->userid = user_num;
-               set_top(info, info->data);
                break;
        case PERM_ROOT:
                /* Assume masked off by default. */
@@ -104,24 +102,24 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
                        /* App-specific directories inside; let anyone traverse */
                        info->data->perm = PERM_ANDROID;
                        info->data->under_android = true;
-                       set_top(info, info->data);
+               } else {
+                       set_top(info, parent_info);
                }
                break;
        case PERM_ANDROID:
                if (qstr_case_eq(name, &q_data)) {
                        /* App-specific directories inside; let anyone traverse */
                        info->data->perm = PERM_ANDROID_DATA;
-                       set_top(info, info->data);
                } else if (qstr_case_eq(name, &q_obb)) {
                        /* App-specific directories inside; let anyone traverse */
                        info->data->perm = PERM_ANDROID_OBB;
                        info->data->under_obb = true;
-                       set_top(info, info->data);
                        /* Single OBB directory is always shared */
                } else if (qstr_case_eq(name, &q_media)) {
                        /* App-specific directories inside; let anyone traverse */
                        info->data->perm = PERM_ANDROID_MEDIA;
-                       set_top(info, info->data);
+               } else {
+                       set_top(info, parent_info);
                }
                break;
        case PERM_ANDROID_OBB:
@@ -132,13 +130,13 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
                if (appid != 0 && !is_excluded(name->name, parent_data->userid))
                        info->data->d_uid =
                                multiuser_get_uid(parent_data->userid, appid);
-               set_top(info, info->data);
                break;
        case PERM_ANDROID_PACKAGE:
                if (qstr_case_eq(name, &q_cache)) {
                        info->data->perm = PERM_ANDROID_PACKAGE_CACHE;
                        info->data->under_cache = true;
                }
+               set_top(info, parent_info);
                break;
        }
 }
index f949fcd0ce928aad6fed1476df89b6faef492f01..512d8d0e57a4e3932ba9a261ecbe2d77be9ab87a 100644 (file)
@@ -348,13 +348,11 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
        mutex_lock(&sdcardfs_super_list_lock);
        if (sb_info->options.multiuser) {
                setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT,
-                               sb_info->options.fs_user_id, AID_ROOT,
-                               false, SDCARDFS_I(sb->s_root->d_inode)->data);
+                               sb_info->options.fs_user_id, AID_ROOT);
                snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
        } else {
                setup_derived_state(sb->s_root->d_inode, PERM_ROOT,
-                               sb_info->options.fs_user_id, AID_ROOT,
-                               false, SDCARDFS_I(sb->s_root->d_inode)->data);
+                               sb_info->options.fs_user_id, AID_ROOT);
                snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
        }
        fixup_tmp_permissions(sb->s_root->d_inode);
index 31c013d28cc4520f526e878a119026e0d5044f30..641b1ec59e62b230c47ef766b51778da6d4ad3ae 100644 (file)
@@ -200,6 +200,7 @@ struct sdcardfs_inode_info {
        struct sdcardfs_inode_data *data;
 
        /* top folder for ownership */
+       spinlock_t top_lock;
        struct sdcardfs_inode_data *top_data;
 
        struct inode vfs_inode;
@@ -379,7 +380,12 @@ static inline struct sdcardfs_inode_data *data_get(
 static inline struct sdcardfs_inode_data *top_data_get(
                struct sdcardfs_inode_info *info)
 {
-       return data_get(info->top_data);
+       struct sdcardfs_inode_data *top_data;
+
+       spin_lock(&info->top_lock);
+       top_data = data_get(info->top_data);
+       spin_unlock(&info->top_lock);
+       return top_data;
 }
 
 extern void data_release(struct kref *ref);
@@ -401,15 +407,20 @@ static inline void release_own_data(struct sdcardfs_inode_info *info)
 }
 
 static inline void set_top(struct sdcardfs_inode_info *info,
-                       struct sdcardfs_inode_data *top)
+                       struct sdcardfs_inode_info *top_owner)
 {
-       struct sdcardfs_inode_data *old_top = info->top_data;
+       struct sdcardfs_inode_data *old_top;
+       struct sdcardfs_inode_data *new_top = NULL;
+
+       if (top_owner)
+               new_top = top_data_get(top_owner);
 
-       if (top)
-               data_get(top);
-       info->top_data = top;
+       spin_lock(&info->top_lock);
+       old_top = info->top_data;
+       info->top_data = new_top;
        if (old_top)
                data_put(old_top);
+       spin_unlock(&info->top_lock);
 }
 
 static inline int get_gid(struct vfsmount *mnt,
@@ -515,8 +526,7 @@ struct limit_search {
 };
 
 extern void setup_derived_state(struct inode *inode, perm_t perm,
-               userid_t userid, uid_t uid, bool under_android,
-               struct sdcardfs_inode_data *top);
+                       userid_t userid, uid_t uid);
 extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
 extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
 extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
index 6885c3e8972eba96d8b9ebd3afef825d3f2cfe30..c9447dfdcdfb6836f9861a6f2cbba4450d661f4a 100644 (file)
@@ -215,6 +215,9 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
 
        i->data = d;
        kref_init(&d->refcount);
+       i->top_data = d;
+       spin_lock_init(&i->top_lock);
+       kref_get(&d->refcount);
 
        i->vfs_inode.i_version = 1;
        return &i->vfs_inode;