binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
|
|
|
|
|
#ifndef _LINUX_BINDER_INTERNAL_H
|
|
|
|
#define _LINUX_BINDER_INTERNAL_H
|
|
|
|
|
|
|
|
#include <linux/export.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/miscdevice.h>
|
|
|
|
#include <linux/mutex.h>
|
2020-03-03 16:43:40 +00:00
|
|
|
#include <linux/refcount.h>
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
#include <linux/stddef.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/uidgid.h>
|
2020-11-11 03:02:42 +00:00
|
|
|
#include <uapi/linux/android/binderfs.h>
|
|
|
|
#include "binder_alloc.h"
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
|
|
|
|
struct binder_context {
|
|
|
|
struct binder_node *binder_context_mgr_node;
|
|
|
|
struct mutex context_mgr_node_lock;
|
|
|
|
kuid_t binder_context_mgr_uid;
|
|
|
|
const char *name;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_device - information about a binder device node
|
|
|
|
* @hlist: list of binder devices (only used for devices requested via
|
|
|
|
* CONFIG_ANDROID_BINDER_DEVICES)
|
|
|
|
* @miscdev: information about a binder character device node
|
|
|
|
* @context: binder context information
|
|
|
|
* @binderfs_inode: This is the inode of the root dentry of the super block
|
|
|
|
* belonging to a binderfs mount.
|
|
|
|
*/
|
|
|
|
struct binder_device {
|
|
|
|
struct hlist_node hlist;
|
|
|
|
struct miscdevice miscdev;
|
|
|
|
struct binder_context context;
|
|
|
|
struct inode *binderfs_inode;
|
2020-03-03 16:43:40 +00:00
|
|
|
refcount_t ref;
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
};
|
|
|
|
|
2019-09-03 16:16:55 +00:00
|
|
|
/**
|
|
|
|
* binderfs_mount_opts - mount options for binderfs
|
|
|
|
* @max: maximum number of allocatable binderfs binder devices
|
|
|
|
* @stats_mode: enable binder stats in binderfs.
|
|
|
|
*/
|
|
|
|
struct binderfs_mount_opts {
|
|
|
|
int max;
|
|
|
|
int stats_mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* binderfs_info - information about a binderfs mount
|
|
|
|
* @ipc_ns: The ipc namespace the binderfs mount belongs to.
|
|
|
|
* @control_dentry: This records the dentry of this binderfs mount
|
|
|
|
* binder-control device.
|
|
|
|
* @root_uid: uid that needs to be used when a new binder device is
|
|
|
|
* created.
|
|
|
|
* @root_gid: gid that needs to be used when a new binder device is
|
|
|
|
* created.
|
|
|
|
* @mount_opts: The mount options in use.
|
|
|
|
* @device_count: The current number of allocated binder devices.
|
|
|
|
* @proc_log_dir: Pointer to the directory dentry containing process-specific
|
|
|
|
* logs.
|
|
|
|
*/
|
|
|
|
struct binderfs_info {
|
|
|
|
struct ipc_namespace *ipc_ns;
|
|
|
|
struct dentry *control_dentry;
|
|
|
|
kuid_t root_uid;
|
|
|
|
kgid_t root_gid;
|
|
|
|
struct binderfs_mount_opts mount_opts;
|
|
|
|
int device_count;
|
|
|
|
struct dentry *proc_log_dir;
|
|
|
|
};
|
|
|
|
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
extern const struct file_operations binder_fops;
|
|
|
|
|
2019-09-04 11:07:03 +00:00
|
|
|
extern char *binder_devices_param;
|
|
|
|
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
#ifdef CONFIG_ANDROID_BINDERFS
|
|
|
|
extern bool is_binderfs_device(const struct inode *inode);
|
2019-09-03 16:16:55 +00:00
|
|
|
extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
|
|
|
|
const struct file_operations *fops,
|
|
|
|
void *data);
|
|
|
|
extern void binderfs_remove_file(struct dentry *dentry);
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
#else
|
|
|
|
static inline bool is_binderfs_device(const struct inode *inode)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2019-09-03 16:16:55 +00:00
|
|
|
static inline struct dentry *binderfs_create_file(struct dentry *dir,
|
|
|
|
const char *name,
|
|
|
|
const struct file_operations *fops,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static inline void binderfs_remove_file(struct dentry *dentry) {}
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
#endif
|
|
|
|
|
2019-01-31 00:25:02 +00:00
|
|
|
#ifdef CONFIG_ANDROID_BINDERFS
|
|
|
|
extern int __init init_binderfs(void);
|
|
|
|
#else
|
|
|
|
static inline int __init init_binderfs(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-07-01 18:20:41 +00:00
|
|
|
struct binder_debugfs_entry {
|
|
|
|
const char *name;
|
|
|
|
umode_t mode;
|
|
|
|
const struct file_operations *fops;
|
|
|
|
void *data;
|
2019-09-03 16:16:54 +00:00
|
|
|
};
|
|
|
|
|
2022-07-01 18:20:41 +00:00
|
|
|
extern const struct binder_debugfs_entry binder_debugfs_entries[];
|
|
|
|
|
|
|
|
#define binder_for_each_debugfs_entry(entry) \
|
|
|
|
for ((entry) = binder_debugfs_entries; \
|
|
|
|
(entry)->name; \
|
|
|
|
(entry)++)
|
2019-09-03 16:16:54 +00:00
|
|
|
|
2020-11-11 03:02:42 +00:00
|
|
|
enum binder_stat_types {
|
|
|
|
BINDER_STAT_PROC,
|
|
|
|
BINDER_STAT_THREAD,
|
|
|
|
BINDER_STAT_NODE,
|
|
|
|
BINDER_STAT_REF,
|
|
|
|
BINDER_STAT_DEATH,
|
|
|
|
BINDER_STAT_TRANSACTION,
|
|
|
|
BINDER_STAT_TRANSACTION_COMPLETE,
|
|
|
|
BINDER_STAT_COUNT
|
|
|
|
};
|
|
|
|
|
|
|
|
struct binder_stats {
|
2022-11-23 20:16:54 +00:00
|
|
|
atomic_t br[_IOC_NR(BR_TRANSACTION_PENDING_FROZEN) + 1];
|
2020-11-11 03:02:42 +00:00
|
|
|
atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1];
|
|
|
|
atomic_t obj_created[BINDER_STAT_COUNT];
|
|
|
|
atomic_t obj_deleted[BINDER_STAT_COUNT];
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_work - work enqueued on a worklist
|
|
|
|
* @entry: node enqueued on list
|
|
|
|
* @type: type of work to be performed
|
|
|
|
*
|
|
|
|
* There are separate work lists for proc, thread, and node (async).
|
|
|
|
*/
|
|
|
|
struct binder_work {
|
|
|
|
struct list_head entry;
|
|
|
|
|
|
|
|
enum binder_work_type {
|
|
|
|
BINDER_WORK_TRANSACTION = 1,
|
|
|
|
BINDER_WORK_TRANSACTION_COMPLETE,
|
2022-11-23 20:16:54 +00:00
|
|
|
BINDER_WORK_TRANSACTION_PENDING,
|
2021-04-09 09:40:46 +00:00
|
|
|
BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT,
|
2020-11-11 03:02:42 +00:00
|
|
|
BINDER_WORK_RETURN_ERROR,
|
|
|
|
BINDER_WORK_NODE,
|
|
|
|
BINDER_WORK_DEAD_BINDER,
|
|
|
|
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
|
|
|
|
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
|
|
|
|
} type;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct binder_error {
|
|
|
|
struct binder_work work;
|
|
|
|
uint32_t cmd;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_node - binder node bookkeeping
|
|
|
|
* @debug_id: unique ID for debugging
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @lock: lock for node fields
|
|
|
|
* @work: worklist element for node work
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
* @rb_node: element for proc->nodes tree
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
* @dead_node: element for binder_dead_nodes list
|
|
|
|
* (protected by binder_dead_nodes_lock)
|
|
|
|
* @proc: binder_proc that owns this node
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @refs: list of references on this node
|
|
|
|
* (protected by @lock)
|
|
|
|
* @internal_strong_refs: used to take strong references when
|
|
|
|
* initiating a transaction
|
|
|
|
* (protected by @proc->inner_lock if @proc
|
|
|
|
* and by @lock)
|
|
|
|
* @local_weak_refs: weak user refs from local process
|
|
|
|
* (protected by @proc->inner_lock if @proc
|
|
|
|
* and by @lock)
|
|
|
|
* @local_strong_refs: strong user refs from local process
|
|
|
|
* (protected by @proc->inner_lock if @proc
|
|
|
|
* and by @lock)
|
|
|
|
* @tmp_refs: temporary kernel refs
|
|
|
|
* (protected by @proc->inner_lock while @proc
|
|
|
|
* is valid, and by binder_dead_nodes_lock
|
|
|
|
* if @proc is NULL. During inc/dec and node release
|
|
|
|
* it is also protected by @lock to provide safety
|
|
|
|
* as the node dies and @proc becomes NULL)
|
|
|
|
* @ptr: userspace pointer for node
|
|
|
|
* (invariant, no lock needed)
|
|
|
|
* @cookie: userspace cookie for node
|
|
|
|
* (invariant, no lock needed)
|
|
|
|
* @has_strong_ref: userspace notified of strong ref
|
|
|
|
* (protected by @proc->inner_lock if @proc
|
|
|
|
* and by @lock)
|
|
|
|
* @pending_strong_ref: userspace has acked notification of strong ref
|
|
|
|
* (protected by @proc->inner_lock if @proc
|
|
|
|
* and by @lock)
|
|
|
|
* @has_weak_ref: userspace notified of weak ref
|
|
|
|
* (protected by @proc->inner_lock if @proc
|
|
|
|
* and by @lock)
|
|
|
|
* @pending_weak_ref: userspace has acked notification of weak ref
|
|
|
|
* (protected by @proc->inner_lock if @proc
|
|
|
|
* and by @lock)
|
|
|
|
* @has_async_transaction: async transaction to node in progress
|
|
|
|
* (protected by @lock)
|
|
|
|
* @accept_fds: file descriptor operations supported for node
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @min_priority: minimum scheduling priority
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @txn_security_ctx: require sender's security context
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @async_todo: list of async work items
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
*
|
|
|
|
* Bookkeeping structure for binder nodes.
|
|
|
|
*/
|
|
|
|
struct binder_node {
|
|
|
|
int debug_id;
|
|
|
|
spinlock_t lock;
|
|
|
|
struct binder_work work;
|
|
|
|
union {
|
|
|
|
struct rb_node rb_node;
|
|
|
|
struct hlist_node dead_node;
|
|
|
|
};
|
|
|
|
struct binder_proc *proc;
|
|
|
|
struct hlist_head refs;
|
|
|
|
int internal_strong_refs;
|
|
|
|
int local_weak_refs;
|
|
|
|
int local_strong_refs;
|
|
|
|
int tmp_refs;
|
|
|
|
binder_uintptr_t ptr;
|
|
|
|
binder_uintptr_t cookie;
|
|
|
|
struct {
|
|
|
|
/*
|
|
|
|
* bitfield elements protected by
|
|
|
|
* proc inner_lock
|
|
|
|
*/
|
|
|
|
u8 has_strong_ref:1;
|
|
|
|
u8 pending_strong_ref:1;
|
|
|
|
u8 has_weak_ref:1;
|
|
|
|
u8 pending_weak_ref:1;
|
|
|
|
};
|
|
|
|
struct {
|
|
|
|
/*
|
|
|
|
* invariant after initialization
|
|
|
|
*/
|
|
|
|
u8 accept_fds:1;
|
|
|
|
u8 txn_security_ctx:1;
|
|
|
|
u8 min_priority;
|
|
|
|
};
|
|
|
|
bool has_async_transaction;
|
|
|
|
struct list_head async_todo;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct binder_ref_death {
|
|
|
|
/**
|
|
|
|
* @work: worklist element for death notifications
|
|
|
|
* (protected by inner_lock of the proc that
|
|
|
|
* this ref belongs to)
|
|
|
|
*/
|
|
|
|
struct binder_work work;
|
|
|
|
binder_uintptr_t cookie;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_ref_data - binder_ref counts and id
|
|
|
|
* @debug_id: unique ID for the ref
|
|
|
|
* @desc: unique userspace handle for ref
|
|
|
|
* @strong: strong ref count (debugging only if not locked)
|
|
|
|
* @weak: weak ref count (debugging only if not locked)
|
|
|
|
*
|
|
|
|
* Structure to hold ref count and ref id information. Since
|
|
|
|
* the actual ref can only be accessed with a lock, this structure
|
|
|
|
* is used to return information about the ref to callers of
|
|
|
|
* ref inc/dec functions.
|
|
|
|
*/
|
|
|
|
struct binder_ref_data {
|
|
|
|
int debug_id;
|
|
|
|
uint32_t desc;
|
|
|
|
int strong;
|
|
|
|
int weak;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_ref - struct to track references on nodes
|
|
|
|
* @data: binder_ref_data containing id, handle, and current refcounts
|
|
|
|
* @rb_node_desc: node for lookup by @data.desc in proc's rb_tree
|
|
|
|
* @rb_node_node: node for lookup by @node in proc's rb_tree
|
|
|
|
* @node_entry: list entry for node->refs list in target node
|
|
|
|
* (protected by @node->lock)
|
|
|
|
* @proc: binder_proc containing ref
|
|
|
|
* @node: binder_node of target node. When cleaning up a
|
|
|
|
* ref for deletion in binder_cleanup_ref, a non-NULL
|
|
|
|
* @node indicates the node must be freed
|
|
|
|
* @death: pointer to death notification (ref_death) if requested
|
|
|
|
* (protected by @node->lock)
|
|
|
|
*
|
|
|
|
* Structure to track references from procA to target node (on procB). This
|
|
|
|
* structure is unsafe to access without holding @proc->outer_lock.
|
|
|
|
*/
|
|
|
|
struct binder_ref {
|
|
|
|
/* Lookups needed: */
|
|
|
|
/* node + proc => ref (transaction) */
|
|
|
|
/* desc + proc => ref (transaction, inc/dec ref) */
|
|
|
|
/* node => refs + procs (proc exit) */
|
|
|
|
struct binder_ref_data data;
|
|
|
|
struct rb_node rb_node_desc;
|
|
|
|
struct rb_node rb_node_node;
|
|
|
|
struct hlist_node node_entry;
|
|
|
|
struct binder_proc *proc;
|
|
|
|
struct binder_node *node;
|
|
|
|
struct binder_ref_death *death;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_proc - binder process bookkeeping
|
|
|
|
* @proc_node: element for binder_procs list
|
|
|
|
* @threads: rbtree of binder_threads in this proc
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @nodes: rbtree of binder nodes associated with
|
|
|
|
* this proc ordered by node->ptr
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @refs_by_desc: rbtree of refs ordered by ref->desc
|
|
|
|
* (protected by @outer_lock)
|
|
|
|
* @refs_by_node: rbtree of refs ordered by ref->node
|
|
|
|
* (protected by @outer_lock)
|
|
|
|
* @waiting_threads: threads currently waiting for proc work
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @pid PID of group_leader of process
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @tsk task_struct for group_leader of process
|
|
|
|
* (invariant after initialized)
|
2021-10-12 16:56:12 +00:00
|
|
|
* @cred struct cred associated with the `struct file`
|
|
|
|
* in binder_open()
|
|
|
|
* (invariant after initialized)
|
2020-11-11 03:02:42 +00:00
|
|
|
* @deferred_work_node: element for binder_deferred_list
|
|
|
|
* (protected by binder_deferred_lock)
|
|
|
|
* @deferred_work: bitmap of deferred work to perform
|
|
|
|
* (protected by binder_deferred_lock)
|
2021-03-16 01:16:28 +00:00
|
|
|
* @outstanding_txns: number of transactions to be transmitted before
|
|
|
|
* processes in freeze_wait are woken up
|
|
|
|
* (protected by @inner_lock)
|
2020-11-11 03:02:42 +00:00
|
|
|
* @is_dead: process is dead and awaiting free
|
|
|
|
* when outstanding transactions are cleaned up
|
|
|
|
* (protected by @inner_lock)
|
2021-03-16 01:16:28 +00:00
|
|
|
* @is_frozen: process is frozen and unable to service
|
|
|
|
* binder transactions
|
|
|
|
* (protected by @inner_lock)
|
2021-03-16 01:16:30 +00:00
|
|
|
* @sync_recv: process received sync transactions since last frozen
|
binder: fix freeze race
Currently cgroup freezer is used to freeze the application threads, and
BINDER_FREEZE is used to freeze the corresponding binder interface.
There's already a mechanism in ioctl(BINDER_FREEZE) to wait for any
existing transactions to drain out before actually freezing the binder
interface.
But freezing an app requires 2 steps, freezing the binder interface with
ioctl(BINDER_FREEZE) and then freezing the application main threads with
cgroupfs. This is not an atomic operation. The following race issue
might happen.
1) Binder interface is frozen by ioctl(BINDER_FREEZE);
2) Main thread A initiates a new sync binder transaction to process B;
3) Main thread A is frozen by "echo 1 > cgroup.freeze";
4) The response from process B reaches the frozen thread, which will
unexpectedly fail.
This patch provides a mechanism to check if there's any new pending
transaction happening between ioctl(BINDER_FREEZE) and freezing the
main thread. If there's any, the main thread freezing operation can
be rolled back to finish the pending transaction.
Furthermore, the response might reach the binder driver before the
rollback actually happens. That will still cause failed transaction.
As the other process doesn't wait for another response of the response,
the response transaction failure can be fixed by treating the response
transaction like an oneway/async one, allowing it to reach the frozen
thread. And it will be consumed when the thread gets unfrozen later.
NOTE: This patch reuses the existing definition of struct
binder_frozen_status_info but expands the bit assignments of __u32
member sync_recv.
To ensure backward compatibility, bit 0 of sync_recv still indicates
there's an outstanding sync binder transaction. This patch adds new
information to bit 1 of sync_recv, indicating the binder transaction
happens exactly when there's a race.
If an existing userspace app runs on a new kernel, a sync binder call
will set bit 0 of sync_recv so ioctl(BINDER_GET_FROZEN_INFO) still
return the expected value (true). The app just doesn't check bit 1
intentionally so it doesn't have the ability to tell if there's a race.
This behavior is aligned with what happens on an old kernel which
doesn't set bit 1 at all.
A new userspace app can 1) check bit 0 to know if there's a sync binder
transaction happened when being frozen - same as before; and 2) check
bit 1 to know if that sync binder transaction happened exactly when
there's a race - a new information for rollback decision.
the same time, confirmed the pending transactions succeeded.
Fixes: 432ff1e91694 ("binder: BINDER_FREEZE ioctl")
Acked-by: Todd Kjos <tkjos@google.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Li Li <dualli@google.com>
Test: stress test with apps being frozen and initiating binder calls at
Link: https://lore.kernel.org/r/20210910164210.2282716-2-dualli@chromium.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-09-10 16:42:10 +00:00
|
|
|
* bit 0: received sync transaction after being frozen
|
|
|
|
* bit 1: new pending sync transaction during freezing
|
2021-03-16 01:16:30 +00:00
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @async_recv: process received async transactions since last frozen
|
|
|
|
* (protected by @inner_lock)
|
2021-03-16 01:16:28 +00:00
|
|
|
* @freeze_wait: waitqueue of processes waiting for all outstanding
|
|
|
|
* transactions to be processed
|
|
|
|
* (protected by @inner_lock)
|
2020-11-11 03:02:42 +00:00
|
|
|
* @todo: list of work for this process
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @stats: per-process binder statistics
|
|
|
|
* (atomics, no lock needed)
|
|
|
|
* @delivered_death: list of delivered death notification
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @max_threads: cap on number of binder threads
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @requested_threads: number of binder threads requested but not
|
|
|
|
* yet started. In current implementation, can
|
|
|
|
* only be 0 or 1.
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @requested_threads_started: number binder threads started
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @tmp_ref: temporary reference to indicate proc is in use
|
|
|
|
* (protected by @inner_lock)
|
|
|
|
* @default_priority: default scheduler priority
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @debugfs_entry: debugfs node
|
|
|
|
* @alloc: binder allocator bookkeeping
|
|
|
|
* @context: binder_context for this proc
|
|
|
|
* (invariant after initialized)
|
|
|
|
* @inner_lock: can nest under outer_lock and/or node lock
|
|
|
|
* @outer_lock: no nesting under innor or node lock
|
|
|
|
* Lock order: 1) outer, 2) node, 3) inner
|
|
|
|
* @binderfs_entry: process-specific binderfs log file
|
2021-04-09 09:40:46 +00:00
|
|
|
* @oneway_spam_detection_enabled: process enabled oneway spam detection
|
|
|
|
* or not
|
2020-11-11 03:02:42 +00:00
|
|
|
*
|
|
|
|
* Bookkeeping structure for binder processes
|
|
|
|
*/
|
|
|
|
struct binder_proc {
|
|
|
|
struct hlist_node proc_node;
|
|
|
|
struct rb_root threads;
|
|
|
|
struct rb_root nodes;
|
|
|
|
struct rb_root refs_by_desc;
|
|
|
|
struct rb_root refs_by_node;
|
|
|
|
struct list_head waiting_threads;
|
|
|
|
int pid;
|
|
|
|
struct task_struct *tsk;
|
2021-10-12 16:56:12 +00:00
|
|
|
const struct cred *cred;
|
2020-11-11 03:02:42 +00:00
|
|
|
struct hlist_node deferred_work_node;
|
|
|
|
int deferred_work;
|
2021-03-16 01:16:28 +00:00
|
|
|
int outstanding_txns;
|
2020-11-11 03:02:42 +00:00
|
|
|
bool is_dead;
|
2021-03-16 01:16:28 +00:00
|
|
|
bool is_frozen;
|
2021-03-16 01:16:30 +00:00
|
|
|
bool sync_recv;
|
|
|
|
bool async_recv;
|
2021-03-16 01:16:28 +00:00
|
|
|
wait_queue_head_t freeze_wait;
|
2020-11-11 03:02:42 +00:00
|
|
|
|
|
|
|
struct list_head todo;
|
|
|
|
struct binder_stats stats;
|
|
|
|
struct list_head delivered_death;
|
|
|
|
int max_threads;
|
|
|
|
int requested_threads;
|
|
|
|
int requested_threads_started;
|
|
|
|
int tmp_ref;
|
|
|
|
long default_priority;
|
|
|
|
struct dentry *debugfs_entry;
|
|
|
|
struct binder_alloc alloc;
|
|
|
|
struct binder_context *context;
|
|
|
|
spinlock_t inner_lock;
|
|
|
|
spinlock_t outer_lock;
|
|
|
|
struct dentry *binderfs_entry;
|
2021-04-09 09:40:46 +00:00
|
|
|
bool oneway_spam_detection_enabled;
|
2020-11-11 03:02:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_thread - binder thread bookkeeping
|
|
|
|
* @proc: binder process for this thread
|
|
|
|
* (invariant after initialization)
|
|
|
|
* @rb_node: element for proc->threads rbtree
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
* @waiting_thread_node: element for @proc->waiting_threads list
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
* @pid: PID for this thread
|
|
|
|
* (invariant after initialization)
|
|
|
|
* @looper: bitmap of looping state
|
|
|
|
* (only accessed by this thread)
|
|
|
|
* @looper_needs_return: looping thread needs to exit driver
|
|
|
|
* (no lock needed)
|
|
|
|
* @transaction_stack: stack of in-progress transactions for this thread
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
* @todo: list of work to do for this thread
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
* @process_todo: whether work in @todo should be processed
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
* @return_error: transaction errors reported by this thread
|
|
|
|
* (only accessed by this thread)
|
|
|
|
* @reply_error: transaction errors reported by target thread
|
|
|
|
* (protected by @proc->inner_lock)
|
2022-04-29 23:56:41 +00:00
|
|
|
* @ee: extended error information from this thread
|
|
|
|
* (protected by @proc->inner_lock)
|
2020-11-11 03:02:42 +00:00
|
|
|
* @wait: wait queue for thread work
|
|
|
|
* @stats: per-thread statistics
|
|
|
|
* (atomics, no lock needed)
|
|
|
|
* @tmp_ref: temporary reference to indicate thread is in use
|
|
|
|
* (atomic since @proc->inner_lock cannot
|
|
|
|
* always be acquired)
|
|
|
|
* @is_dead: thread is dead and awaiting free
|
|
|
|
* when outstanding transactions are cleaned up
|
|
|
|
* (protected by @proc->inner_lock)
|
|
|
|
*
|
|
|
|
* Bookkeeping structure for binder threads.
|
|
|
|
*/
|
|
|
|
struct binder_thread {
|
|
|
|
struct binder_proc *proc;
|
|
|
|
struct rb_node rb_node;
|
|
|
|
struct list_head waiting_thread_node;
|
|
|
|
int pid;
|
|
|
|
int looper; /* only modified by this thread */
|
|
|
|
bool looper_need_return; /* can be written by other thread */
|
|
|
|
struct binder_transaction *transaction_stack;
|
|
|
|
struct list_head todo;
|
|
|
|
bool process_todo;
|
|
|
|
struct binder_error return_error;
|
|
|
|
struct binder_error reply_error;
|
2022-04-29 23:56:41 +00:00
|
|
|
struct binder_extended_error ee;
|
2020-11-11 03:02:42 +00:00
|
|
|
wait_queue_head_t wait;
|
|
|
|
struct binder_stats stats;
|
|
|
|
atomic_t tmp_ref;
|
|
|
|
bool is_dead;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_txn_fd_fixup - transaction fd fixup list element
|
|
|
|
* @fixup_entry: list entry
|
|
|
|
* @file: struct file to be associated with new fd
|
|
|
|
* @offset: offset in buffer data to this fixup
|
2022-03-25 23:24:54 +00:00
|
|
|
* @target_fd: fd to use by the target to install @file
|
2020-11-11 03:02:42 +00:00
|
|
|
*
|
|
|
|
* List element for fd fixups in a transaction. Since file
|
|
|
|
* descriptors need to be allocated in the context of the
|
|
|
|
* target process, we pass each fd to be processed in this
|
|
|
|
* struct.
|
|
|
|
*/
|
|
|
|
struct binder_txn_fd_fixup {
|
|
|
|
struct list_head fixup_entry;
|
|
|
|
struct file *file;
|
|
|
|
size_t offset;
|
2022-03-25 23:24:54 +00:00
|
|
|
int target_fd;
|
2020-11-11 03:02:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct binder_transaction {
|
|
|
|
int debug_id;
|
|
|
|
struct binder_work work;
|
|
|
|
struct binder_thread *from;
|
|
|
|
struct binder_transaction *from_parent;
|
|
|
|
struct binder_proc *to_proc;
|
|
|
|
struct binder_thread *to_thread;
|
|
|
|
struct binder_transaction *to_parent;
|
|
|
|
unsigned need_reply:1;
|
|
|
|
/* unsigned is_dead:1; */ /* not used at the moment */
|
|
|
|
|
|
|
|
struct binder_buffer *buffer;
|
|
|
|
unsigned int code;
|
|
|
|
unsigned int flags;
|
|
|
|
long priority;
|
|
|
|
long saved_priority;
|
|
|
|
kuid_t sender_euid;
|
2023-04-24 11:05:14 +00:00
|
|
|
ktime_t start_time;
|
2020-11-11 03:02:42 +00:00
|
|
|
struct list_head fd_fixups;
|
|
|
|
binder_uintptr_t security_ctx;
|
|
|
|
/**
|
|
|
|
* @lock: protects @from, @to_proc, and @to_thread
|
|
|
|
*
|
|
|
|
* @from, @to_proc, and @to_thread can be set to NULL
|
|
|
|
* during thread teardown
|
|
|
|
*/
|
|
|
|
spinlock_t lock;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct binder_object - union of flat binder object types
|
|
|
|
* @hdr: generic object header
|
|
|
|
* @fbo: binder object (nodes and refs)
|
|
|
|
* @fdo: file descriptor object
|
|
|
|
* @bbo: binder buffer pointer
|
|
|
|
* @fdao: file descriptor array
|
|
|
|
*
|
|
|
|
* Used for type-independent object copies
|
|
|
|
*/
|
|
|
|
struct binder_object {
|
|
|
|
union {
|
|
|
|
struct binder_object_header hdr;
|
|
|
|
struct flat_binder_object fbo;
|
|
|
|
struct binder_fd_object fdo;
|
|
|
|
struct binder_buffer_object bbo;
|
|
|
|
struct binder_fd_array_object fdao;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
binder: implement binderfs
As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the
implementation of binderfs.
/* Abstract */
binderfs is a backwards-compatible filesystem for Android's binder ipc
mechanism. Each ipc namespace will mount a new binderfs instance. Mounting
binderfs multiple times at different locations in the same ipc namespace
will not cause a new super block to be allocated and hence it will be the
same filesystem instance.
Each new binderfs mount will have its own set of binder devices only
visible in the ipc namespace it has been mounted in. All devices in a new
binderfs mount will follow the scheme binder%d and numbering will always
start at 0.
/* Backwards compatibility */
Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the
initial ipc namespace will work as before. They will be registered via
misc_register() and appear in the devtmpfs mount. Specifically, the
standard devices binder, hwbinder, and vndbinder will all appear in their
standard locations in /dev. Mounting or unmounting the binderfs mount in
the initial ipc namespace will have no effect on these devices, i.e. they
will neither show up in the binderfs mount nor will they disappear when the
binderfs mount is gone.
/* binder-control */
Each new binderfs instance comes with a binder-control device. No other
devices will be present at first. The binder-control device can be used to
dynamically allocate binder devices. All requests operate on the binderfs
mount the binder-control device resides in.
Assuming a new instance of binderfs has been mounted at /dev/binderfs
via mount -t binderfs binderfs /dev/binderfs. Then a request to create a
new binder device can be made as illustrated in [2].
Binderfs devices can simply be removed via unlink().
/* Implementation details */
- dynamic major number allocation:
When binderfs is registered as a new filesystem it will dynamically
allocate a new major number. The allocated major number will be returned
in struct binderfs_device when a new binder device is allocated.
- global minor number tracking:
Minor are tracked in a global idr struct that is capped at
BINDERFS_MAX_MINOR. The minor number tracker is protected by a global
mutex. This is the only point of contention between binderfs mounts.
- struct binderfs_info:
Each binderfs super block has its own struct binderfs_info that tracks
specific details about a binderfs instance:
- ipc namespace
- dentry of the binder-control device
- root uid and root gid of the user namespace the binderfs instance
was mounted in
- mountable by user namespace root:
binderfs can be mounted by user namespace root in a non-initial user
namespace. The devices will be owned by user namespace root.
- binderfs binder devices without misc infrastructure:
New binder devices associated with a binderfs mount do not use the
full misc_register() infrastructure.
The misc_register() infrastructure can only create new devices in the
host's devtmpfs mount. binderfs does however only make devices appear
under its own mountpoint and thus allocates new character device nodes
from the inode of the root dentry of the super block. This will have
the side-effect that binderfs specific device nodes do not appear in
sysfs. This behavior is similar to devpts allocated pts devices and
has no effect on the functionality of the ipc mechanism itself.
[1]: https://goo.gl/JL2tfX
[2]: program to allocate a new binderfs binder device:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder_ctl.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
size_t len;
struct binderfs_device device = { 0 };
if (argc < 2)
exit(EXIT_FAILURE);
len = strlen(argv[1]);
if (len > BINDERFS_MAX_NAME)
exit(EXIT_FAILURE);
memcpy(device.name, argv[1], len);
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
printf("%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
printf("%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and "
"name %s\n", device.major, device.minor,
device.name);
exit(EXIT_SUCCESS);
}
Cc: Martijn Coenen <maco@android.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-14 12:11:14 +00:00
|
|
|
#endif /* _LINUX_BINDER_INTERNAL_H */
|