Char/Misc fixes for 5.0-rc6

Here are some small char and misc driver fixes for 5.0-rc6.
 
 Nothing huge here, some more binderfs fixups found as people use it, and
 there is a "large" selftest added to validate the binderfs code, which
 makes up the majority of this pull request.
 
 There's also some small mei and mic fixes to resolve some reported
 issues.
 
 All of these have been in linux-next for over a week with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXF03cw8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynULgCeILcTViHw5hJDl719p776CiVjOmMAn2wFXob9
 vk5MXb1j5Uf4liLz2x9K
 =jGjf
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-5.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc fixes from Greg KH:
 "Here are some small char and misc driver fixes for 5.0-rc6.

  Nothing huge here, some more binderfs fixups found as people use it,
  and there is a "large" selftest added to validate the binderfs code,
  which makes up the majority of this pull request.

  There's also some small mei and mic fixes to resolve some reported
  issues.

  All of these have been in linux-next for over a week with no reported
  issues"

* tag 'char-misc-5.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc:
  mic: vop: Fix crash on remove
  mic: vop: Fix use-after-free on remove
  binderfs: remove separate device_initcall()
  fpga: stratix10-soc: fix wrong of_node_put() in init function
  mic: vop: Fix broken virtqueues
  mei: free read cb on ctrl_wr list flush
  samples: mei: use /dev/mei0 instead of /dev/mei
  mei: me: add ice lake point device id.
  binderfs: respect limit on binder control creation
  binder: fix CONFIG_ANDROID_BINDER_DEVICES
  selftests: add binderfs selftests
This commit is contained in:
Linus Torvalds 2019-02-08 10:56:31 -08:00
commit 680905431b
14 changed files with 380 additions and 55 deletions

View File

@ -5854,9 +5854,10 @@ static int __init init_binder_device(const char *name)
static int __init binder_init(void)
{
int ret;
char *device_name, *device_names, *device_tmp;
char *device_name, *device_tmp;
struct binder_device *device;
struct hlist_node *tmp;
char *device_names = NULL;
ret = binder_alloc_shrinker_init();
if (ret)
@ -5898,22 +5899,28 @@ static int __init binder_init(void)
&transaction_log_fops);
}
/*
* Copy the module_parameter string, because we don't want to
* tokenize it in-place.
*/
device_names = kstrdup(binder_devices_param, GFP_KERNEL);
if (!device_names) {
ret = -ENOMEM;
goto err_alloc_device_names_failed;
if (strcmp(binder_devices_param, "") != 0) {
/*
* Copy the module_parameter string, because we don't want to
* tokenize it in-place.
*/
device_names = kstrdup(binder_devices_param, GFP_KERNEL);
if (!device_names) {
ret = -ENOMEM;
goto err_alloc_device_names_failed;
}
device_tmp = device_names;
while ((device_name = strsep(&device_tmp, ","))) {
ret = init_binder_device(device_name);
if (ret)
goto err_init_binder_device_failed;
}
}
device_tmp = device_names;
while ((device_name = strsep(&device_tmp, ","))) {
ret = init_binder_device(device_name);
if (ret)
goto err_init_binder_device_failed;
}
ret = init_binderfs();
if (ret)
goto err_init_binder_device_failed;
return ret;

View File

@ -46,4 +46,13 @@ static inline bool is_binderfs_device(const struct inode *inode)
}
#endif
#ifdef CONFIG_ANDROID_BINDERFS
extern int __init init_binderfs(void);
#else
static inline int __init init_binderfs(void)
{
return 0;
}
#endif
#endif /* _LINUX_BINDER_INTERNAL_H */

View File

