Commit b52da405 authored by Corey Minyard's avatar Corey Minyard
Browse files

ipmi: Rework user message limit handling



The limit on the number of user messages had a number of issues,
improper counting in some cases and a use after free.

Restructure how this is all done to handle more in the receive message
allocation routine, so all refcouting and user message limit counts
are done in that routine.  It's a lot cleaner and safer.

Reported-by: default avatarGilles BULOZ <gilles.buloz@kontron.com>
Closes: https://lore.kernel.org/lkml/aLsw6G0GyqfpKs2S@mail.minyard.net/


Fixes: 8e76741c ("ipmi: Add a limit on the number of users that may use IPMI")
Cc: <stable@vger.kernel.org> # 4.19
Signed-off-by: default avatarCorey Minyard <corey@minyard.net>
Tested-by: default avatarGilles BULOZ <gilles.buloz@kontron.com>
parent 5d09ee1b
Loading
Loading
Loading
Loading
+200 −220
Original line number Diff line number Diff line
@@ -38,7 +38,9 @@

#define IPMI_DRIVER_VERSION "39.2"

static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user);
static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg,
				   struct ipmi_user *user);
static int ipmi_init_msghandler(void);
static void smi_work(struct work_struct *t);
static void handle_new_recv_msgs(struct ipmi_smi *intf);
@@ -955,7 +957,6 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
		 * risk.  At this moment, simply skip it in that case.
		 */
		ipmi_free_recv_msg(msg);
		atomic_dec(&msg->user->nr_msgs);
	} else {
		/*
		 * Deliver it in smi_work.  The message will hold a
@@ -1611,8 +1612,7 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val)
		}

		list_for_each_entry_safe(msg, msg2, &msgs, link) {
			msg->user = user;
			kref_get(&user->refcount);
			ipmi_set_recv_msg_user(msg, user);
			deliver_local_response(intf, msg);
		}
	}
@@ -2277,22 +2277,15 @@ static int i_ipmi_request(struct ipmi_user *user,
	int run_to_completion = READ_ONCE(intf->run_to_completion);
	int rv = 0;

	if (user) {
		if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) {
			/* Decrement will happen at the end of the routine. */
			rv = -EBUSY;
			goto out;
		}
	}

	if (supplied_recv)
	if (supplied_recv) {
		recv_msg = supplied_recv;
	else {
		recv_msg = ipmi_alloc_recv_msg();
		if (recv_msg == NULL) {
			rv = -ENOMEM;
			goto out;
		}
		recv_msg->user = user;
		if (user)
			atomic_inc(&user->nr_msgs);
	} else {
		recv_msg = ipmi_alloc_recv_msg(user);
		if (IS_ERR(recv_msg))
			return PTR_ERR(recv_msg);
	}
	recv_msg->user_msg_data = user_msg_data;

@@ -2303,8 +2296,7 @@ static int i_ipmi_request(struct ipmi_user *user,
		if (smi_msg == NULL) {
			if (!supplied_recv)
				ipmi_free_recv_msg(recv_msg);
			rv = -ENOMEM;
			goto out;
			return -ENOMEM;
		}
	}

@@ -2315,10 +2307,6 @@ static int i_ipmi_request(struct ipmi_user *user,
		goto out_err;
	}

	recv_msg->user = user;
	if (user)
		/* The put happens when the message is freed. */
		kref_get(&user->refcount);
	recv_msg->msgid = msgid;
	/*
	 * Store the message to send in the receive message so timeout
@@ -2347,7 +2335,9 @@ static int i_ipmi_request(struct ipmi_user *user,

	if (rv) {
out_err:
		if (!supplied_smi)
			ipmi_free_smi_msg(smi_msg);
		if (!supplied_recv)
			ipmi_free_recv_msg(recv_msg);
	} else {
		dev_dbg(intf->si_dev, "Send: %*ph\n",
@@ -2358,9 +2348,6 @@ static int i_ipmi_request(struct ipmi_user *user,
	if (!run_to_completion)
		mutex_unlock(&intf->users_mutex);

out:
	if (rv && user)
		atomic_dec(&user->nr_msgs);
	return rv;
}

@@ -3851,7 +3838,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
	unsigned char            chan;
	struct ipmi_user         *user = NULL;
	struct ipmi_ipmb_addr    *ipmb_addr;
	struct ipmi_recv_msg     *recv_msg;
	struct ipmi_recv_msg     *recv_msg = NULL;

	if (msg->rsp_size < 10) {
		/* Message not big enough, just ignore it. */
@@ -3872,9 +3859,8 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
	rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
	if (rcvr) {
		user = rcvr->user;
		kref_get(&user->refcount);
	} else
		user = NULL;
		recv_msg = ipmi_alloc_recv_msg(user);
	}
	rcu_read_unlock();

	if (user == NULL) {
@@ -3904,17 +3890,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
		 * causes it to not be freed or queued.
		 */
		rv = -1;
	} else {
		recv_msg = ipmi_alloc_recv_msg();
		if (!recv_msg) {
			/*
			 * We couldn't allocate memory for the
			 * message, so requeue it for handling
			 * later.
			 */
			rv = 1;
			kref_put(&user->refcount, free_ipmi_user);
		} else {
	} else if (!IS_ERR(recv_msg)) {
		/* Extract the source address from the data. */
		ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
		ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
@@ -3926,7 +3902,6 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
		 * Extract the rest of the message information
		 * from the IPMB header.
		 */
			recv_msg->user = user;
		recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
		recv_msg->msgid = msg->rsp[7] >> 2;
		recv_msg->msg.netfn = msg->rsp[4] >> 2;
@@ -3944,7 +3919,12 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
			ipmi_inc_stat(intf, unhandled_commands);
		else
			ipmi_inc_stat(intf, handled_commands);
		}
	} else {
		/*
		 * We couldn't allocate memory for the message, so
		 * requeue it for handling later.
		 */
		rv = 1;
	}

	return rv;
