aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Brauner <christian@brauner.io>2019-01-02 06:32:18 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-01-11 04:18:24 -0500
commit849d540ddfcd4f232f3b2cf40a2e07eccbd6212c (patch)
treec265c1e3a2ad6bd1b9e9173adc3c62295451748e
parentb6c770d7c9dc7185b17d53a9d5ca1278c182d6fa (diff)
binderfs: implement "max" mount option
Since binderfs can be mounted by userns root in non-initial user namespaces some precautions are in order. First, a way to set a maximum on the number of binder devices that can be allocated per binderfs instance and second, a way to reserve a reasonable chunk of binderfs devices for the initial ipc namespace. A first approach as seen in [1] used sysctls similiar to devpts but was shown to be flawed (cf. [2] and [3]) since some aspects were unneeded. This is an alternative approach which avoids sysctls completely and instead switches to a single mount option. Starting with this commit binderfs instances can be mounted with a limit on the number of binder devices that can be allocated. The max=<count> mount option serves as a per-instance limit. If max=<count> is set then only <count> number of binder devices can be allocated in this binderfs instance. This allows to safely bind-mount binderfs instances into unprivileged user namespaces since userns root in a non-initial user namespace cannot change the mount option as long as it does not own the mount namespace the binderfs mount was created in and hence cannot drain the host of minor device numbers [1]: https://lore.kernel.org/lkml/20181221133909.18794-1-christian@brauner.io/ [2]; https://lore.kernel.org/lkml/20181221163316.GA8517@kroah.com/ [3]: https://lore.kernel.org/lkml/CAHRSSEx+gDVW4fKKK8oZNAir9G5icJLyodO8hykv3O0O1jt2FQ@mail.gmail.com/ [4]: https://lore.kernel.org/lkml/20181221192044.5yvfnuri7gdop4rs@brauner.io/ Cc: Todd Kjos <tkjos@google.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/android/binderfs.c104
1 files changed, 98 insertions, 6 deletions
diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
index 4990d65d4850..89788969bc04 100644
--- a/drivers/android/binderfs.c
+++ b/drivers/android/binderfs.c
@@ -20,6 +20,7 @@
20#include <linux/parser.h> 20#include <linux/parser.h>
21#include <linux/radix-tree.h> 21#include <linux/radix-tree.h>
22#include <linux/sched.h> 22#include <linux/sched.h>
23#include <linux/seq_file.h>
23#include <linux/slab.h> 24#include <linux/slab.h>
24#include <linux/spinlock_types.h> 25#include <linux/spinlock_types.h>
25#include <linux/stddef.h> 26#include <linux/stddef.h>
@@ -45,6 +46,24 @@ static DEFINE_MUTEX(binderfs_minors_mutex);
45static DEFINE_IDA(binderfs_minors); 46static DEFINE_IDA(binderfs_minors);
46 47
47/** 48/**
49 * binderfs_mount_opts - mount options for binderfs
50 * @max: maximum number of allocatable binderfs binder devices
51 */
52struct binderfs_mount_opts {
53 int max;
54};
55
56enum {
57 Opt_max,
58 Opt_err
59};
60
61static const match_table_t tokens = {
62 { Opt_max, "max=%d" },
63 { Opt_err, NULL }
64};
65
66/**
48 * binderfs_info - information about a binderfs mount 67 * binderfs_info - information about a binderfs mount
49 * @ipc_ns: The ipc namespace the binderfs mount belongs to. 68 * @ipc_ns: The ipc namespace the binderfs mount belongs to.
50 * @control_dentry: This records the dentry of this binderfs mount 69 * @control_dentry: This records the dentry of this binderfs mount
@@ -53,13 +72,16 @@ static DEFINE_IDA(binderfs_minors);
53 * created. 72 * created.
54 * @root_gid: gid that needs to be used when a new binder device is 73 * @root_gid: gid that needs to be used when a new binder device is
55 * created. 74 * created.
75 * @mount_opts: The mount options in use.
76 * @device_count: The current number of allocated binder devices.
56 */ 77 */
57struct binderfs_info { 78struct binderfs_info {
58 struct ipc_namespace *ipc_ns; 79 struct ipc_namespace *ipc_ns;
59 struct dentry *control_dentry; 80 struct dentry *control_dentry;
60 kuid_t root_uid; 81 kuid_t root_uid;
61 kgid_t root_gid; 82 kgid_t root_gid;
62 83 struct binderfs_mount_opts mount_opts;
84 int device_count;
63}; 85};
64 86
65static inline struct binderfs_info *BINDERFS_I(const struct inode *inode) 87static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
@@ -108,10 +130,17 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
108 130
109 /* Reserve new minor number for the new device. */ 131 /* Reserve new minor number for the new device. */
110 mutex_lock(&binderfs_minors_mutex); 132 mutex_lock(&binderfs_minors_mutex);
111 minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL); 133 if (++info->device_count <= info->mount_opts.max)
112 mutex_unlock(&binderfs_minors_mutex); 134 minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR,
113 if (minor < 0) 135 GFP_KERNEL);
136 else
137 minor = -ENOSPC;
138 if (minor < 0) {
139 --info->device_count;
140 mutex_unlock(&binderfs_minors_mutex);
114 return minor; 141 return minor;
142 }
143 mutex_unlock(&binderfs_minors_mutex);
115 144
116 ret = -ENOMEM; 145 ret = -ENOMEM;
117 device = kzalloc(sizeof(*device), GFP_KERNEL); 146 device = kzalloc(sizeof(*device), GFP_KERNEL);
@@ -185,6 +214,7 @@ err:
185 kfree(name); 214 kfree(name);
186 kfree(device); 215 kfree(device);
187 mutex_lock(&binderfs_minors_mutex); 216 mutex_lock(&binderfs_minors_mutex);
217 --info->device_count;
188 ida_free(&binderfs_minors, minor); 218 ida_free(&binderfs_minors, minor);
189 mutex_unlock(&binderfs_minors_mutex); 219 mutex_unlock(&binderfs_minors_mutex);
190 iput(inode); 220 iput(inode);
@@ -230,6 +260,7 @@ static long binder_ctl_ioctl(struct file *file, unsigned int cmd,
230static void binderfs_evict_inode(struct inode *inode) 260static void binderfs_evict_inode(struct inode *inode)
231{ 261{
232 struct binder_device *device = inode->i_private; 262 struct binder_device *device = inode->i_private;
263 struct binderfs_info *info = BINDERFS_I(inode);
233 264
234 clear_inode(inode); 265 clear_inode(inode);
235 266
@@ -237,6 +268,7 @@ static void binderfs_evict_inode(struct inode *inode)
237 return; 268 return;
238 269
239 mutex_lock(&binderfs_minors_mutex); 270 mutex_lock(&binderfs_minors_mutex);
271 --info->device_count;
240 ida_free(&binderfs_minors, device->miscdev.minor); 272 ida_free(&binderfs_minors, device->miscdev.minor);
241 mutex_unlock(&binderfs_minors_mutex); 273 mutex_unlock(&binderfs_minors_mutex);
242 274
@@ -244,9 +276,65 @@ static void binderfs_evict_inode(struct inode *inode)
244 kfree(device); 276 kfree(device);
245} 277}
246 278
279/**
280 * binderfs_parse_mount_opts - parse binderfs mount options
281 * @data: options to set (can be NULL in which case defaults are used)
282 */
283static int binderfs_parse_mount_opts(char *data,
284 struct binderfs_mount_opts *opts)
285{
286 char *p;
287 opts->max = BINDERFS_MAX_MINOR;
288
289 while ((p = strsep(&data, ",")) != NULL) {
290 substring_t args[MAX_OPT_ARGS];
291 int token;
292 int max_devices;
293
294 if (!*p)
295 continue;
296
297 token = match_token(p, tokens, args);
298 switch (token) {
299 case Opt_max:
300 if (match_int(&args[0], &max_devices) ||
301 (max_devices < 0 ||
302 (max_devices > BINDERFS_MAX_MINOR)))
303 return -EINVAL;
304
305 opts->max = max_devices;
306 break;
307 default:
308 pr_err("Invalid mount options\n");
309 return -EINVAL;
310 }
311 }
312
313 return 0;
314}
315
316static int binderfs_remount(struct super_block *sb, int *flags, char *data)
317{
318 struct binderfs_info *info = sb->s_fs_info;
319 return binderfs_parse_mount_opts(data, &info->mount_opts);
320}
321
322static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
323{
324 struct binderfs_info *info;
325
326 info = root->d_sb->s_fs_info;
327 if (info->mount_opts.max <= BINDERFS_MAX_MINOR)
328 seq_printf(seq, ",max=%d", info->mount_opts.max);
329
330 return 0;
331}
332
247static const struct super_operations binderfs_super_ops = { 333static const struct super_operations binderfs_super_ops = {
248 .statfs = simple_statfs, 334 .evict_inode = binderfs_evict_inode,
249 .evict_inode = binderfs_evict_inode, 335 .remount_fs = binderfs_remount,
336 .show_options = binderfs_show_mount_opts,
337 .statfs = simple_statfs,
250}; 338};
251 339
252static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry, 340static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -407,6 +495,10 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
407 if (!info) 495 if (!info)
408 goto err_without_dentry; 496 goto err_without_dentry;
409 497
498 ret = binderfs_parse_mount_opts(data, &info->mount_opts);
499 if (ret)
500 goto err_without_dentry;
501
410 info->ipc_ns = ipc_ns; 502 info->ipc_ns = ipc_ns;
411 info->root_gid = make_kgid(sb->s_user_ns, 0); 503 info->root_gid = make_kgid(sb->s_user_ns, 0);
412 if (!gid_valid(info->root_gid)) 504 if (!gid_valid(info->root_gid))