@ -395,6 +395,11 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
struct inode *inode = NULL;
struct dentry *root = sb->s_root;
struct binderfs_info *info = sb->s_fs_info;
#if defined(CONFIG_IPC_NS)
bool use_reserve = (info->ipc_ns == &init_ipc_ns);
#else
bool use_reserve = true;
#endif
device = kzalloc(sizeof(*device), GFP_KERNEL);
if (!device)
@ -413,7 +418,10 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
/* Reserve a new minor number for the new device. */
mutex_lock(&binderfs_minors_mutex);
minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL);
minor = ida_alloc_max(&binderfs_minors,
use_reserve ? BINDERFS_MAX_MINOR :
BINDERFS_MAX_MINOR_CAPPED,
GFP_KERNEL);
mutex_unlock(&binderfs_minors_mutex);
if (minor < 0) {
ret = minor;
@ -542,7 +550,7 @@ static struct file_system_type binder_fs_type = {
.fs_flags = FS_USERNS_MOUNT,
};
static int __init init_binderfs(void)
int __init init_binderfs(void)
{
int ret;
@ -560,5 +568,3 @@ static int __init init_binderfs(void)
return ret;
}
device_initcall(init_binderfs);

View File

@ -508,14 +508,11 @@ static int __init s10_init(void)
return -ENODEV;
np = of_find_matching_node(fw_np, s10_of_match);
if (!np) {
of_node_put(fw_np);
if (!np)
return -ENODEV;
}
of_node_put(np);
ret = of_platform_populate(fw_np, s10_of_match, NULL, NULL);
of_node_put(fw_np);
if (ret)
return ret;

View File

@ -401,8 +401,11 @@ static void mei_io_list_flush_cl(struct list_head *head,
struct mei_cl_cb *cb, *next;
list_for_each_entry_safe(cb, next, head, list) {
if (cl == cb->cl)
if (cl == cb->cl) {
list_del_init(&cb->list);
if (cb->fop_type == MEI_FOP_READ)
mei_io_cb_free(cb);
}
}
}

View File

@ -139,6 +139,8 @@
#define MEI_DEV_ID_CNP_H 0xA360 /* Cannon Point H */
#define MEI_DEV_ID_CNP_H_4 0xA364 /* Cannon Point H 4 (iTouch) */
#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */
/*
* MEI HW Section
*/

View File

@ -105,6 +105,8 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_4, MEI_ME_PCH8_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)},
/* required last entry */
{0, }
};

View File

