[VZ8] venetdev: check ve_ns is not null before dereferencing

Submitted by Pavel Tikhomirov on Nov. 27, 2020, 2:46 p.m.

Details

Message ID 20201127144631.1256613-1-ptikhomirov@virtuozzo.com
State New
Series "venetdev: check ve_ns is not null before dereferencing"
Headers show

Commit Message

Pavel Tikhomirov Nov. 27, 2020, 2:46 p.m.
When testing criu on vz8 I got crash in venet_newlink on dereferencing
zero ve_ns:

  BUG: unable to handle kernel NULL pointer dereference at 0000000000000028
  PGD 8000000136ceb067 P4D 8000000136ceb067 PUD 137624067 PMD 0
  Oops: 0000 [#1] SMP PTI
  CPU: 0 PID: 6853 Comm: criu ve: ad7d77df-8614-42b4-8bf4-c9313fcb2a17
  Kdump: loaded Not tainted 4.18.0-193.6.3.vz8.4.18 #1 4.18
  Hardware name: Virtuozzo KVM, BIOS 1.11.0-2.vz7.2 04/01/2014
  RIP: 0010:venet_newlink+0x18/0xc0 [vznetdev]

Small reproducer:

  # term 1
  echo $$
  # 407283

  # term 2
  mkdir /sys/fs/cgroup/ve/my_new_ve
  echo 666 > /sys/fs/cgroup/ve/my_new_ve/ve.veid
  echo 407283 > /sys/fs/cgroup/ve/my_new_ve/tasks

  # term 1
  unshare -n
  ip link add venet0 type venet

If we create venet in network namespace which is owned by ve which is
not started yet - we crash.

Note on vz7 we are safe as there is no ve_ns.

https://jira.sw.ru/browse/PSBM-123077

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
 drivers/net/venetdev.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

Patch hide | download patch | download mbox

diff --git a/drivers/net/venetdev.c b/drivers/net/venetdev.c
index b5b3f7e16c58d..cdf56b9e7ec13 100644
--- a/drivers/net/venetdev.c
+++ b/drivers/net/venetdev.c
@@ -7,6 +7,7 @@ 
 
 #include <linux/proc_fs.h>
 #include <linux/inet.h>
+#include <linux/nsproxy.h>
 #include <net/ip.h>
 
 #include <linux/venet.h>
@@ -733,6 +734,7 @@  static int venet_newlink(struct net *src_net,
 			 struct netlink_ext_ack *extack)
 {
 	struct ve_struct *ve = src_net->owner_ve;
+	struct nsproxy *ve_ns;
 	struct net *net;
 	int err = 0;
 
@@ -741,7 +743,10 @@  static int venet_newlink(struct net *src_net,
 	 * also referenced on assignment => ve won't die =>
 	 * rcu_read_lock()/unlock not needed here.
 	 */
-	net = rcu_dereference_check(ve->ve_ns, 1)->net_ns;
+	ve_ns = rcu_dereference_check(ve->ve_ns, 1);
+	if (!ve_ns)
+		return -EBUSY;
+	net = ve_ns->net_ns;
 	if (!net)
 		return -EBUSY;