Commit a444ad14 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'netdevsim-fix-several-bugs-in-netdevsim-module'

Taehee Yoo says:

=====================
netdevsim: fix several bugs in netdevsim module

This patchset fixes several bugs in netdevsim module.

1. The first patch fixes using uninitialized resources
This patch fixes two similar problems, which is to use uninitialized
resources.
a) In the current code, {new/del}_device_store() use resource,
they are initialized by __init().
But, these functions could be called before __init() is finished.
So, accessing uninitialized data could occur and it eventually makes panic.
b) In the current code, {new/del}_port_store() uses resource,
they are initialized by new_device_store().
But thes functions could be called before new_device_store() is finished.

2. The second patch fixes another race condition.
The main problem is a race condition in {new/del}_port() and devlink reload
function.
These functions would allocate and remove resources. So these functions
should not be executed concurrently.

3. The third patch fixes a panic in nsim_dev_take_snapshot_write().
nsim_dev_take_snapshot_write() uses nsim_dev and nsim_dev->dummy_region.
But these data could be removed by both reload routine and
del_device_store(). And these functions could be executed concurrently.

4. The fourth patch fixes stack-out-of-bound in nsim_dev_debugfs_init().
nsim_dev_debugfs_init() provides only 16bytes for name pointer.
But, there are some case the name length is over 16bytes.
So, stack-out-of-bound occurs.

5. The fifth patch uses IS_ERR instead of IS_ERR_OR_NULL.
debugfs_create_{dir/file} doesn't return NULL.
So, IS_ERR() is more correct.

6. The sixth patch avoids kmalloc warning.
When too large memory allocation is requested by user-space, kmalloc
internally prints warning message.
That warning message is not necessary.
In order to avoid that, it adds __GFP_NOWARN.

7. The last patch removes an unused sdev.c file

Change log:

v2 -> v3:
 - Use smp_load_acquire() and smp_store_release() for flag variables.
 - Change variable names.
 - Fix deadlock in second patch.
 - Update lock variable comment.
 - Add new patch for fixing panic in snapshot_write().
 - Include Reviewed-by tags.
 - Update some log messages and comment.

v1 -> v2:
 - Splits a fixing race condition patch into two patches.
 - Fix incorrect Fixes tags.
 - Update comments
 - Fix use-after-free
 - Add a new patch, which removes an unused sdev.c file.
 - Remove a patch, which tries to avoid debugfs warning.