@@ -3957,7 +3937,7 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
	int                      rv = 0;
	struct ipmi_user         *user = NULL;
	struct ipmi_ipmb_direct_addr *daddr;
	struct ipmi_recv_msg     *recv_msg;
	struct ipmi_recv_msg     *recv_msg = NULL;
	unsigned char netfn = msg->rsp[0] >> 2;
	unsigned char cmd = msg->rsp[3];

@@ -3966,9 +3946,8 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
	rcvr = find_cmd_rcvr(intf, netfn, cmd, 0);
	if (rcvr) {
		user = rcvr->user;
		kref_get(&user->refcount);
	} else
		user = NULL;
		recv_msg = ipmi_alloc_recv_msg(user);
	}
	rcu_read_unlock();

	if (user == NULL) {
@@ -3990,17 +3969,7 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
		 * causes it to not be freed or queued.
		 */
		rv = -1;
	} else {
		recv_msg = ipmi_alloc_recv_msg();
		if (!recv_msg) {
			/*
			 * We couldn't allocate memory for the
			 * message, so requeue it for handling
			 * later.
			 */
			rv = 1;
			kref_put(&user->refcount, free_ipmi_user);
		} else {
	} else if (!IS_ERR(recv_msg)) {
		/* Extract the source address from the data. */
		daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
		daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
@@ -4013,7 +3982,6 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
		 * Extract the rest of the message information
		 * from the IPMB header.
		 */
			recv_msg->user = user;
		recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
		recv_msg->msgid = (msg->rsp[2] >> 2);
		recv_msg->msg.netfn = msg->rsp[0] >> 2;
@@ -4027,7 +3995,12 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
			ipmi_inc_stat(intf, unhandled_commands);
		else
			ipmi_inc_stat(intf, handled_commands);
		}
	} else {
		/*
		 * We couldn't allocate memory for the message, so
		 * requeue it for handling later.
		 */
		rv = 1;
	}

	return rv;
@@ -4141,7 +4114,7 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
	unsigned char            chan;
	struct ipmi_user         *user = NULL;
	struct ipmi_lan_addr     *lan_addr;
	struct ipmi_recv_msg     *recv_msg;
	struct ipmi_recv_msg     *recv_msg = NULL;

	if (msg->rsp_size < 12) {
		/* Message not big enough, just ignore it. */
@@ -4162,9 +4135,8 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
	rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
	if (rcvr) {
		user = rcvr->user;
		kref_get(&user->refcount);
	} else
		user = NULL;
		recv_msg = ipmi_alloc_recv_msg(user);
	}
	rcu_read_unlock();

	if (user == NULL) {
@@ -4195,16 +4167,7 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
		 * causes it to not be freed or queued.
		 */
		rv = -1;
	} else {
		recv_msg = ipmi_alloc_recv_msg();
		if (!recv_msg) {
			/*
			 * We couldn't allocate memory for the
			 * message, so requeue it for handling later.
			 */
			rv = 1;
			kref_put(&user->refcount, free_ipmi_user);
		} else {
	} else if (!IS_ERR(recv_msg)) {
		/* Extract the source address from the data. */
		lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
		lan_addr->addr_type = IPMI_LAN_ADDR_TYPE;
@@ -4219,7 +4182,6 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
		 * Extract the rest of the message information
		 * from the IPMB header.
		 */
			recv_msg->user = user;
		recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
		recv_msg->msgid = msg->rsp[9] >> 2;
		recv_msg->msg.netfn = msg->rsp[6] >> 2;
@@ -4237,7 +4199,12 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
			ipmi_inc_stat(intf, unhandled_commands);
		else
			ipmi_inc_stat(intf, handled_commands);
		}
	} else {
		/*
		 * We couldn't allocate memory for the message, so
		 * requeue it for handling later.
		 */
		rv = 1;
	}

	return rv;
