Skip to contentSkip to conversation

NET: Fix function put_cmsg() which may cause usr application memory overflow

PATCH In review Wei Yongjun 2 participants 2 messages Dec 18, 2007 – Dec 20, 2007

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 NET: Fix function put_cmsg() which may cause usr application memory overflow

When used function put_cmsg() to copy kernel information to user application memory, if the memory length given by user application is not enough, by the bad length calculate of msg.msg_controllen, put_cmsg() function may cause the msg.msg_controllen to be a large value, such as 0xFFFFFFF0, so the following put_cmsg() can also write data to usr application memory even usr has no valid memory to store this. This may cause usr application memory overflow.

int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) {
struct cmsghdr __user *cm

       = (__force struct cmsghdr __user *)msg->msg_control;

struct cmsghdr cmhdr;
int cmlen = CMSG_LEN(len);
~~~~~~~~~~~~~~~~~~~~~
int err;

if (MSG_CMSG_COMPAT & msg->msg_flags)

       return put_cmsg_compat(msg, level, type, len, data);
   if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
       msg->msg_flags |= MSG_CTRUNC;
       return 0; /* XXX: return error? check spec. */
   }
   if (msg->msg_controllen < cmlen) {
   ~~~~~~~~~~~~~~~~~~~~~~~~
       msg->msg_flags |= MSG_CTRUNC;
       cmlen = msg->msg_controllen;
   }
   cmhdr.cmsg_level = level;
   cmhdr.cmsg_type = type;
   cmhdr.cmsg_len = cmlen;

err = -EFAULT;
if (copy_to_user(cm, &cmhdr, sizeof cmhdr))

       goto out;
   if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
       goto out;

cmlen = CMSG_SPACE(len);
~~~~~~~~~~~~~~~~~~~~~~~~~~~
If MSG_CTRUNC flags is set, msg->msg_controllen is less than
CMSG_SPACE(len), "msg->msg_controllen -= cmlen" will cause unsinged int type msg->msg_controllen to be a large value.
~~~~~~~~~~~~~~~~~~~~~~~~~~~
msg->msg_control += cmlen;
msg->msg_controllen -= cmlen;
~~~~~~~~~~~~~~~~~~~~~
err = 0;
out:
return err;
}

The same promble exists in put_cmsg_compat(). This patch can fix this problem.

net/core/scm.c +0 -0
@@ -196,6 +196,8 @@ int put_cmsg(struct msghdr * msg, int le
	if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
		goto out;
	cmlen = CMSG_SPACE(len);
+	if (msg->msg_controllen < cmlen)
+		cmlen = msg->msg_controllen;
	msg->msg_control += cmlen;
	msg->msg_controllen -= cmlen;
	err = 0;
net/compat.c +0 -0
@@ -254,6 +254,8 @@ int put_cmsg_compat(struct msghdr *kmsg,
	if (copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr)))
		return -EFAULT;
	cmlen = CMSG_COMPAT_SPACE(len);
+	if (kmsg->msg_controllen < cmlen)
+		cmlen = kmsg->msg_controllen;
	kmsg->msg_control += cmlen;
	kmsg->msg_controllen -= cmlen;
	return 0;
maintainer
#1 lore raw

From: Wei Yongjun <[email protected]>
Date: Tue, 18 Dec 2007 15:19:09 +0900

Quoted 7 lines

When used function put_cmsg() to copy kernel information to user application memory, if the memory length given by user application is not enough, by the bad length calculate of msg.msg_controllen, put_cmsg() function may cause the msg.msg_controllen to be a large value, such as 0xFFFFFFF0, so the following put_cmsg() can also write data to usr application memory even usr has no valid memory to store this. This may cause usr application memory overflow.

...

Quoted 4 lines

The same promble exists in put_cmsg_compat(). This patch can fix this problem.

Thank you for fixing this bug, patch applied.

put_cmsg() is a confusing function, it takes a while to understand what it is trying to accomplish. And this explains why this bug lived for so long.

Each CMSG entry has to be aligned, but put_cmsg() will allow to put the full final message into the CMSG area if there is enough room to fit the message without the added alignment.

So, with Wei's fix, we now have:

	int cmlen = CMSG_LEN(len);
 ...
	if (msg->msg_controllen < cmlen) {
		msg->msg_flags |= MSG_CTRUNC;
		cmlen = msg->msg_controllen;
	}
 ...
	if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
		goto out;
	if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
		goto out;
	cmlen = CMSG_SPACE(len);
	if (msg->msg_controllen < cmlen)
		cmlen = msg->msg_controllen;
	msg->msg_control += cmlen;
	msg->msg_controllen -= cmlen;

...

which I think finally implements the intended behavior accurately.