=====================
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 2b5ea294 24531163
......@@ -218,6 +218,7 @@ static int nsim_bpf_create_prog(struct nsim_dev *nsim_dev,
{
struct nsim_bpf_bound_prog *state;
char name[16];
int ret;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
......@@ -230,9 +231,10 @@ static int nsim_bpf_create_prog(struct nsim_dev *nsim_dev,
/* Program id is not populated yet when we create the state. */
sprintf(name, "%u", nsim_dev->prog_id_gen++);
state->ddir = debugfs_create_dir(name, nsim_dev->ddir_bpf_bound_progs);
if (IS_ERR_OR_NULL(state->ddir)) {
if (IS_ERR(state->ddir)) {
ret = PTR_ERR(state->ddir);
kfree(state);
return -ENOMEM;
return ret;
}
debugfs_create_u32("id", 0400, state->ddir, &prog->aux->id);
......@@ -587,8 +589,8 @@ int nsim_bpf_dev_init(struct nsim_dev *nsim_dev)
nsim_dev->ddir_bpf_bound_progs = debugfs_create_dir("bpf_bound_progs",
nsim_dev->ddir);
if (IS_ERR_OR_NULL(nsim_dev->ddir_bpf_bound_progs))
return -ENOMEM;
if (IS_ERR(nsim_dev->ddir_bpf_bound_progs))
return PTR_ERR(nsim_dev->ddir_bpf_bound_progs);
nsim_dev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops, nsim_dev);
err = PTR_ERR_OR_ZERO(nsim_dev->bpf_dev);
......
......@@ -17,6 +17,7 @@
static DEFINE_IDA(nsim_bus_dev_ids);
static LIST_HEAD(nsim_bus_dev_list);
static DEFINE_MUTEX(nsim_bus_dev_list_lock);
static bool nsim_bus_enable;
static struct nsim_bus_dev *to_nsim_bus_dev(struct device *dev)
{
......@@ -28,7 +29,7 @@ static int nsim_bus_dev_vfs_enable(struct nsim_bus_dev *nsim_bus_dev,
{
nsim_bus_dev->vfconfigs = kcalloc(num_vfs,
sizeof(struct nsim_vf_config),
GFP_KERNEL);
GFP_KERNEL | __GFP_NOWARN);
if (!nsim_bus_dev->vfconfigs)
return -ENOMEM;
nsim_bus_dev->num_vfs = num_vfs;
......@@ -96,13 +97,25 @@ new_port_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
struct nsim_dev *nsim_dev = dev_get_drvdata(dev);
struct devlink *devlink;
unsigned int port_index;
int ret;
/* Prevent to use nsim_bus_dev before initialization. */
if (!smp_load_acquire(&nsim_bus_dev->init))
return -EBUSY;
ret = kstrtouint(buf, 0, &port_index);
if (ret)
return ret;
devlink = priv_to_devlink(nsim_dev);
mutex_lock(&nsim_bus_dev->nsim_bus_reload_lock);
devlink_reload_disable(devlink);
ret = nsim_dev_port_add(nsim_bus_dev, port_index);
devlink_reload_enable(devlink);
mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
return ret ? ret : count;
}
......@@ -113,13 +126,25 @@ del_port_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
struct nsim_dev *nsim_dev = dev_get_drvdata(dev);
struct devlink *devlink;
unsigned int port_index;
int ret;
/* Prevent to use nsim_bus_dev before initialization. */
if (!smp_load_acquire(&nsim_bus_dev->init))
return -EBUSY;
ret = kstrtouint(buf, 0, &port_index);
if (ret)
return ret;
devlink = priv_to_devlink(nsim_dev);
mutex_lock(&nsim_bus_dev->nsim_bus_reload_lock);
devlink_reload_disable(devlink);
ret = nsim_dev_port_del(nsim_bus_dev, port_index);
devlink_reload_enable(devlink);
mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
return ret ? ret : count;
}
......@@ -179,15 +204,30 @@ new_device_store(struct bus_type *bus, const char *buf, size_t count)
pr_err("Format for adding new device is \"id port_count\" (uint uint).\n");
return -EINVAL;
}
nsim_bus_dev = nsim_bus_dev_new(id, port_count);
if (IS_ERR(nsim_bus_dev))
return PTR_ERR(nsim_bus_dev);
mutex_lock(&nsim_bus_dev_list_lock);
/* Prevent to use resource before initialization. */
if (!smp_load_acquire(&nsim_bus_enable)) {
err = -EBUSY;
goto err;
}
nsim_bus_dev = nsim_bus_dev_new(id, port_count);
if (IS_ERR(nsim_bus_dev)) {
err = PTR_ERR(nsim_bus_dev);
goto err;
}
/* Allow using nsim_bus_dev */
smp_store_release(&nsim_bus_dev->init, true);
list_add_tail(&nsim_bus_dev->list, &nsim_bus_dev_list);
mutex_unlock(&nsim_bus_dev_list_lock);
return count;
err:
mutex_unlock(&nsim_bus_dev_list_lock);
return err;
}
static BUS_ATTR_WO(new_device);
......@@ -215,6 +255,11 @@ del_device_store(struct bus_type *bus, const char *buf, size_t count)
err = -ENOENT;
mutex_lock(&nsim_bus_dev_list_lock);
/* Prevent to use resource before initialization. */
if (!smp_load_acquire(&nsim_bus_enable)) {
mutex_unlock(&nsim_bus_dev_list_lock);
return -EBUSY;
}
list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) {
if (nsim_bus_dev->dev.id != id)
continue;
......@@ -284,6 +329,9 @@ nsim_bus_dev_new(unsigned int id, unsigned int port_count)
nsim_bus_dev->dev.type = &nsim_bus_dev_type;
nsim_bus_dev->port_count = port_count;
nsim_bus_dev->initial_net = current->nsproxy->net_ns;
mutex_init(&nsim_bus_dev->nsim_bus_reload_lock);
/* Disallow using nsim_bus_dev */
smp_store_release(&nsim_bus_dev->init, false);
err = device_register(&nsim_bus_dev->dev);
if (err)
......@@ -299,6 +347,8 @@ nsim_bus_dev_new(unsigned int id, unsigned int port_count)
static void nsim_bus_dev_del(struct nsim_bus_dev *nsim_bus_dev)
{
/* Disallow using nsim_bus_dev */
smp_store_release(&nsim_bus_dev->init, false);
device_unregister(&nsim_bus_dev->dev);
ida_free(&nsim_bus_dev_ids, nsim_bus_dev->dev.id);
kfree(nsim_bus_dev);
......@@ -320,6 +370,8 @@ int nsim_bus_init(void)
err = driver_register(&nsim_driver);
if (err)
goto err_bus_unregister;
/* Allow using resources */
smp_store_release(&nsim_bus_enable, true);
return 0;
err_bus_unregister:
......@@ -331,12 +383,16 @@ void nsim_bus_exit(void)
{
struct nsim_bus_dev *nsim_bus_dev, *tmp;
/* Disallow using resources */
smp_store_release(&nsim_bus_enable, false);
mutex_lock(&nsim_bus_dev_list_lock);
list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) {
list_del(&nsim_bus_dev->list);
nsim_bus_dev_del(nsim_bus_dev);
}
mutex_unlock(&nsim_bus_dev_list_lock);
driver_unregister(&nsim_driver);
bus_unregister(&nsim_bus);
}
......@@ -73,23 +73,26 @@ static const struct file_operations nsim_dev_take_snapshot_fops = {
static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
{
char dev_ddir_name[16];
char dev_ddir_name[sizeof(DRV_NAME) + 10];
sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
if (IS_ERR_OR_NULL(nsim_dev->ddir))
return PTR_ERR_OR_ZERO(nsim_dev->ddir) ?: -EINVAL;
if (IS_ERR(nsim_dev->ddir))
return PTR_ERR(nsim_dev->ddir);
nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
if (IS_ERR_OR_NULL(nsim_dev->ports_ddir))
return PTR_ERR_OR_ZERO(nsim_dev->ports_ddir) ?: -EINVAL;
if (IS_ERR(nsim_dev->ports_ddir))
return PTR_ERR(nsim_dev->ports_ddir);
debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
&nsim_dev->fw_update_status);
debugfs_create_u32("max_macs", 0600, nsim_dev->ddir,
&nsim_dev->max_macs);
debugfs_create_bool("test1", 0600, nsim_dev->ddir,
&nsim_dev->test1);
debugfs_create_file("take_snapshot", 0200, nsim_dev->ddir, nsim_dev,
&nsim_dev_take_snapshot_fops);
nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
0200,
nsim_dev->ddir,
nsim_dev,
&nsim_dev_take_snapshot_fops);
debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir,
&nsim_dev->dont_allow_reload);
debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir,
......@@ -112,8 +115,8 @@ static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
nsim_dev->ports_ddir);
if (IS_ERR_OR_NULL(nsim_dev_port->ddir))
return -ENOMEM;
if (IS_ERR(nsim_dev_port->ddir))
return PTR_ERR(nsim_dev_port->ddir);
sprintf(dev_link_name, "../../../" DRV_NAME "%u",
nsim_dev->nsim_bus_dev->dev.id);
......@@ -740,6 +743,11 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
if (err)
goto err_health_exit;
nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
0200,
nsim_dev->ddir,
nsim_dev,
&nsim_dev_take_snapshot_fops);
return 0;
err_health_exit:
......@@ -853,6 +861,7 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
if (devlink_is_reload_failed(devlink))
return;
debugfs_remove(nsim_dev->take_snapshot);
nsim_dev_port_del_all(nsim_dev);
nsim_dev_health_exit(nsim_dev);
nsim_dev_traps_exit(devlink);
......@@ -925,8 +934,8 @@ int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
int nsim_dev_init(void)
{
nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
if (IS_ERR_OR_NULL(nsim_dev_ddir))
return -ENOMEM;
if (IS_ERR(nsim_dev_ddir))
return PTR_ERR(nsim_dev_ddir);
return 0;
}
......
......@@ -82,7 +82,7 @@ static int nsim_dev_dummy_fmsg_put(struct devlink_fmsg *fmsg, u32 binary_len)
if (err)
return err;
binary = kmalloc(binary_len, GFP_KERNEL);
binary = kmalloc(binary_len, GFP_KERNEL | __GFP_NOWARN);
if (!binary)
return -ENOMEM;
get_random_bytes(binary, binary_len);
......@@ -285,8 +285,8 @@ int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
}
health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
if (IS_ERR_OR_NULL(health->ddir)) {
err = PTR_ERR_OR_ZERO(health->ddir) ?: -EINVAL;
if (IS_ERR(health->ddir)) {
err = PTR_ERR(health->ddir);
goto err_dummy_reporter_destroy;
}
......
......@@ -160,6 +160,7 @@ struct nsim_dev {
struct nsim_trap_data *trap_data;
struct dentry *ddir;
struct dentry *ports_ddir;
struct dentry *take_snapshot;
struct bpf_offload_dev *bpf_dev;
bool bpf_bind_accept;
u32 bpf_bind_verifier_delay;
......@@ -240,6 +241,9 @@ struct nsim_bus_dev {
*/
unsigned int num_vfs;
struct nsim_vf_config *vfconfigs;
/* Lock for devlink->reload_enabled in netdevsim module */
struct mutex nsim_bus_reload_lock;
bool init;
};
int nsim_bus_init(void);
......
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include "netdevsim.h"
static struct dentry *nsim_sdev_ddir;
static u32 nsim_sdev_id;
struct netdevsim_shared_dev *nsim_sdev_get(struct netdevsim *joinns)
{
struct netdevsim_shared_dev *sdev;
char sdev_ddir_name[10];
int err;
if (joinns) {
if (WARN_ON(!joinns->sdev))
return ERR_PTR(-EINVAL);
sdev = joinns->sdev;
sdev->refcnt++;
return sdev;
}
sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
if (!sdev)
return ERR_PTR(-ENOMEM);
sdev->refcnt = 1;
sdev->switch_id = nsim_sdev_id++;
sprintf(sdev_ddir_name, "%u", sdev->switch_id);
sdev->ddir = debugfs_create_dir(sdev_ddir_name, nsim_sdev_ddir);
if (IS_ERR_OR_NULL(sdev->ddir)) {
err = PTR_ERR_OR_ZERO(sdev->ddir) ?: -EINVAL;
goto err_sdev_free;
}
return sdev;
err_sdev_free:
nsim_sdev_id--;
kfree(sdev);
return ERR_PTR(err);
}
void nsim_sdev_put(struct netdevsim_shared_dev *sdev)
{
if (--sdev->refcnt)
return;
debugfs_remove_recursive(sdev->ddir);
kfree(sdev);
}
int nsim_sdev_init(void)
{
nsim_sdev_ddir = debugfs_create_dir(DRV_NAME "_sdev", NULL);
if (IS_ERR_OR_NULL(nsim_sdev_ddir))
return -ENOMEM;
return 0;
}
void nsim_sdev_exit(void)
{
debugfs_remove_recursive(nsim_sdev_ddir);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment