mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Nullify the resource task struct pointer to ensure that subsequent calls
won't try to release task_struct again.
------------[ cut here ]------------
ODEBUG: free active (active state 1) object type: rcu_head hint:
(null)
WARNING: CPU: 0 PID: 6048 at lib/debugobjects.c:329
debug_print_object+0x16a/0x210 lib/debugobjects.c:326
Kernel panic - not syncing: panic_on_warn set ...
CPU: 0 PID: 6048 Comm: syz-executor022 Not tainted
4.19.0-rc7-next-20181008+ #89
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
Google 01/01/2011
Call Trace:
  __dump_stack lib/dump_stack.c:77 [inline]
  dump_stack+0x244/0x3ab lib/dump_stack.c:113
  panic+0x238/0x4e7 kernel/panic.c:184
  __warn.cold.8+0x163/0x1ba kernel/panic.c:536
  report_bug+0x254/0x2d0 lib/bug.c:186
  fixup_bug arch/x86/kernel/traps.c:178 [inline]
  do_error_trap+0x11b/0x200 arch/x86/kernel/traps.c:271
  do_invalid_op+0x36/0x40 arch/x86/kernel/traps.c:290
  invalid_op+0x14/0x20 arch/x86/entry/entry_64.S:969
RIP: 0010:debug_print_object+0x16a/0x210 lib/debugobjects.c:326
Code: 41 88 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 92 00 00 00 48 8b 14
dd
60 02 41 88 4c 89 fe 48 c7 c7 00 f8 40 88 e8 36 2f b4 fd <0f> 0b 83 05
a9
f4 5e 06 01 48 83 c4 18 5b 41 5c 41 5d 41 5e 41 5f
RSP: 0018:ffff8801d8c3eda8 EFLAGS: 00010086
RAX: 0000000000000000 RBX: 0000000000000003 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffffff8164d235 RDI: 0000000000000005
RBP: ffff8801d8c3ede8 R08: ffff8801d70aa280 R09: ffffed003b5c3eda
R10: ffffed003b5c3eda R11: ffff8801dae1f6d7 R12: 0000000000000001
R13: ffffffff8939a760 R14: 0000000000000000 R15: ffffffff8840fca0
  __debug_check_no_obj_freed lib/debugobjects.c:786 [inline]
  debug_check_no_obj_freed+0x3ae/0x58d lib/debugobjects.c:818
  kmem_cache_free+0x202/0x290 mm/slab.c:3759
  free_task_struct kernel/fork.c:163 [inline]
  free_task+0x16e/0x1f0 kernel/fork.c:457
  __put_task_struct+0x2e6/0x620 kernel/fork.c:730
  put_task_struct include/linux/sched/task.h:96 [inline]
  finish_task_switch+0x66c/0x900 kernel/sched/core.c:2715
  context_switch kernel/sched/core.c:2834 [inline]
  __schedule+0x8d7/0x21d0 kernel/sched/core.c:3480
  schedule+0xfe/0x460 kernel/sched/core.c:3524
  freezable_schedule include/linux/freezer.h:172 [inline]
  futex_wait_queue_me+0x3f9/0x840 kernel/futex.c:2530
  futex_wait+0x45c/0xa50 kernel/futex.c:2645
  do_futex+0x31a/0x26d0 kernel/futex.c:3528
  __do_sys_futex kernel/futex.c:3589 [inline]
  __se_sys_futex kernel/futex.c:3557 [inline]
  __x64_sys_futex+0x472/0x6a0 kernel/futex.c:3557
  do_syscall_64+0x1b9/0x820 arch/x86/entry/common.c:290
  entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x446549
Code: e8 2c b3 02 00 48 83 c4 18 c3 0f 1f 80 00 00 00 00 48 89 f8 48 89 f7
48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff
ff 0f 83 2b 09 fc ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f3a998f5da8 EFLAGS: 00000246 ORIG_RAX: 00000000000000ca
RAX: ffffffffffffffda RBX: 00000000006dbc38 RCX: 0000000000446549
RDX: 0000000000000000 RSI: 0000000000000080 RDI: 00000000006dbc38
RBP: 00000000006dbc30 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000006dbc3c
R13: 2f646e6162696e69 R14: 666e692f7665642f R15: 00000000006dbd2c
Kernel Offset: disabled
Reported-by: syzbot+71aff6ea121ffefc280f@syzkaller.appspotmail.com
Fixes: ed7a01fd3f ("RDMA/restrack: Release task struct which was hold by CM_ID object")
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
		
	
			
		
			
				
	
	
		
			247 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
 | 
						|
/*
 | 
						|
 * Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved.
 | 
						|
 */
 | 
						|
 | 
						|
#include <rdma/rdma_cm.h>
 | 
						|
#include <rdma/ib_verbs.h>
 | 
						|
#include <rdma/restrack.h>
 | 
						|
#include <linux/mutex.h>
 | 
						|
