Skip to content

Commit ecaca62

Browse files
tstrukExactExampl
authored andcommitted
ANDROID: incremental-fs: limit mount stack depth
Syzbot recently found a number of issues related to incremental-fs (see bug numbers below). All have to do with the fact that incr-fs allows mounts of the same source and target multiple times. This is a design decision and the user space component "Data Loader" expects this to work for app re-install use case. The mounting depth needs to be controlled, however, and only allowed to be two levels deep. In case of more than two mount attempts the driver needs to return an error. In case of the issues listed below the common pattern is that the reproducer calls: mount("./file0", "./file0", "incremental-fs", 0, NULL) many times and then invokes a file operation like chmod, setxattr, or open on the ./file0. This causes a recursive call for all the mounted instances, which eventually causes a stack overflow and a kernel crash: BUG: stack guard page was hit at ffffc90000c0fff8 kernel stack overflow (double-fault): 0000 [#1] PREEMPT SMP KASAN This change also cleans up the mount error path to properly clean allocated resources and call deactivate_locked_super(), which causes the incfs_kill_sb() to be called, where the sb is freed. Bug: 211066171 Bug: 213140206 Bug: 213215835 Bug: 211914587 Bug: 211213635 Bug: 213137376 Bug: 211161296 Signed-off-by: Tadeusz Struk <tadeusz.struk@linaro.org> Change-Id: I08d9b545a2715423296bf4beb67bdbbed78d1be1
1 parent 6abbd18 commit ecaca62

2 files changed

Lines changed: 63 additions & 20 deletions

File tree

fs/incfs/data_mgmt.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,14 @@ struct mount_info {
131131
struct path mi_backing_dir_path;
132132

133133
struct dentry *mi_index_dir;
134+
/* For stacking mounts, if true, this indicates if the index dir needs
135+
* to be freed for this SB otherwise it was created by lower level SB */
136+
bool mi_index_free;
134137

135138
struct dentry *mi_incomplete_dir;
139+
/* For stacking mounts, if true, this indicates if the incomplete dir
140+
* needs to be freed for this SB. Similar to mi_index_free */
141+
bool mi_incomplete_free;
136142

137143
const struct cred *mi_owner;
138144

fs/incfs/vfs.c

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,8 @@ static int incfs_init_dentry(struct dentry *dentry, struct path *path)
435435
}
436436

437437
static struct dentry *open_or_create_special_dir(struct dentry *backing_dir,
438-
const char *name)
438+
const char *name,
439+
bool *created)
439440
{
440441
struct dentry *index_dentry;
441442
struct inode *backing_inode = d_inode(backing_dir);
@@ -448,6 +449,7 @@ static struct dentry *open_or_create_special_dir(struct dentry *backing_dir,
448449
return index_dentry;
449450
} else if (d_really_is_positive(index_dentry)) {
450451
/* Index already exists. */
452+
*created = false;
451453
return index_dentry;
452454
}
453455

@@ -467,6 +469,7 @@ static struct dentry *open_or_create_special_dir(struct dentry *backing_dir,
467469
return ERR_PTR(-EINVAL);
468470
}
469471

472+
*created = true;
470473
return index_dentry;
471474
}
472475

@@ -1745,6 +1748,7 @@ struct dentry *incfs_mount_fs(struct file_system_type *type, int flags,
17451748
struct super_block *src_fs_sb = NULL;
17461749
struct inode *root_inode = NULL;
17471750
struct super_block *sb = sget(type, NULL, set_anon_super, flags, NULL);
1751+
bool dir_created = false;
17481752
int error = 0;
17491753

17501754
if (IS_ERR(sb))
@@ -1761,17 +1765,23 @@ struct dentry *incfs_mount_fs(struct file_system_type *type, int flags,
17611765

17621766
BUILD_BUG_ON(PAGE_SIZE != INCFS_DATA_FILE_BLOCK_SIZE);
17631767

1768+
if (!dev_name) {
1769+
pr_err("incfs: Backing dir is not set, filesystem can't be mounted.\n");
1770+
error = -ENOENT;
1771+
goto err_deactivate;
1772+
}
1773+
17641774
error = parse_options(&options, (char *)data);
17651775
if (error != 0) {
17661776
pr_err("incfs: Options parsing error. %d\n", error);
1767-
goto err;
1777+
goto err_deactivate;
17681778
}
17691779

17701780
sb->s_bdi->ra_pages = options.readahead_pages;
17711781
if (!dev_name) {
17721782
pr_err("incfs: Backing dir is not set, filesystem can't be mounted.\n");
17731783
error = -ENOENT;
1774-
goto err;
1784+
goto err_free_opts;
17751785
}
17761786

17771787
error = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
@@ -1780,69 +1790,80 @@ struct dentry *incfs_mount_fs(struct file_system_type *type, int flags,
17801790
!d_really_is_positive(backing_dir_path.dentry)) {
17811791
pr_err("incfs: Error accessing: %s.\n",
17821792
dev_name);
1783-
goto err;
1793+
goto err_free_opts;
17841794
}
17851795
src_fs_sb = backing_dir_path.dentry->d_sb;
17861796
sb->s_maxbytes = src_fs_sb->s_maxbytes;
1797+
sb->s_stack_depth = src_fs_sb->s_stack_depth + 1;
17871798

1788-
mi = incfs_alloc_mount_info(sb, &options, &backing_dir_path);
1799+
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
1800+
error = -EINVAL;
1801+
goto err_put_path;
1802+
}
17891803

1804+
mi = incfs_alloc_mount_info(sb, &options, &backing_dir_path);
17901805
if (IS_ERR_OR_NULL(mi)) {
17911806
error = PTR_ERR(mi);
17921807
pr_err("incfs: Error allocating mount info. %d\n", error);
1793-
mi = NULL;
1794-
goto err;
1808+
goto err_put_path;
17951809
}
17961810

1811+
sb->s_fs_info = mi;
1812+
mi->mi_backing_dir_path = backing_dir_path;
17971813
index_dir = open_or_create_special_dir(backing_dir_path.dentry,
1798-
INCFS_INDEX_NAME);
1814+
INCFS_INDEX_NAME, &dir_created);
17991815
if (IS_ERR_OR_NULL(index_dir)) {
18001816
error = PTR_ERR(index_dir);
18011817
pr_err("incfs: Can't find or create .index dir in %s\n",
18021818
dev_name);
18031819
/* No need to null index_dir since we don't put it */
1804-
goto err;
1820+
goto err_put_path;
18051821
}
1822+
18061823
mi->mi_index_dir = index_dir;
1824+
mi->mi_index_free = dir_created;
18071825

