diff options
author | Al Viro <viro@ZenIV.linux.org.uk> | 2013-09-20 12:14:21 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-09-26 19:22:29 -0400 |
commit | 2606b28aabd7dea1766c23a105e1124c95409c96 (patch) | |
tree | f03ecf1044bd863c0b6fab6f94a29a6411a60d5c | |
parent | ad1260e9fbf768d6bed227d9604ebee76a84aae3 (diff) |
USB: Fix breakage in ffs_fs_mount()
There's a bunch of failure exits in ffs_fs_mount() with
seriously broken recovery logics. Most of that appears to stem
from misunderstanding of the ->kill_sb() semantics; unlike
->put_super() it is called for *all* superblocks of given type,
no matter how (in)complete the setup had been. ->put_super()
is called only if ->s_root is not NULL; any failure prior to
setting ->s_root will have the call of ->put_super() skipped.
->kill_sb(), OTOH, awaits every superblock that has come from
sget().
Current behaviour of ffs_fs_mount():
We have struct ffs_sb_fill_data data on stack there. We do
ffs_dev = functionfs_acquire_dev_callback(dev_name);
and store that in data.private_data. Then we call mount_nodev(),
passing it ffs_sb_fill() as a callback. That will either fail
outright, or manage to call ffs_sb_fill(). There we allocate an
instance of struct ffs_data, slap the value of ffs_dev (picked
from data.private_data) into ffs->private_data and overwrite
data.private_data by storing ffs into an overlapping member
(data.ffs_data). Then we store ffs into sb->s_fs_info and attempt
to set the rest of the things up (root inode, root dentry, then
create /ep0 there). Any of those might fail. Should that
happen, we get ffs_fs_kill_sb() called before mount_nodev()
returns. If mount_nodev() fails for any reason whatsoever,
we proceed to
functionfs_release_dev_callback(data.ffs_data);
That's broken in a lot of ways. Suppose the thing has failed in
allocation of e.g. root inode or dentry. We have
functionfs_release_dev_callback(ffs);
ffs_data_put(ffs);
done by ffs_fs_kill_sb() (ffs accessed via sb->s_fs_info), followed by
functionfs_release_dev_callback(ffs);
from ffs_fs_mount() (via data.ffs_data). Note that the second
functionfs_release_dev_callback() has every chance to be done to freed memory.
Suppose we fail *before* root inode allocation. What happens then?
ffs_fs_kill_sb() doesn't do anything to ffs (it's either not called at all,
or it doesn't have a pointer to ffs stored in sb->s_fs_info). And
functionfs_release_dev_callback(data.ffs_data);
is called by ffs_fs_mount(), but here we are in nasal daemon country - we
are reading from a member of union we'd never stored into. In practice,
we'll get what we used to store into the overlapping field, i.e. ffs_dev.
And then we get screwed, since we treat it (struct gfs_ffs_obj * in
disguise, returned by functionfs_acquire_dev_callback()) as struct
ffs_data *, pick what would've been ffs_data ->private_data from it
(*well* past the actual end of the struct gfs_ffs_obj - struct ffs_data
is much bigger) and poke in whatever it points to.
FWIW, there's a minor leak on top of all that in case if ffs_sb_fill()
fails on kstrdup() - ffs is obviously forgotten.
The thing is, there is no point in playing all those games with union.
Just allocate and initialize ffs_data *before* calling mount_nodev() and
pass a pointer to it via data.ffs_data. And once it's stored in
sb->s_fs_info, clear data.ffs_data, so that ffs_fs_mount() knows that
it doesn't need to kill the sucker manually - from that point on
we'll have it done by ->kill_sb().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: stable <stable@vger.kernel.org> # 3.3+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/gadget/f_fs.c | 60 |
1 files changed, 26 insertions, 34 deletions
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 1a66c5baa0d1..0658908d8968 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c | |||
@@ -1034,37 +1034,19 @@ struct ffs_sb_fill_data { | |||
1034 | struct ffs_file_perms perms; | 1034 | struct ffs_file_perms perms; |
1035 | umode_t root_mode; | 1035 | umode_t root_mode; |
1036 | const char *dev_name; | 1036 | const char *dev_name; |
1037 | union { | 1037 | struct ffs_data *ffs_data; |
1038 | /* set by ffs_fs_mount(), read by ffs_sb_fill() */ | ||
1039 | void *private_data; | ||
1040 | /* set by ffs_sb_fill(), read by ffs_fs_mount */ | ||
1041 | struct ffs_data *ffs_data; | ||
1042 | }; | ||
1043 | }; | 1038 | }; |
1044 | 1039 | ||
1045 | static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) | 1040 | static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) |
1046 | { | 1041 | { |
1047 | struct ffs_sb_fill_data *data = _data; | 1042 | struct ffs_sb_fill_data *data = _data; |
1048 | struct inode *inode; | 1043 | struct inode *inode; |
1049 | struct ffs_data *ffs; | 1044 | struct ffs_data *ffs = data->ffs_data; |
1050 | 1045 | ||
1051 | ENTER(); | 1046 | ENTER(); |
1052 | 1047 | ||
1053 | /* Initialise data */ | ||
1054 | ffs = ffs_data_new(); | ||
1055 | if (unlikely(!ffs)) | ||
1056 | goto Enomem; | ||
1057 | |||
1058 | ffs->sb = sb; | 1048 | ffs->sb = sb; |
1059 | ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL); | 1049 | data->ffs_data = NULL; |
1060 | if (unlikely(!ffs->dev_name)) | ||
1061 | goto Enomem; | ||
1062 | ffs->file_perms = data->perms; | ||
1063 | ffs->private_data = data->private_data; | ||
1064 | |||
1065 | /* used by the caller of this function */ | ||
1066 | data->ffs_data = ffs; | ||
1067 | |||
1068 | sb->s_fs_info = ffs; | 1050 | sb->s_fs_info = ffs; |
1069 | sb->s_blocksize = PAGE_CACHE_SIZE; | 1051 | sb->s_blocksize = PAGE_CACHE_SIZE; |
1070 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | 1052 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
@@ -1080,17 +1062,14 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) | |||
1080 | &data->perms); | 1062 | &data->perms); |
1081 | sb->s_root = d_make_root(inode); | 1063 | sb->s_root = d_make_root(inode); |
1082 | if (unlikely(!sb->s_root)) | 1064 | if (unlikely(!sb->s_root)) |
1083 | goto Enomem; | 1065 | return -ENOMEM; |
1084 | 1066 | ||
1085 | /* EP0 file */ | 1067 | /* EP0 file */ |
1086 | if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs, | 1068 | if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs, |
1087 | &ffs_ep0_operations, NULL))) | 1069 | &ffs_ep0_operations, NULL))) |
1088 | goto Enomem; | 1070 | return -ENOMEM; |
1089 | 1071 | ||
1090 | return 0; | 1072 | return 0; |
1091 | |||
1092 | Enomem: | ||
1093 | return -ENOMEM; | ||
1094 | } | 1073 | } |
1095 | 1074 | ||
1096 | static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) | 1075 | static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) |
@@ -1193,6 +1172,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, | |||
1193 | struct dentry *rv; | 1172 | struct dentry *rv; |
1194 | int ret; | 1173 | int ret; |
1195 | void *ffs_dev; | 1174 | void *ffs_dev; |
1175 | struct ffs_data *ffs; | ||
1196 | 1176 | ||
1197 | ENTER(); | 1177 | ENTER(); |
1198 | 1178 | ||
@@ -1200,18 +1180,30 @@ ffs_fs_mount(struct file_system_type *t, int flags, | |||
1200 | if (unlikely(ret < 0)) | 1180 | if (unlikely(ret < 0)) |
1201 | return ERR_PTR(ret); | 1181 | return ERR_PTR(ret); |
1202 | 1182 | ||
1183 | ffs = ffs_data_new(); | ||
1184 | if (unlikely(!ffs)) | ||
1185 | return ERR_PTR(-ENOMEM); | ||
1186 | ffs->file_perms = data.perms; | ||
1187 | |||
1188 | ffs->dev_name = kstrdup(dev_name, GFP_KERNEL); | ||
1189 | if (unlikely(!ffs->dev_name)) { | ||
1190 | ffs_data_put(ffs); | ||
1191 | return ERR_PTR(-ENOMEM); | ||
1192 | } | ||
1193 | |||
1203 | ffs_dev = functionfs_acquire_dev_callback(dev_name); | 1194 | ffs_dev = functionfs_acquire_dev_callback(dev_name); |
1204 | if (IS_ERR(ffs_dev)) | 1195 | if (IS_ERR(ffs_dev)) { |
1205 | return ffs_dev; | 1196 | ffs_data_put(ffs); |
1197 | return ERR_CAST(ffs_dev); | ||
1198 | } | ||
1199 | ffs->private_data = ffs_dev; | ||
1200 | data.ffs_data = ffs; | ||
1206 | 1201 | ||
1207 | data.dev_name = dev_name; | ||
1208 | data.private_data = ffs_dev; | ||
1209 | rv = mount_nodev(t, flags, &data, ffs_sb_fill); | 1202 | rv = mount_nodev(t, flags, &data, ffs_sb_fill); |
1210 | 1203 | if (IS_ERR(rv) && data.ffs_data) { | |
1211 | /* data.ffs_data is set by ffs_sb_fill */ | ||
1212 | if (IS_ERR(rv)) | ||
1213 | functionfs_release_dev_callback(data.ffs_data); | 1204 | functionfs_release_dev_callback(data.ffs_data); |
1214 | 1205 | ffs_data_put(data.ffs_data); | |
1206 | } | ||
1215 | return rv; | 1207 | return rv; |
1216 | } | 1208 | } |
1217 | 1209 | ||