@ -47,7 +47,8 @@
* @dc: Virtio device control
* @vpdev: VOP device which is the parent for this virtio device
* @vr: Buffer for accessing the VRING
* @used: Buffer for used
* @used_virt: Virtual address of used ring
* @used: DMA address of used ring
* @used_size: Size of the used buffer
* @reset_done: Track whether VOP reset is complete
* @virtio_cookie: Cookie returned upon requesting a interrupt
@ -61,6 +62,7 @@ struct _vop_vdev {
struct mic_device_ctrl __iomem *dc;
struct vop_device *vpdev;
void __iomem *vr[VOP_MAX_VRINGS];
void *used_virt[VOP_MAX_VRINGS];
dma_addr_t used[VOP_MAX_VRINGS];
int used_size[VOP_MAX_VRINGS];
struct completion reset_done;
@ -260,12 +262,12 @@ static bool vop_notify(struct virtqueue *vq)
static void vop_del_vq(struct virtqueue *vq, int n)
{
struct _vop_vdev *vdev = to_vopvdev(vq->vdev);
struct vring *vr = (struct vring *)(vq + 1);
struct vop_device *vpdev = vdev->vpdev;
dma_unmap_single(&vpdev->dev, vdev->used[n],
vdev->used_size[n], DMA_BIDIRECTIONAL);
free_pages((unsigned long)vr->used, get_order(vdev->used_size[n]));
free_pages((unsigned long)vdev->used_virt[n],
get_order(vdev->used_size[n]));
vring_del_virtqueue(vq);
vpdev->hw_ops->iounmap(vpdev, vdev->vr[n]);
vdev->vr[n] = NULL;
@ -283,6 +285,26 @@ static void vop_del_vqs(struct virtio_device *dev)
vop_del_vq(vq, idx++);
}
static struct virtqueue *vop_new_virtqueue(unsigned int index,
unsigned int num,
struct virtio_device *vdev,
bool context,
void *pages,
bool (*notify)(struct virtqueue *vq),
void (*callback)(struct virtqueue *vq),
const char *name,
void *used)
{
bool weak_barriers = false;
struct vring vring;
vring_init(&vring, num, pages, MIC_VIRTIO_RING_ALIGN);
vring.used = used;
return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
notify, callback, name);
}
/*
* This routine will assign vring's allocated in host/io memory. Code in
* virtio_ring.c however continues to access this io memory as if it were local
@ -302,7 +324,6 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
struct _mic_vring_info __iomem *info;
void *used;
int vr_size, _vr_size, err, magic;
struct vring *vr;
u8 type = ioread8(&vdev->desc->type);
if (index >= ioread8(&vdev->desc->num_vq))
@ -322,17 +343,7 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
return ERR_PTR(-ENOMEM);
vdev->vr[index] = va;
memset_io(va, 0x0, _vr_size);
vq = vring_new_virtqueue(
index,
le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN,
dev,
false,
ctx,
(void __force *)va, vop_notify, callback, name);
if (!vq) {
err = -ENOMEM;
goto unmap;
}
info = va + _vr_size;
magic = ioread32(&info->magic);
@ -341,18 +352,27 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
goto unmap;
}
/* Allocate and reassign used ring now */
vdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 +
sizeof(struct vring_used_elem) *
le16_to_cpu(config.num));
used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(vdev->used_size[index]));
vdev->used_virt[index] = used;
if (!used) {
err = -ENOMEM;
dev_err(_vop_dev(vdev), "%s %d err %d\n",
__func__, __LINE__, err);
goto del_vq;
goto unmap;
}
vq = vop_new_virtqueue(index, le16_to_cpu(config.num), dev, ctx,
(void __force *)va, vop_notify, callback,
name, used);
if (!vq) {
err = -ENOMEM;
goto free_used;
}
vdev->used[index] = dma_map_single(&vpdev->dev, used,
vdev->used_size[index],
DMA_BIDIRECTIONAL);
@ -360,26 +380,17 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
err = -ENOMEM;
dev_err(_vop_dev(vdev), "%s %d err %d\n",
__func__, __LINE__, err);
goto free_used;
goto del_vq;
}
writeq(vdev->used[index], &vqconfig->used_address);
/*
* To reassign the used ring here we are directly accessing
* struct vring_virtqueue which is a private data structure
* in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in
* vring_new_virtqueue() would ensure that
* (&vq->vring == (struct vring *) (&vq->vq + 1));
*/
vr = (struct vring *)(vq + 1);
vr->used = used;
vq->priv = vdev;
return vq;
del_vq:
vring_del_virtqueue(vq);
free_used:
free_pages((unsigned long)used,
get_order(vdev->used_size[index]));
del_vq:
vring_del_virtqueue(vq);
unmap:
vpdev->hw_ops->iounmap(vpdev, vdev->vr[index]);
return ERR_PTR(err);
@ -581,6 +592,8 @@ static int _vop_remove_device(struct mic_device_desc __iomem *d,
int ret = -1;
if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) {
struct device *dev = get_device(&vdev->vdev.dev);
dev_dbg(&vpdev->dev,
"%s %d config_change %d type %d vdev %p\n",
__func__, __LINE__,
@ -592,7 +605,7 @@ static int _vop_remove_device(struct mic_device_desc __iomem *d,
iowrite8(-1, &dc->h2c_vdev_db);
if (status & VIRTIO_CONFIG_S_DRIVER_OK)
wait_for_completion(&vdev->reset_done);
put_device(&vdev->vdev.dev);
put_device(dev);
iowrite8(1, &dc->guest_ack);
dev_dbg(&vpdev->dev, "%s %d guest_ack %d\n",
__func__, __LINE__, ioread8(&dc->guest_ack));

View File

@ -117,7 +117,7 @@ static bool mei_init(struct mei *me, const uuid_le *guid,
me->verbose = verbose;
me->fd = open("/dev/mei", O_RDWR);
me->fd = open("/dev/mei0", O_RDWR);
if (me->fd == -1) {
mei_err(me, "Cannot establish a handle to the Intel MEI driver\n");
goto err;

View File

@ -10,6 +10,7 @@ TARGETS += drivers/dma-buf
TARGETS += efivarfs
TARGETS += exec
TARGETS += filesystems
TARGETS += filesystems/binderfs
TARGETS += firmware
TARGETS += ftrace
TARGETS += futex

View File

@ -0,0 +1 @@
binderfs_test

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += -I../../../../../usr/include/
TEST_GEN_PROGS := binderfs_test
include ../../lib.mk

View File

@ -0,0 +1,275 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder.h>
#include <linux/android/binderfs.h>
#include "../../kselftest.h"
static ssize_t write_nointr(int fd, const void *buf, size_t count)
{
ssize_t ret;
again:
ret = write(fd, buf, count);
if (ret < 0 && errno == EINTR)
goto again;
return ret;
}
static void write_to_file(const char *filename, const void *buf, size_t count,
int allowed_errno)
{
int fd, saved_errno;
ssize_t ret;
fd = open(filename, O_WRONLY | O_CLOEXEC);
if (fd < 0)
ksft_exit_fail_msg("%s - Failed to open file %s\n",
strerror(errno), filename);
ret = write_nointr(fd, buf, count);
if (ret < 0) {
if (allowed_errno && (errno == allowed_errno)) {
close(fd);
return;
}
goto on_error;
}
if ((size_t)ret != count)
goto on_error;
close(fd);
return;
on_error:
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0)
ksft_exit_fail_msg("%s - Failed to write to file %s\n",
strerror(errno), filename);
ksft_exit_fail_msg("Failed to write to file %s\n", filename);
}
static void change_to_userns(void)
{
int ret;
uid_t uid;
gid_t gid;
/* {g,u}id_map files only allow a max of 4096 bytes written to them */
char idmap[4096];
uid = getuid();
gid = getgid();
ret = unshare(CLONE_NEWUSER);
if (ret < 0)
ksft_exit_fail_msg("%s - Failed to unshare user namespace\n",
strerror(errno));
write_to_file("/proc/self/setgroups", "deny", strlen("deny"), ENOENT);
ret = snprintf(idmap, sizeof(idmap), "0 %d 1", uid);
if (ret < 0 || (size_t)ret >= sizeof(idmap))
ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
strerror(errno));
write_to_file("/proc/self/uid_map", idmap, strlen(idmap), 0);
ret = snprintf(idmap, sizeof(idmap), "0 %d 1", gid);
if (ret < 0 || (size_t)ret >= sizeof(idmap))
ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
strerror(errno));
write_to_file("/proc/self/gid_map", idmap, strlen(idmap), 0);
ret = setgid(0);
if (ret)
ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
strerror(errno));
ret = setuid(0);
if (ret)
ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
strerror(errno));
}
static void change_to_mountns(void)
{
int ret;
ret = unshare(CLONE_NEWNS);
if (ret < 0)
ksft_exit_fail_msg("%s - Failed to unshare mount namespace\n",
strerror(errno));
ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
if (ret < 0)
ksft_exit_fail_msg("%s - Failed to mount / as private\n",
strerror(errno));
}
static void rmdir_protect_errno(const char *dir)
{
int saved_errno = errno;
(void)rmdir(dir);
errno = saved_errno;
}
static void __do_binderfs_test(void)
{
int fd, ret, saved_errno;
size_t len;
ssize_t wret;
bool keep = false;
struct binderfs_device device = { 0 };
struct binder_version version = { 0 };
change_to_mountns();
ret = mkdir("/dev/binderfs", 0755);
if (ret < 0) {
if (errno != EEXIST)
ksft_exit_fail_msg(
"%s - Failed to create binderfs mountpoint\n",
strerror(errno));
keep = true;
}
ret = mount(NULL, "/dev/binderfs", "binder", 0, 0);
if (ret < 0) {
if (errno != ENODEV)
ksft_exit_fail_msg("%s - Failed to mount binderfs\n",
strerror(errno));
keep ? : rmdir_protect_errno("/dev/binderfs");
ksft_exit_skip(
"The Android binderfs filesystem is not available\n");
}
/* binderfs mount test passed */
ksft_inc_pass_cnt();
memcpy(device.name, "my-binder", strlen("my-binder"));
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0)
ksft_exit_fail_msg(
"%s - Failed to open binder-control device\n",
strerror(errno));
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
keep ? : rmdir_protect_errno("/dev/binderfs");
ksft_exit_fail_msg(
"%s - Failed to allocate new binder device\n",
strerror(errno));
}
ksft_print_msg(
"Allocated new binder device with major %d, minor %d, and name %s\n",
device.major, device.minor, device.name);
/* binder device allocation test passed */
ksft_inc_pass_cnt();
fd = open("/dev/binderfs/my-binder", O_CLOEXEC | O_RDONLY);
if (fd < 0) {
keep ? : rmdir_protect_errno("/dev/binderfs");
ksft_exit_fail_msg("%s - Failed to open my-binder device\n",
strerror(errno));
}
ret = ioctl(fd, BINDER_VERSION, &version);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
keep ? : rmdir_protect_errno("/dev/binderfs");
ksft_exit_fail_msg(
"%s - Failed to open perform BINDER_VERSION request\n",
strerror(errno));
}
ksft_print_msg("Detected binder version: %d\n",
version.protocol_version);
/* binder transaction with binderfs binder device passed */
ksft_inc_pass_cnt();
ret = unlink("/dev/binderfs/my-binder");
if (ret < 0) {
keep ? : rmdir_protect_errno("/dev/binderfs");
ksft_exit_fail_msg("%s - Failed to delete binder device\n",
strerror(errno));
}
/* binder device removal passed */
ksft_inc_pass_cnt();
ret = unlink("/dev/binderfs/binder-control");
if (!ret) {
keep ? : rmdir_protect_errno("/dev/binderfs");
ksft_exit_fail_msg("Managed to delete binder-control device\n");
} else if (errno != EPERM) {
keep ? : rmdir_protect_errno("/dev/binderfs");
ksft_exit_fail_msg(
"%s - Failed to delete binder-control device but exited with unexpected error code\n",
strerror(errno));
}
/* binder-control device removal failed as expected */
ksft_inc_xfail_cnt();
on_error:
ret = umount2("/dev/binderfs", MNT_DETACH);
keep ?: rmdir_protect_errno("/dev/binderfs");
if (ret < 0)
ksft_exit_fail_msg("%s - Failed to unmount binderfs\n",
strerror(errno));
/* binderfs unmount test passed */
ksft_inc_pass_cnt();
}
static void binderfs_test_privileged()
{
if (geteuid() != 0)
ksft_print_msg(
"Tests are not run as root. Skipping privileged tests\n");
else
__do_binderfs_test();
}
static void binderfs_test_unprivileged()
{
change_to_userns();
__do_binderfs_test();
}
int main(int argc, char *argv[])
{
binderfs_test_privileged();
binderfs_test_unprivileged();
ksft_exit_pass();
}

View File

@ -0,0 +1,3 @@
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDERFS=y
CONFIG_ANDROID_BINDER_IPC=y