Skip to content

Commit 849d540

Browse files
braunergregkh
authored andcommitted
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/[email protected]/ [2]; https://lore.kernel.org/lkml/[email protected]/ [3]: https://lore.kernel.org/lkml/CAHRSSEx+gDVW4fKKK8oZNAir9G5icJLyodO8hykv3O0O1jt2FQ@mail.gmail.com/ [4]: https://lore.kernel.org/lkml/[email protected]/ Cc: Todd Kjos <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Signed-off-by: Christian Brauner <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b6c770d commit 849d540

File tree

1 file changed

+98
-6
lines changed

1 file changed

+98
-6
lines changed

drivers/android/binderfs.c

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/parser.h>
2121
#include <linux/radix-tree.h>
2222
#include <linux/sched.h>
23+
#include <linux/seq_file.h>
2324
#include <linux/slab.h>
2425
#include <linux/spinlock_types.h>
2526
#include <linux/stddef.h>
@@ -44,6 +45,24 @@ static dev_t binderfs_dev;
4445
static DEFINE_MUTEX(binderfs_minors_mutex);
4546
static DEFINE_IDA(binderfs_minors);
4647

48+
/**
49+
* binderfs_mount_opts - mount options for binderfs
50+
* @max: maximum number of allocatable binderfs binder devices
51+
*/
52+
struct binderfs_mount_opts {
53+
int max;
54+
};
55+
56+
enum {
57+
Opt_max,
58+
Opt_err
59+
};
60+
61+
static const match_table_t tokens = {
62+
{ Opt_max, "max=%d" },
63+
{ Opt_err, NULL }
64+
};
65+
4766
/**
4867
* binderfs_info - information about a binderfs mount
4968
* @ipc_ns: The ipc namespace the binderfs mount belongs to.
@@ -53,13 +72,16 @@ static DEFINE_IDA(binderfs_minors);
5372
* created.
5473
* @root_gid: gid that needs to be used when a new binder device is
5574
* created.
75+
* @mount_opts: The mount options in use.
76+
* @device_count: The current number of allocated binder devices.
5677
*/
5778
struct binderfs_info {
5879
struct ipc_namespace *ipc_ns;
5980
struct dentry *control_dentry;
6081
kuid_t root_uid;
6182
kgid_t root_gid;
62-
83+
struct binderfs_mount_opts mount_opts;
84+
int device_count;
6385
};
6486

6587
static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
@@ -108,10 +130,17 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
108130

109131
/* Reserve new minor number for the new device. */
110132
mutex_lock(&binderfs_minors_mutex);
111-
minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL);
112-
mutex_unlock(&binderfs_minors_mutex);
113-
if (minor < 0)
133+
if (++info->device_count <= info->mount_opts.max)
134+
minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR,
135+
GFP_KERNEL);
136+
else
137+
minor = -ENOSPC;
138+
if (minor < 0) {
139+
--info->device_count;
140+
mutex_unlock(&binderfs_minors_mutex);
114141
return minor;
142+
}
143+
mutex_unlock(&binderfs_minors_mutex);
115144

116145
ret = -ENOMEM;
117146
device = kzalloc(sizeof(*device), GFP_KERNEL);
@@ -185,6 +214,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
185214
kfree(name);
186215
kfree(device);
187216
mutex_lock(&binderfs_minors_mutex);
217+
--info->device_count;
188218
ida_free(&binderfs_minors, minor);
189219
mutex_unlock(&binderfs_minors_mutex);
190220
iput(inode);
@@ -230,23 +260,81 @@ static long binder_ctl_ioctl(struct file *file, unsigned int cmd,
230260
static void binderfs_evict_inode(struct inode *inode)
231261
{
232262
struct binder_device *device = inode->i_private;
263+
struct binderfs_info *info = BINDERFS_I(inode);
233264

234265
clear_inode(inode);
235266

236267
if (!device)
237268
return;
238269

239270
mutex_lock(&binderfs_minors_mutex);
271+
--info->device_count;
240272
ida_free(&binderfs_minors, device->miscdev.minor);
241273
mutex_unlock(&binderfs_minors_mutex);
242274

243275
kfree(device->context.name);
244276
kfree(device);
245277
}
246278

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+
*/
283+
static 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+
316+
static 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+
322+
static 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+
247333
static const struct super_operations binderfs_super_ops = {
248-
.statfs = simple_statfs,
249-
.evict_inode = binderfs_evict_inode,
334+
.evict_inode = binderfs_evict_inode,
335+
.remount_fs = binderfs_remount,
336+
.show_options = binderfs_show_mount_opts,
337+
.statfs = simple_statfs,
250338
};
251339

252340
static 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)
407495
if (!info)
408496
goto err_without_dentry;
409497

498+
ret = binderfs_parse_mount_opts(data, &info->mount_opts);
499+
if (ret)
500+
goto err_without_dentry;
501+
410502
info->ipc_ns = ipc_ns;
411503
info->root_gid = make_kgid(sb->s_user_ns, 0);
412504
if (!gid_valid(info->root_gid))

0 commit comments

Comments
 (0)