Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6dc878a8 authored by Gao feng's avatar Gao feng Committed by David S. Miller
Browse files

netlink: add reference of module in netlink_dump_start



I get a panic when I use ss -a and rmmod inet_diag at the
same time.

It's because netlink_dump uses inet_diag_dump which belongs to module
inet_diag.

I search the codes and find many modules have the same problem.  We
need to add a reference to the module which the cb->dump belongs to.

Thanks for all help from Stephen,Jan,Eric,Steffen and Pablo.

Change From v3:
change netlink_dump_start to inline,suggestion from Pablo and
Eric.

Change From v2:
delete netlink_dump_done,and call module_put in netlink_dump
and netlink_sock_destruct.

Signed-off-by: default avatarGao feng <gaofeng@cn.fujitsu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ed5062dd
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -245,6 +245,8 @@ struct netlink_callback {
					struct netlink_callback *cb);
	int			(*done)(struct netlink_callback *cb);
	void			*data;
	/* the module that dump function belong to */
	struct module		*module;
	u16			family;
	u16			min_dump_alloc;
	unsigned int		prev_seq, seq;
@@ -264,12 +266,22 @@ struct netlink_dump_control {
	int (*dump)(struct sk_buff *skb, struct netlink_callback *);
	int (*done)(struct netlink_callback *);
	void *data;
	struct module *module;
	u16 min_dump_alloc;
};

extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
extern int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
				const struct nlmsghdr *nlh,
				struct netlink_dump_control *control);
static inline int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
				     const struct nlmsghdr *nlh,
				     struct netlink_dump_control *control)
{
	if (!control->module)
		control->module = THIS_MODULE;

	return __netlink_dump_start(ssk, skb, nlh, control);
}

#endif /* __KERNEL__ */

+21 −8
Original line number Diff line number Diff line
@@ -169,6 +169,8 @@ static void netlink_sock_destruct(struct sock *sk)
	if (nlk->cb) {
		if (nlk->cb->done)
			nlk->cb->done(nlk->cb);

		module_put(nlk->cb->module);
		netlink_destroy_callback(nlk->cb);
	}

@@ -1758,6 +1760,7 @@ static int netlink_dump(struct sock *sk)
	nlk->cb = NULL;
	mutex_unlock(nlk->cb_mutex);

	module_put(cb->module);
	netlink_consume_callback(cb);
	return 0;

@@ -1767,7 +1770,7 @@ errout_skb:
	return err;
}

int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
			 const struct nlmsghdr *nlh,
			 struct netlink_dump_control *control)
{
@@ -1784,6 +1787,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
	cb->done = control->done;
	cb->nlh = nlh;
	cb->data = control->data;
	cb->module = control->module;
	cb->min_dump_alloc = control->min_dump_alloc;
	atomic_inc(&skb->users);
	cb->skb = skb;
@@ -1794,19 +1798,28 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
		return -ECONNREFUSED;
	}
	nlk = nlk_sk(sk);
	/* A dump is in progress... */

	mutex_lock(nlk->cb_mutex);
	/* A dump is in progress... */
	if (nlk->cb) {
		mutex_unlock(nlk->cb_mutex);
		netlink_destroy_callback(cb);
		sock_put(sk);
		return -EBUSY;
		ret = -EBUSY;
		goto out;
	}
	/* add reference of module which cb->dump belongs to */
	if (!try_module_get(cb->module)) {
		mutex_unlock(nlk->cb_mutex);
		netlink_destroy_callback(cb);
		ret = -EPROTONOSUPPORT;
		goto out;
	}

	nlk->cb = cb;
	mutex_unlock(nlk->cb_mutex);

	ret = netlink_dump(sk);

out:
	sock_put(sk);

	if (ret)
@@ -1817,7 +1830,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
	 */
	return -EINTR;
}
EXPORT_SYMBOL(netlink_dump_start);
EXPORT_SYMBOL(__netlink_dump_start);

void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
{