f0fe2c0f05
This is a necessary follow up to the first fix I proposed and we merged in2669b8b0c7
("binder: prevent UAF for binderfs devices"). I have been overly optimistic that the simple fix I proposed would work. But alas, ihold() + iput() won't work since the inodes won't survive the destruction of the superblock. So all we get with my prior fix is a different race with a tinier race-window but it doesn't solve the issue. Fwiw, the problem lies with generic_shutdown_super(). It even has this cozy Al-style comment: if (!list_empty(&sb->s_inodes)) { printk("VFS: Busy inodes after unmount of %s. " "Self-destruct in 5 seconds. Have a nice day...\n", sb->s_id); } On binder_release(), binder_defer_work(proc, BINDER_DEFERRED_RELEASE) is called which punts the actual cleanup operation to a workqueue. At some point, binder_deferred_func() will be called which will end up calling binder_deferred_release() which will retrieve and cleanup the binder_context attach to this struct binder_proc. If we trace back where this binder_context is attached to binder_proc we see that it is set in binder_open() and is taken from the struct binder_device it is associated with. This obviously assumes that the struct binder_device that context is attached to is _never_ freed. While that might be true for devtmpfs binder devices it is most certainly wrong for binderfs binder devices. So, assume binder_open() is called on a binderfs binder devices. We now stash away the struct binder_context associated with that struct binder_devices: proc->context = &binder_dev->context; /* binderfs stashes devices in i_private */ if (is_binderfs_device(nodp)) { binder_dev = nodp->i_private; info = nodp->i_sb->s_fs_info; binder_binderfs_dir_entry_proc = info->proc_log_dir; } else { . . . proc->context = &binder_dev->context; Now let's assume that the binderfs instance for that binder devices is shutdown via umount() and/or the mount namespace associated with it goes away. As long as there is still an fd open for that binderfs binder device things are fine. But let's assume we now close the last fd for that binderfs binder device. Now binder_release() is called and punts to the workqueue. Assume that the workqueue has quite a bit of stuff to do and doesn't get to cleaning up the struct binder_proc and the associated struct binder_context with it for that binderfs binder device right away. In the meantime, the VFS is killing the super block and is ultimately calling sb->evict_inode() which means it will call binderfs_evict_inode() which does: static void binderfs_evict_inode(struct inode *inode) { struct binder_device *device = inode->i_private; struct binderfs_info *info = BINDERFS_I(inode); clear_inode(inode); if (!S_ISCHR(inode->i_mode) || !device) return; mutex_lock(&binderfs_minors_mutex); --info->device_count; ida_free(&binderfs_minors, device->miscdev.minor); mutex_unlock(&binderfs_minors_mutex); kfree(device->context.name); kfree(device); } thereby freeing the struct binder_device including struct binder_context. Now the workqueue finally has time to get around to cleaning up struct binder_proc and is now trying to access the associate struct binder_context. Since it's already freed it will OOPs. Fix this by introducing a refounct on binder devices. This is an alternative fix to51d8a7eca6
("binder: prevent UAF read in print_binder_transaction_log_entry()"). Fixes:3ad20fe393
("binder: implement binderfs") Fixes:2669b8b0c7
("binder: prevent UAF for binderfs devices") Fixes:03e2e07e38
("binder: Make transaction_log available in binderfs") Related :51d8a7eca6
("binder: prevent UAF read in print_binder_transaction_log_entry()") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com> Acked-by: Todd Kjos <tkjos@google.com> Link: https://lore.kernel.org/r/20200303164340.670054-1-christian.brauner@ubuntu.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
147 lines
4.0 KiB
C
147 lines
4.0 KiB
C
/* 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>
|
|
#include <linux/refcount.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/types.h>
|
|
#include <linux/uidgid.h>
|
|
|
|
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;
|
|
refcount_t ref;
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
};
|
|
|
|
extern const struct file_operations binder_fops;
|
|
|
|
extern char *binder_devices_param;
|
|
|
|
#ifdef CONFIG_ANDROID_BINDERFS
|
|
extern bool is_binderfs_device(const struct inode *inode);
|
|
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);
|
|
#else
|
|
static inline bool is_binderfs_device(const struct inode *inode)
|
|
{
|
|
return false;
|
|
}
|
|
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) {}
|
|
#endif
|
|
|
|
#ifdef CONFIG_ANDROID_BINDERFS
|
|
extern int __init init_binderfs(void);
|
|
#else
|
|
static inline int __init init_binderfs(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int binder_stats_show(struct seq_file *m, void *unused);
|
|
DEFINE_SHOW_ATTRIBUTE(binder_stats);
|
|
|
|
int binder_state_show(struct seq_file *m, void *unused);
|
|
DEFINE_SHOW_ATTRIBUTE(binder_state);
|
|
|
|
int binder_transactions_show(struct seq_file *m, void *unused);
|
|
DEFINE_SHOW_ATTRIBUTE(binder_transactions);
|
|
|
|
int binder_transaction_log_show(struct seq_file *m, void *unused);
|
|
DEFINE_SHOW_ATTRIBUTE(binder_transaction_log);
|
|
|
|
struct binder_transaction_log_entry {
|
|
int debug_id;
|
|
int debug_id_done;
|
|
int call_type;
|
|
int from_proc;
|
|
int from_thread;
|
|
int target_handle;
|
|
int to_proc;
|
|
int to_thread;
|
|
int to_node;
|
|
int data_size;
|
|
int offsets_size;
|
|
int return_error_line;
|
|
uint32_t return_error;
|
|
uint32_t return_error_param;
|
|
char context_name[BINDERFS_MAX_NAME + 1];
|
|
};
|
|
|
|
struct binder_transaction_log {
|
|
atomic_t cur;
|
|
bool full;
|
|
struct binder_transaction_log_entry entry[32];
|
|
};
|
|
|
|
extern struct binder_transaction_log binder_transaction_log;
|
|
extern struct binder_transaction_log binder_transaction_log_failed;
|
|
#endif /* _LINUX_BINDER_INTERNAL_H */
|