18081826
incomplete_dir = open_or_create_special_dir(backing_dir_path.dentry,
1809-
INCFS_INCOMPLETE_NAME);
1827+
INCFS_INCOMPLETE_NAME,
1828+
&dir_created);
18101829
if (IS_ERR_OR_NULL(incomplete_dir)) {
18111830
error = PTR_ERR(incomplete_dir);
18121831
pr_err("incfs: Can't find or create .incomplete dir in %s\n",
18131832
dev_name);
18141833
/* No need to null incomplete_dir since we don't put it */
1815-
goto err;
1834+
goto err_put_path;
18161835
}
18171836
mi->mi_incomplete_dir = incomplete_dir;
1837+
mi->mi_incomplete_free = dir_created;
18181838

1819-
sb->s_fs_info = mi;
18201839
root_inode = fetch_regular_inode(sb, backing_dir_path.dentry);
18211840
if (IS_ERR(root_inode)) {
18221841
error = PTR_ERR(root_inode);
1823-
goto err;
1842+
goto err_put_path;
18241843
}
18251844

18261845
sb->s_root = d_make_root(root_inode);
18271846
if (!sb->s_root) {
18281847
error = -ENOMEM;
1829-
goto err;
1848+
goto err_put_path;
18301849
}
18311850
error = incfs_init_dentry(sb->s_root, &backing_dir_path);
18321851
if (error)
1833-
goto err;
1852+
goto err_put_path;
18341853

18351854
path_put(&backing_dir_path);
18361855
sb->s_flags |= SB_ACTIVE;
18371856

18381857
pr_debug("incfs: mount\n");
18391858
return dget(sb->s_root);
1840-
err:
1841-
sb->s_fs_info = NULL;
1859+
1860+
err_put_path:
18421861
path_put(&backing_dir_path);
1843-
incfs_free_mount_info(mi);
1844-
deactivate_locked_super(sb);
1862+
err_free_opts:
18451863
free_options(&options);
1864+
err_deactivate:
1865+
deactivate_locked_super(sb);
1866+
pr_err("incfs: mount failed %d\n", error);
18461867
return ERR_PTR(error);
18471868
}
18481869

@@ -1877,10 +1898,26 @@ static int incfs_remount_fs(struct super_block *sb, int *flags, char *data)
18771898
void incfs_kill_sb(struct super_block *sb)
18781899
{
18791900
struct mount_info *mi = sb->s_fs_info;
1901+
struct inode *dinode = NULL;
18801902

18811903
pr_debug("incfs: unmount\n");
1904+
1905+
if (mi) {
1906+
if (mi->mi_backing_dir_path.dentry)
1907+
dinode = d_inode(mi->mi_backing_dir_path.dentry);
1908+
1909+
if (dinode) {
1910+
if (mi->mi_index_dir && mi->mi_index_free)
1911+
vfs_rmdir(dinode, mi->mi_index_dir);
1912+
1913+
if (mi->mi_incomplete_dir && mi->mi_incomplete_free)
1914+
vfs_rmdir(dinode, mi->mi_incomplete_dir);
1915+
}
1916+
1917+
incfs_free_mount_info(mi);
1918+
sb->s_fs_info = NULL;
1919+
}
18821920
kill_anon_super(sb);
1883-
incfs_free_mount_info(mi);
18841921
}
18851922

18861923
static int show_options(struct seq_file *m, struct dentry *root)

0 commit comments

Comments
 (0)