Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Jakub Kicinski <kuba@kernel.org>

+93 -91
+6 -4
drivers/net/netdevsim/bpf.c
··· 218 218 { 219 219 struct nsim_bpf_bound_prog *state; 220 220 char name[16]; 221 + int ret; 221 222 222 223 state = kzalloc(sizeof(*state), GFP_KERNEL); 223 224 if (!state) ··· 231 230 /* Program id is not populated yet when we create the state. */ 232 231 sprintf(name, "%u", nsim_dev->prog_id_gen++); 233 232 state->ddir = debugfs_create_dir(name, nsim_dev->ddir_bpf_bound_progs); 234 - if (IS_ERR_OR_NULL(state->ddir)) { 233 + if (IS_ERR(state->ddir)) { 234 + ret = PTR_ERR(state->ddir); 235 235 kfree(state); 236 - return -ENOMEM; 236 + return ret; 237 237 } 238 238 239 239 debugfs_create_u32("id", 0400, state->ddir, &prog->aux->id); ··· 589 587 590 588 nsim_dev->ddir_bpf_bound_progs = debugfs_create_dir("bpf_bound_progs", 591 589 nsim_dev->ddir); 592 - if (IS_ERR_OR_NULL(nsim_dev->ddir_bpf_bound_progs)) 593 - return -ENOMEM; 590 + if (IS_ERR(nsim_dev->ddir_bpf_bound_progs)) 591 + return PTR_ERR(nsim_dev->ddir_bpf_bound_progs); 594 592 595 593 nsim_dev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops, nsim_dev); 596 594 err = PTR_ERR_OR_ZERO(nsim_dev->bpf_dev);
+60 -4
drivers/net/netdevsim/bus.c
··· 17 17 static DEFINE_IDA(nsim_bus_dev_ids); 18 18 static LIST_HEAD(nsim_bus_dev_list); 19 19 static DEFINE_MUTEX(nsim_bus_dev_list_lock); 20 + static bool nsim_bus_enable; 20 21 21 22 static struct nsim_bus_dev *to_nsim_bus_dev(struct device *dev) 22 23 { ··· 29 28 { 30 29 nsim_bus_dev->vfconfigs = kcalloc(num_vfs, 31 30 sizeof(struct nsim_vf_config), 32 - GFP_KERNEL); 31 + GFP_KERNEL | __GFP_NOWARN); 33 32 if (!nsim_bus_dev->vfconfigs) 34 33 return -ENOMEM; 35 34 nsim_bus_dev->num_vfs = num_vfs; ··· 97 96 const char *buf, size_t count) 98 97 { 99 98 struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); 99 + struct nsim_dev *nsim_dev = dev_get_drvdata(dev); 100 + struct devlink *devlink; 100 101 unsigned int port_index; 101 102 int ret; 102 103 104 + /* Prevent to use nsim_bus_dev before initialization. */ 105 + if (!smp_load_acquire(&nsim_bus_dev->init)) 106 + return -EBUSY; 103 107 ret = kstrtouint(buf, 0, &port_index); 104 108 if (ret) 105 109 return ret; 110 + 111 + devlink = priv_to_devlink(nsim_dev); 112 + 113 + mutex_lock(&nsim_bus_dev->nsim_bus_reload_lock); 114 + devlink_reload_disable(devlink); 106 115 ret = nsim_dev_port_add(nsim_bus_dev, port_index); 116 + devlink_reload_enable(devlink); 117 + mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock); 107 118 return ret ? ret : count; 108 119 } 109 120 ··· 126 113 const char *buf, size_t count) 127 114 { 128 115 struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); 116 + struct nsim_dev *nsim_dev = dev_get_drvdata(dev); 117 + struct devlink *devlink; 129 118 unsigned int port_index; 130 119 int ret; 131 120 121 + /* Prevent to use nsim_bus_dev before initialization. */ 122 + if (!smp_load_acquire(&nsim_bus_dev->init)) 123 + return -EBUSY; 132 124 ret = kstrtouint(buf, 0, &port_index); 133 125 if (ret) 134 126 return ret; 127 + 128 + devlink = priv_to_devlink(nsim_dev); 129 + 130 + mutex_lock(&nsim_bus_dev->nsim_bus_reload_lock); 131 + devlink_reload_disable(devlink); 135 132 ret = nsim_dev_port_del(nsim_bus_dev, port_index); 133 + devlink_reload_enable(devlink); 134 + mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock); 136 135 return ret ? ret : count; 137 136 } 138 137 ··· 204 179 pr_err("Format for adding new device is \"id port_count\" (uint uint).\n"); 205 180 return -EINVAL; 206 181 } 207 - nsim_bus_dev = nsim_bus_dev_new(id, port_count); 208 - if (IS_ERR(nsim_bus_dev)) 209 - return PTR_ERR(nsim_bus_dev); 210 182 211 183 mutex_lock(&nsim_bus_dev_list_lock); 184 + /* Prevent to use resource before initialization. */ 185 + if (!smp_load_acquire(&nsim_bus_enable)) { 186 + err = -EBUSY; 187 + goto err; 188 + } 189 + 190 + nsim_bus_dev = nsim_bus_dev_new(id, port_count); 191 + if (IS_ERR(nsim_bus_dev)) { 192 + err = PTR_ERR(nsim_bus_dev); 193 + goto err; 194 + } 195 + 196 + /* Allow using nsim_bus_dev */ 197 + smp_store_release(&nsim_bus_dev->init, true); 198 + 212 199 list_add_tail(&nsim_bus_dev->list, &nsim_bus_dev_list); 213 200 mutex_unlock(&nsim_bus_dev_list_lock); 214 201 215 202 return count; 203 + err: 204 + mutex_unlock(&nsim_bus_dev_list_lock); 205 + return err; 216 206 } 217 207 static BUS_ATTR_WO(new_device); 218 208 ··· 255 215 256 216 err = -ENOENT; 257 217 mutex_lock(&nsim_bus_dev_list_lock); 218 + /* Prevent to use resource before initialization. */ 219 + if (!smp_load_acquire(&nsim_bus_enable)) { 220 + mutex_unlock(&nsim_bus_dev_list_lock); 221 + return -EBUSY; 222 + } 258 223 list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) { 259 224 if (nsim_bus_dev->dev.id != id) 260 225 continue; ··· 329 284 nsim_bus_dev->dev.type = &nsim_bus_dev_type; 330 285 nsim_bus_dev->port_count = port_count; 331 286 nsim_bus_dev->initial_net = current->nsproxy->net_ns; 287 + mutex_init(&nsim_bus_dev->nsim_bus_reload_lock); 288 + /* Disallow using nsim_bus_dev */ 289 + smp_store_release(&nsim_bus_dev->init, false); 332 290 333 291 err = device_register(&nsim_bus_dev->dev); 334 292 if (err) ··· 347 299 348 300 static void nsim_bus_dev_del(struct nsim_bus_dev *nsim_bus_dev) 349 301 { 302 + /* Disallow using nsim_bus_dev */ 303 + smp_store_release(&nsim_bus_dev->init, false); 350 304 device_unregister(&nsim_bus_dev->dev); 351 305 ida_free(&nsim_bus_dev_ids, nsim_bus_dev->dev.id); 352 306 kfree(nsim_bus_dev); ··· 370 320 err = driver_register(&nsim_driver); 371 321 if (err) 372 322 goto err_bus_unregister; 323 + /* Allow using resources */ 324 + smp_store_release(&nsim_bus_enable, true); 373 325 return 0; 374 326 375 327 err_bus_unregister: ··· 383 331 { 384 332 struct nsim_bus_dev *nsim_bus_dev, *tmp; 385 333 334 + /* Disallow using resources */ 335 + smp_store_release(&nsim_bus_enable, false); 336 + 386 337 mutex_lock(&nsim_bus_dev_list_lock); 387 338 list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) { 388 339 list_del(&nsim_bus_dev->list); 389 340 nsim_bus_dev_del(nsim_bus_dev); 390 341 } 391 342 mutex_unlock(&nsim_bus_dev_list_lock); 343 + 392 344 driver_unregister(&nsim_driver); 393 345 bus_unregister(&nsim_bus); 394 346 }
+20 -11
drivers/net/netdevsim/dev.c
··· 73 73 74 74 static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) 75 75 { 76 - char dev_ddir_name[16]; 76 + char dev_ddir_name[sizeof(DRV_NAME) + 10]; 77 77 78 78 sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id); 79 79 nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir); 80 - if (IS_ERR_OR_NULL(nsim_dev->ddir)) 81 - return PTR_ERR_OR_ZERO(nsim_dev->ddir) ?: -EINVAL; 80 + if (IS_ERR(nsim_dev->ddir)) 81 + return PTR_ERR(nsim_dev->ddir); 82 82 nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir); 83 - if (IS_ERR_OR_NULL(nsim_dev->ports_ddir)) 84 - return PTR_ERR_OR_ZERO(nsim_dev->ports_ddir) ?: -EINVAL; 83 + if (IS_ERR(nsim_dev->ports_ddir)) 84 + return PTR_ERR(nsim_dev->ports_ddir); 85 85 debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir, 86 86 &nsim_dev->fw_update_status); 87 87 debugfs_create_u32("max_macs", 0600, nsim_dev->ddir, 88 88 &nsim_dev->max_macs); 89 89 debugfs_create_bool("test1", 0600, nsim_dev->ddir, 90 90 &nsim_dev->test1); 91 - debugfs_create_file("take_snapshot", 0200, nsim_dev->ddir, nsim_dev, 92 - &nsim_dev_take_snapshot_fops); 91 + nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 92 + 0200, 93 + nsim_dev->ddir, 94 + nsim_dev, 95 + &nsim_dev_take_snapshot_fops); 93 96 debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir, 94 97 &nsim_dev->dont_allow_reload); 95 98 debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir, ··· 115 112 sprintf(port_ddir_name, "%u", nsim_dev_port->port_index); 116 113 nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name, 117 114 nsim_dev->ports_ddir); 118 - if (IS_ERR_OR_NULL(nsim_dev_port->ddir)) 119 - return -ENOMEM; 115 + if (IS_ERR(nsim_dev_port->ddir)) 116 + return PTR_ERR(nsim_dev_port->ddir); 120 117 121 118 sprintf(dev_link_name, "../../../" DRV_NAME "%u", 122 119 nsim_dev->nsim_bus_dev->dev.id); ··· 743 740 if (err) 744 741 goto err_health_exit; 745 742 743 + nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 744 + 0200, 745 + nsim_dev->ddir, 746 + nsim_dev, 747 + &nsim_dev_take_snapshot_fops); 746 748 return 0; 747 749 748 750 err_health_exit: ··· 861 853 862 854 if (devlink_is_reload_failed(devlink)) 863 855 return; 856 + debugfs_remove(nsim_dev->take_snapshot); 864 857 nsim_dev_port_del_all(nsim_dev); 865 858 nsim_dev_health_exit(nsim_dev); 866 859 nsim_dev_traps_exit(devlink); ··· 934 925 int nsim_dev_init(void) 935 926 { 936 927 nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); 937 - if (IS_ERR_OR_NULL(nsim_dev_ddir)) 938 - return -ENOMEM; 928 + if (IS_ERR(nsim_dev_ddir)) 929 + return PTR_ERR(nsim_dev_ddir); 939 930 return 0; 940 931 } 941 932
+3 -3
drivers/net/netdevsim/health.c
··· 82 82 if (err) 83 83 return err; 84 84 85 - binary = kmalloc(binary_len, GFP_KERNEL); 85 + binary = kmalloc(binary_len, GFP_KERNEL | __GFP_NOWARN); 86 86 if (!binary) 87 87 return -ENOMEM; 88 88 get_random_bytes(binary, binary_len); ··· 285 285 } 286 286 287 287 health->ddir = debugfs_create_dir("health", nsim_dev->ddir); 288 - if (IS_ERR_OR_NULL(health->ddir)) { 289 - err = PTR_ERR_OR_ZERO(health->ddir) ?: -EINVAL; 288 + if (IS_ERR(health->ddir)) { 289 + err = PTR_ERR(health->ddir); 290 290 goto err_dummy_reporter_destroy; 291 291 } 292 292
+4
drivers/net/netdevsim/netdevsim.h
··· 160 160 struct nsim_trap_data *trap_data; 161 161 struct dentry *ddir; 162 162 struct dentry *ports_ddir; 163 + struct dentry *take_snapshot; 163 164 struct bpf_offload_dev *bpf_dev; 164 165 bool bpf_bind_accept; 165 166 u32 bpf_bind_verifier_delay; ··· 241 240 */ 242 241 unsigned int num_vfs; 243 242 struct nsim_vf_config *vfconfigs; 243 + /* Lock for devlink->reload_enabled in netdevsim module */ 244 + struct mutex nsim_bus_reload_lock; 245 + bool init; 244 246 }; 245 247 246 248 int nsim_bus_init(void);
-69
drivers/net/netdevsim/sdev.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - /* Copyright (c) 2019 Mellanox Technologies. All rights reserved */ 3 - 4 - #include <linux/debugfs.h> 5 - #include <linux/err.h> 6 - #include <linux/kernel.h> 7 - #include <linux/slab.h> 8 - 9 - #include "netdevsim.h" 10 - 11 - static struct dentry *nsim_sdev_ddir; 12 - 13 - static u32 nsim_sdev_id; 14 - 15 - struct netdevsim_shared_dev *nsim_sdev_get(struct netdevsim *joinns) 16 - { 17 - struct netdevsim_shared_dev *sdev; 18 - char sdev_ddir_name[10]; 19 - int err; 20 - 21 - if (joinns) { 22 - if (WARN_ON(!joinns->sdev)) 23 - return ERR_PTR(-EINVAL); 24 - sdev = joinns->sdev; 25 - sdev->refcnt++; 26 - return sdev; 27 - } 28 - 29 - sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 30 - if (!sdev) 31 - return ERR_PTR(-ENOMEM); 32 - sdev->refcnt = 1; 33 - sdev->switch_id = nsim_sdev_id++; 34 - 35 - sprintf(sdev_ddir_name, "%u", sdev->switch_id); 36 - sdev->ddir = debugfs_create_dir(sdev_ddir_name, nsim_sdev_ddir); 37 - if (IS_ERR_OR_NULL(sdev->ddir)) { 38 - err = PTR_ERR_OR_ZERO(sdev->ddir) ?: -EINVAL; 39 - goto err_sdev_free; 40 - } 41 - 42 - return sdev; 43 - 44 - err_sdev_free: 45 - nsim_sdev_id--; 46 - kfree(sdev); 47 - return ERR_PTR(err); 48 - } 49 - 50 - void nsim_sdev_put(struct netdevsim_shared_dev *sdev) 51 - { 52 - if (--sdev->refcnt) 53 - return; 54 - debugfs_remove_recursive(sdev->ddir); 55 - kfree(sdev); 56 - } 57 - 58 - int nsim_sdev_init(void) 59 - { 60 - nsim_sdev_ddir = debugfs_create_dir(DRV_NAME "_sdev", NULL); 61 - if (IS_ERR_OR_NULL(nsim_sdev_ddir)) 62 - return -ENOMEM; 63 - return 0; 64 - } 65 - 66 - void nsim_sdev_exit(void) 67 - { 68 - debugfs_remove_recursive(nsim_sdev_ddir); 69 - }