RAVENPLAT-277: Kernel components sdcardfs - CVE-2018-9514 [1/1]

PD#SWPL-15901

Problem:
In sdcardfs_open of file.c, there is a possible Use After Free
due to an unusual root cause. This could lead to local escalation
of privilege with no additional execution privileges needed.
User interaction is not needed for exploitation.

Solution:
The fix is designed to avoid the OVERRIDE_CRED macro in favor
of more explicit control flow.

Platform:
Raven

Verify:
Raven

Change-Id: Idab016c33c2dfbd9425533ed5c5501b671677572
Signed-off-by: Hanjie Lin <hanjie.lin@amlogic.com>
This commit is contained in:
Hanjie Lin
2018-12-19 17:34:29 +08:00
committed by Luke Go
parent 01c5015da5
commit d5a35d151f
4 changed files with 66 additions and 190 deletions

View File

@@ -118,7 +118,11 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
goto out;
/* save current_cred and override it */
OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file)));
saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data);
if (!saved_cred) {
err = -ENOMEM;
goto out;
}
if (lower_file->f_op->unlocked_ioctl)
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
@@ -127,7 +131,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
if (!err)
sdcardfs_copy_and_fix_attrs(file_inode(file),
file_inode(lower_file));
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out:
return err;
}
@@ -149,12 +153,16 @@ static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd,
goto out;
/* save current_cred and override it */
OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file)));
saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data);
if (!saved_cred) {
err = -ENOMEM;
goto out;
}
if (lower_file->f_op->compat_ioctl)
err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out:
return err;
}
@@ -241,7 +249,11 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
}
/* save current_cred and override it */
OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode));
saved_cred = override_fsids(sbi, SDCARDFS_I(inode)->data);
if (!saved_cred) {
err = -ENOMEM;
goto out_err;
}
file->private_data =
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
@@ -271,7 +283,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
out_revert_cred:
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_err:
dput(parent);
return err;

View File

@@ -22,7 +22,6 @@
#include <linux/fs_struct.h>
#include <linux/ratelimit.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
struct sdcardfs_inode_data *data)
{
@@ -50,7 +49,6 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
return old_cred;
}
/* Do not directly use this function, use REVERT_CRED() instead. */
void revert_fsids(const struct cred *old_cred)
{
const struct cred *cur_cred;
@@ -78,7 +76,10 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
SDCARDFS_I(dir)->data);
if (!saved_cred)
return -ENOMEM;
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
@@ -119,53 +120,11 @@ out:
out_unlock:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_eacces:
return err;
}
#if 0
static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry)
{
struct dentry *lower_old_dentry;
struct dentry *lower_new_dentry;
struct dentry *lower_dir_dentry;
u64 file_size_save;
int err;
struct path lower_old_path, lower_new_path;
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
file_size_save = i_size_read(d_inode(old_dentry));
sdcardfs_get_lower_path(old_dentry, &lower_old_path);
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
lower_old_dentry = lower_old_path.dentry;
lower_new_dentry = lower_new_path.dentry;
lower_dir_dentry = lock_parent(lower_new_dentry);
err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry),
lower_new_dentry, NULL);
if (err || !d_inode(lower_new_dentry))
goto out;
err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path);
if (err)
goto out;
fsstack_copy_attr_times(dir, d_inode(lower_new_dentry));
fsstack_copy_inode_size(dir, d_inode(lower_new_dentry));
set_nlink(d_inode(old_dentry),
sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink);
i_size_write(d_inode(new_dentry), file_size_save);
out:
unlock_dir(lower_dir_dentry);
sdcardfs_put_lower_path(old_dentry, &lower_old_path);
sdcardfs_put_lower_path(new_dentry, &lower_new_path);
REVERT_CRED();
return err;
}
#endif
static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
{
int err;
@@ -182,7 +141,10 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
SDCARDFS_I(dir)->data);
if (!saved_cred)
return -ENOMEM;
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
@@ -213,43 +175,11 @@ out:
unlock_dir(lower_dir_dentry);
dput(lower_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_eacces:
return err;
}
#if 0
static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry,
const char *symname)
{
int err;
struct dentry *lower_dentry;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
lower_parent_dentry = lock_parent(lower_dentry);
err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname);
if (err)
goto out;
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
out:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED();
return err;
}
#endif
static int touch(char *abs_path, mode_t mode)
{
struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode);
@@ -291,7 +221,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
SDCARDFS_I(dir)->data);
if (!saved_cred)
return -ENOMEM;
/* check disk space */
parent_dentry = dget_parent(dentry);
@@ -372,13 +305,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
if (make_nomedia_in_obb ||
((pd->perm == PERM_ANDROID)
&& (qstr_case_eq(&dentry->d_name, &q_data)))) {
REVERT_CRED(saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry)));
revert_fsids(saved_cred);
saved_cred = override_fsids(sbi,
SDCARDFS_I(d_inode(dentry))->data);
if (!saved_cred) {
pr_err("sdcardfs: failed to set up .nomedia in %s: %d\n",
lower_path.dentry->d_name.name,
-ENOMEM);
goto out;
}
set_fs_pwd(current->fs, &lower_path);
touch_err = touch(".nomedia", 0664);
if (touch_err) {
pr_err("sdcardfs: failed to create .nomedia in %s: %d\n",
lower_path.dentry->d_name.name, touch_err);
lower_path.dentry->d_name.name,
touch_err);
goto out;
}
}
@@ -391,7 +332,7 @@ out:
out_unlock:
sdcardfs_put_lower_path(dentry, &lower_path);
out_revert:
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_eacces:
return err;
}
@@ -411,7 +352,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
SDCARDFS_I(dir)->data);
if (!saved_cred)
return -ENOMEM;
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
* the dentry on the original path should be deleted.
@@ -436,44 +380,11 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
out:
unlock_dir(lower_dir_dentry);
sdcardfs_put_real_lower(dentry, &lower_path);
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_eacces:
return err;
}
#if 0
static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
dev_t dev)
{
int err;
struct dentry *lower_dentry;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
lower_parent_dentry = lock_parent(lower_dentry);
err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev);
if (err)
goto out;
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
out:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED();
return err;
}
#endif
/*
* The locking rules in sdcardfs_rename are complex. We could use a simpler
* superblock-level name-space lock for renames and copy-ups.
@@ -502,7 +413,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir));
saved_cred = override_fsids(SDCARDFS_SB(old_dir->i_sb),
SDCARDFS_I(new_dir)->data);
if (!saved_cred)
return -ENOMEM;
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
@@ -549,7 +463,7 @@ out:
dput(lower_new_dir_dentry);
sdcardfs_put_real_lower(old_dentry, &lower_old_path);
sdcardfs_put_lower_path(new_dentry, &lower_new_path);
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_eacces:
return err;
}
@@ -668,33 +582,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
if (IS_POSIXACL(inode))
pr_warn("%s: This may be undefined behavior...\n", __func__);
err = generic_permission(&tmp, mask);
/* XXX
* Original sdcardfs code calls inode_permission(lower_inode,.. )
* for checking inode permission. But doing such things here seems
* duplicated work, because the functions called after this func,
* such as vfs_create, vfs_unlink, vfs_rename, and etc,
* does exactly same thing, i.e., they calls inode_permission().
* So we just let they do the things.
* If there are any security hole, just uncomment following if block.
*/
#if 0
if (!err) {
/*
* Permission check on lower_inode(=EXT4).
* we check it with AID_MEDIA_RW permission
*/
struct inode *lower_inode;
OVERRIDE_CRED(SDCARDFS_SB(inode->sb));
lower_inode = sdcardfs_lower_inode(inode);
err = inode_permission(lower_inode, mask);
REVERT_CRED();
}
#endif
return err;
}
static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
@@ -785,7 +673,10 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry,
goto out_err;
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode));
saved_cred = override_fsids(SDCARDFS_SB(dentry->d_sb),
SDCARDFS_I(inode)->data);
if (!saved_cred)
return -ENOMEM;
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
@@ -978,7 +869,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
out:
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_err:
return err;
}
@@ -1062,13 +953,6 @@ const struct inode_operations sdcardfs_dir_iops = {
.setattr = sdcardfs_setattr_wrn,
.setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
* These methods are *NOT* perfectly tested.
.symlink = sdcardfs_symlink,
.link = sdcardfs_link,
.mknod = sdcardfs_mknod,
*/
};
const struct inode_operations sdcardfs_main_iops = {

View File

@@ -426,7 +426,12 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
}
/* save current_cred and override it */
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb),
SDCARDFS_I(dir)->data);
if (!saved_cred) {
ret = ERR_PTR(-ENOMEM);
goto out_err;
}
sdcardfs_get_lower_path(parent, &lower_parent_path);
@@ -457,7 +462,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
out:
sdcardfs_put_lower_path(parent, &lower_parent_path);
REVERT_CRED(saved_cred);
revert_fsids(saved_cred);
out_err:
dput(parent);
return ret;

View File

@@ -88,31 +88,6 @@
(x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
} while (0)
/* OVERRIDE_CRED() and REVERT_CRED()
* OVERRIDE_CRED()
* backup original task->cred
* and modifies task->cred->fsuid/fsgid to specified value.
* REVERT_CRED()
* restore original task->cred->fsuid/fsgid.
* These two macro should be used in pair, and OVERRIDE_CRED() should be
* placed at the beginning of a function, right after variable declaration.
*/
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
do { \
saved_cred = override_fsids(sdcardfs_sbi, info->data); \
if (!saved_cred) \
return -ENOMEM; \
} while (0)
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
do { \
saved_cred = override_fsids(sdcardfs_sbi, info->data); \
if (!saved_cred) \
return ERR_PTR(-ENOMEM); \
} while (0)
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
/* Android 5.0 support */
/* Permission mode for a specific node. Controls how file permissions