#include <linux/sched/task.h>
 | 
						|
#include <linux/pid_namespace.h>
 | 
						|
 | 
						|
#include "cma_priv.h"
 | 
						|
 | 
						|
static int fill_res_noop(struct sk_buff *msg,
 | 
						|
			 struct rdma_restrack_entry *entry)
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void rdma_restrack_init(struct rdma_restrack_root *res)
 | 
						|
{
 | 
						|
	init_rwsem(&res->rwsem);
 | 
						|
	res->fill_res_entry = fill_res_noop;
 | 
						|
}
 | 
						|
 | 
						|
static const char *type2str(enum rdma_restrack_type type)
 | 
						|
{
 | 
						|
	static const char * const names[RDMA_RESTRACK_MAX] = {
 | 
						|
		[RDMA_RESTRACK_PD] = "PD",
 | 
						|
		[RDMA_RESTRACK_CQ] = "CQ",
 | 
						|
		[RDMA_RESTRACK_QP] = "QP",
 | 
						|
		[RDMA_RESTRACK_CM_ID] = "CM_ID",
 | 
						|
		[RDMA_RESTRACK_MR] = "MR",
 | 
						|
	};
 | 
						|
 | 
						|
	return names[type];
 | 
						|
};
 | 
						|
 | 
						|
void rdma_restrack_clean(struct rdma_restrack_root *res)
 | 
						|
{
 | 
						|
	struct rdma_restrack_entry *e;
 | 
						|
	char buf[TASK_COMM_LEN];
 | 
						|
	struct ib_device *dev;
 | 
						|
	const char *owner;
 | 
						|
	int bkt;
 | 
						|
 | 
						|
	if (hash_empty(res->hash))
 | 
						|
		return;
 | 
						|
 | 
						|
	dev = container_of(res, struct ib_device, res);
 | 
						|
	pr_err("restrack: %s", CUT_HERE);
 | 
						|
	dev_err(&dev->dev, "BUG: RESTRACK detected leak of resources\n");
 | 
						|
	hash_for_each(res->hash, bkt, e, node) {
 | 
						|
		if (rdma_is_kernel_res(e)) {
 | 
						|
			owner = e->kern_name;
 | 
						|
		} else {
 | 
						|
			/*
 | 
						|
			 * There is no need to call get_task_struct here,
 | 
						|
			 * because we can be here only if there are more
 | 
						|
			 * get_task_struct() call than put_task_struct().
 | 
						|
			 */
 | 
						|
			get_task_comm(buf, e->task);
 | 
						|
			owner = buf;
 | 
						|
		}
 | 
						|
 | 
						|
		pr_err("restrack: %s %s object allocated by %s is not freed\n",
 | 
						|
		       rdma_is_kernel_res(e) ? "Kernel" : "User",
 | 
						|
		       type2str(e->type), owner);
 | 
						|
	}
 | 
						|
	pr_err("restrack: %s", CUT_HERE);
 | 
						|
}
 | 
						|
 | 
						|