@@ -4259,7 +4226,7 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
	unsigned char         chan;
	struct ipmi_user *user = NULL;
	struct ipmi_system_interface_addr *smi_addr;
	struct ipmi_recv_msg  *recv_msg;
	struct ipmi_recv_msg  *recv_msg = NULL;

	/*
	 * We expect the OEM SW to perform error checking
@@ -4288,9 +4255,8 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
	rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
	if (rcvr) {
		user = rcvr->user;
		kref_get(&user->refcount);
	} else
		user = NULL;
		recv_msg = ipmi_alloc_recv_msg(user);
	}
	rcu_read_unlock();

	if (user == NULL) {
@@ -4303,17 +4269,7 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
		 */

		rv = 0;
	} else {
		recv_msg = ipmi_alloc_recv_msg();
		if (!recv_msg) {
			/*
			 * We couldn't allocate memory for the
			 * message, so requeue it for handling
			 * later.
			 */
			rv = 1;
			kref_put(&user->refcount, free_ipmi_user);
		} else {
	} else if (!IS_ERR(recv_msg)) {
		/*
		 * OEM Messages are expected to be delivered via
		 * the system interface to SMS software.  We might
@@ -4326,7 +4282,6 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
		smi_addr->channel = IPMI_BMC_CHANNEL;
		smi_addr->lun = msg->rsp[0] & 3;

			recv_msg->user = user;
		recv_msg->user_msg_data = NULL;
		recv_msg->recv_type = IPMI_OEM_RECV_TYPE;
		recv_msg->msg.netfn = msg->rsp[0] >> 2;
@@ -4344,7 +4299,12 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
			ipmi_inc_stat(intf, unhandled_commands);
		else
			ipmi_inc_stat(intf, handled_commands);
		}
	} else {
		/*
		 * We couldn't allocate memory for the message, so
		 * requeue it for handling later.
		 */
		rv = 1;
	}

	return rv;
@@ -4402,8 +4362,8 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
		if (!user->gets_events)
			continue;

		recv_msg = ipmi_alloc_recv_msg();
		if (!recv_msg) {
		recv_msg = ipmi_alloc_recv_msg(user);
		if (IS_ERR(recv_msg)) {
			mutex_unlock(&intf->users_mutex);
			list_for_each_entry_safe(recv_msg, recv_msg2, &msgs,
						 link) {
@@ -4424,8 +4384,6 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
		deliver_count++;

		copy_event_into_recv_msg(recv_msg, msg);
		recv_msg->user = user;
		kref_get(&user->refcount);
		list_add_tail(&recv_msg->link, &msgs);
	}
	mutex_unlock(&intf->users_mutex);
@@ -4441,8 +4399,8 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
		 * No one to receive the message, put it in queue if there's
		 * not already too many things in the queue.
		 */
		recv_msg = ipmi_alloc_recv_msg();
		if (!recv_msg) {
		recv_msg = ipmi_alloc_recv_msg(NULL);
		if (IS_ERR(recv_msg)) {
			/*
			 * We couldn't allocate memory for the
			 * message, so requeue it for handling
@@ -4858,13 +4816,11 @@ static void smi_work(struct work_struct *t)

		list_del(&msg->link);

		if (refcount_read(&user->destroyed) == 0) {
		if (refcount_read(&user->destroyed) == 0)
			ipmi_free_recv_msg(msg);
		} else {
			atomic_dec(&user->nr_msgs);
		else
			user->handler->ipmi_recv_hndl(msg, user->handler_data);
	}
	}
	mutex_unlock(&intf->user_msgs_mutex);

	kref_put(&intf->refcount, intf_free);
@@ -5179,27 +5135,51 @@ static void free_recv_msg(struct ipmi_recv_msg *msg)
		kfree(msg);
}

static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user)
{
	struct ipmi_recv_msg *rv;

	if (user) {
		if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) {
			atomic_dec(&user->nr_msgs);
			return ERR_PTR(-EBUSY);
		}
	}

	rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC);
	if (rv) {
		rv->user = NULL;
	if (!rv) {
		if (user)
			atomic_dec(&user->nr_msgs);
		return ERR_PTR(-ENOMEM);
	}

	rv->user = user;
	rv->done = free_recv_msg;
	if (user)
		kref_get(&user->refcount);
	atomic_inc(&recv_msg_inuse_count);
	}
	return rv;
}

void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
{
	if (msg->user && !oops_in_progress)
	if (msg->user && !oops_in_progress) {
		atomic_dec(&msg->user->nr_msgs);
		kref_put(&msg->user->refcount, free_ipmi_user);
	}
	msg->done(msg);
}
EXPORT_SYMBOL(ipmi_free_recv_msg);

static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg,
				   struct ipmi_user *user)
{
	WARN_ON_ONCE(msg->user); /* User should not be set. */
	msg->user = user;
	atomic_inc(&user->nr_msgs);
	kref_get(&user->refcount);
}

static atomic_t panic_done_count = ATOMIC_INIT(0);

static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)