int rdma_restrack_count(struct rdma_restrack_root *res,
 | 
						|
			enum rdma_restrack_type type,
 | 
						|
			struct pid_namespace *ns)
 | 
						|
{
 | 
						|
	struct rdma_restrack_entry *e;
 | 
						|
	u32 cnt = 0;
 | 
						|
 | 
						|
	down_read(&res->rwsem);
 | 
						|
	hash_for_each_possible(res->hash, e, node, type) {
 | 
						|
		if (ns == &init_pid_ns ||
 | 
						|
		    (!rdma_is_kernel_res(e) &&
 | 
						|
		     ns == task_active_pid_ns(e->task)))
 | 
						|
			cnt++;
 | 
						|
	}
 | 
						|
	up_read(&res->rwsem);
 | 
						|
	return cnt;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(rdma_restrack_count);
 | 
						|
 | 
						|
static void set_kern_name(struct rdma_restrack_entry *res)
 | 
						|
{
 | 
						|
	struct ib_pd *pd;
 | 
						|
 | 
						|
	switch (res->type) {
 | 
						|
	case RDMA_RESTRACK_QP:
 | 
						|
		pd = container_of(res, struct ib_qp, res)->pd;
 | 
						|
		if (!pd) {
 | 
						|
			WARN_ONCE(true, "XRC QPs are not supported\n");
 | 
						|
			/* Survive, despite the programmer's error */
 | 
						|
			res->kern_name = " ";
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case RDMA_RESTRACK_MR:
 | 
						|
		pd = container_of(res, struct ib_mr, res)->pd;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		/* Other types set kern_name directly */
 | 
						|
		pd = NULL;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (pd)
 | 
						|
		res->kern_name = pd->res.kern_name;
 | 
						|
}
 | 
						|
 | 
						|
static struct ib_device *res_to_dev(struct rdma_restrack_entry *res)
 | 
						|
{
 | 
						|
	switch (res->type) {
 | 
						|
	case RDMA_RESTRACK_PD:
 | 
						|
		return container_of(res, struct ib_pd, res)->device;
 | 
						|
	case RDMA_RESTRACK_CQ:
 | 
						|
		return container_of(res, struct ib_cq, res)->device;
 | 
						|
	case RDMA_RESTRACK_QP:
 | 
						|
		return container_of(res, struct ib_qp, res)->device;
 | 
						|
	case RDMA_RESTRACK_CM_ID:
 | 
						|
		return container_of(res, struct rdma_id_private,
 | 
						|
				    res)->id.device;
 | 
						|
	case RDMA_RESTRACK_MR:
 | 
						|
		return container_of(res, struct ib_mr, res)->device;
 | 
						|
	default:
 | 
						|
		WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static bool res_is_user(struct rdma_restrack_entry *res)
 | 
						|
{
 | 
						|
	switch (res->type) {
 | 
						|
	case RDMA_RESTRACK_PD:
 | 
						|
		return container_of(res, struct ib_pd, res)->uobject;
 | 
						|
	case RDMA_RESTRACK_CQ:
 | 
						|
		return container_of(res, struct ib_cq, res)->uobject;
 | 
						|
	case RDMA_RESTRACK_QP:
 | 
						|
		return container_of(res, struct ib_qp, res)->uobject;
 | 
						|
	case RDMA_RESTRACK_CM_ID:
 | 
						|
		return !res->kern_name;
 | 
						|
	case RDMA_RESTRACK_MR:
 | 
						|
		return container_of(res, struct ib_mr, res)->pd->uobject;
 | 
						|
	default:
 | 
						|
		WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void rdma_restrack_set_task(struct rdma_restrack_entry *res,
 | 
						|
			    const char *caller)
 | 
						|
{
 | 
						|
	if (caller) {
 | 
						|
		res->kern_name = caller;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (res->task)
 | 
						|
		put_task_struct(res->task);
 | 
						|
	get_task_struct(current);
 | 
						|
	res->task = current;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(rdma_restrack_set_task);
 | 
						|
 | 
						|
void rdma_restrack_add(struct rdma_restrack_entry *res)
 | 
						|
{
 | 
						|
	struct ib_device *dev = res_to_dev(res);
 | 
						|
 | 
						|
	if (!dev)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (res->type != RDMA_RESTRACK_CM_ID || !res_is_user(res))
 | 
						|
		res->task = NULL;
 | 
						|
 | 
						|
	if (res_is_user(res)) {
 | 
						|
		if (!res->task)
 | 
						|
			rdma_restrack_set_task(res, NULL);
 | 
						|
		res->kern_name = NULL;
 | 
						|
	} else {
 | 
						|
		set_kern_name(res);
 | 
						|
	}
 | 
						|
 | 
						|
	kref_init(&res->kref);
 | 
						|
	init_completion(&res->comp);
 | 
						|
	res->valid = true;
 | 
						|
 | 
						|
	down_write(&dev->res.rwsem);
 | 
						|
	hash_add(dev->res.hash, &res->node, res->type);
 | 
						|
	up_write(&dev->res.rwsem);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(rdma_restrack_add);
 | 
						|
 | 
						|
int __must_check rdma_restrack_get(struct rdma_restrack_entry *res)
 | 
						|
{
 | 
						|
	return kref_get_unless_zero(&res->kref);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(rdma_restrack_get);
 | 
						|
 | 
						|
static void restrack_release(struct kref *kref)
 | 
						|
{
 | 
						|
	struct rdma_restrack_entry *res;
 | 
						|
 | 
						|
	res = container_of(kref, struct rdma_restrack_entry, kref);
 | 
						|
	complete(&res->comp);
 | 
						|
}
 | 
						|
 | 
						|
int rdma_restrack_put(struct rdma_restrack_entry *res)
 | 
						|
{
 | 
						|
	return kref_put(&res->kref, restrack_release);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(rdma_restrack_put);
 | 
						|
 | 
						|
void rdma_restrack_del(struct rdma_restrack_entry *res)
 | 
						|
{
 | 
						|
	struct ib_device *dev;
 | 
						|
 | 
						|
	if (!res->valid)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	dev = res_to_dev(res);
 | 
						|
	if (!dev)
 | 
						|
		return;
 | 
						|
 | 
						|
	rdma_restrack_put(res);
 | 
						|
 | 
						|
	wait_for_completion(&res->comp);
 | 
						|
 | 
						|
	down_write(&dev->res.rwsem);
 | 
						|
	hash_del(&res->node);
 | 
						|
	res->valid = false;
 | 
						|
	up_write(&dev->res.rwsem);
 | 
						|
 | 
						|
out:
 | 
						|
	if (res->task) {
 | 
						|
		put_task_struct(res->task);
 | 
						|
		res->task = NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(rdma_restrack_del);
 |