[CRIU] [RFC PATCH] ipc: use kernel buffer for peeking messages

Stanislav Kinsbursky skinsbursky at parallels.com
Tue Apr 17 09:51:03 EDT 2012


Copying data to user-space (which may sleep) is not allowed since queue is
traversed under spin_lock(). This patch introduces temporary kernel buffer for
collecting the whole queue, which then then copied to user space. Also,
introduced peek_msg() helper, which is almost the same as store_msg() except it
works with kernel space addresses.

---
 ipc/compat.c  |    9 +++------
 ipc/msg.c     |   29 ++++++++++++++++++-----------
 ipc/msgutil.c |   24 ++++++++++++++++++++++++
 ipc/util.h    |    1 +
 4 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/ipc/compat.c b/ipc/compat.c
index bf31af7..0ae6ba7 100644
--- a/ipc/compat.c
+++ b/ipc/compat.c
@@ -346,12 +346,9 @@ static long compat_do_msg_peek_all(void __user *dest, struct msg_msg *msg, size_
 	if (bufsz < msgsz)
 		return -E2BIG;
 
-	if (put_user(msg->m_type, &msgp->mtype))
-		return -EFAULT;
-	if (put_user(msg->m_ts, &msgp->msize))
-		return -EFAULT;
-	if (store_msg(msgp->mtext, msg, msg->m_ts))
-		return -EFAULT;
+	msgp->mtype = msg->m_type;
+	msgp->msize = msg->m_ts;
+	peek_msg(msgp->mtext, msg, msg->m_ts);
 	return msgsz;
 }
 #else
diff --git a/ipc/msg.c b/ipc/msg.c
index e7d07c9..107e386 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -763,9 +763,9 @@ static inline int convert_mode(long *msgtyp, int msgflg)
 }
 
 #ifdef CONFIG_CHECKPOINT_RESTORE
-static long do_msg_peek_all(void __user *dest, struct msg_msg *msg, size_t bufsz)
+static long do_msg_peek_all(void *dest, struct msg_msg *msg, size_t bufsz)
 {
-	struct msgbuf_a __user *msgp = dest;
+	struct msgbuf_a *msgp = dest;
 	size_t msgsz;
 
 	/*
@@ -781,12 +781,9 @@ static long do_msg_peek_all(void __user *dest, struct msg_msg *msg, size_t bufsz
 	if (bufsz < msgsz)
 		return -E2BIG;
 
-	if (put_user(msg->m_type, &msgp->mtype))
-		return -EFAULT;
-	if (put_user(msg->m_ts, &msgp->msize))
-		return -EFAULT;
-	if (store_msg(msgp->mtext, msg, msg->m_ts))
-		return -EFAULT;
+	msgp->mtype = msg->m_type;
+	msgp->msize = msg->m_ts;
+	peek_msg(msgp->mtext, msg, msg->m_ts);
 	return msgsz;
 }
 #else
@@ -820,6 +817,11 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
 	struct ipc_namespace *ns;
 #ifdef CONFIG_CHECKPOINT_RESTORE
 	size_t arrsz = bufsz;
+	void *kbuf, *kptr;
+
+	kbuf = kptr = kmalloc(bufsz, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
 #endif
 
 	if (msqid < 0 || (long) bufsz < 0)
@@ -862,12 +864,13 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
 				} else if (msgflg & MSG_PEEK_ALL) {
 					long ret;
 
-					ret = msg_fill(buf, msg, arrsz);
+					ret = msg_fill(kptr, msg, arrsz);
 					if (ret < 0) {
+						kfree(kbuf);
 						msg = ERR_PTR(ret);
 						goto out_unlock;
 					}
-					buf += ret;
+					kptr += ret;
 					arrsz -= ret;
 #endif
 				} else {
@@ -977,8 +980,12 @@ out_unlock:
 		return PTR_ERR(msg);
 
 #ifdef CONFIG_CHECKPOINT_RESTORE
-	if (msgflg & MSG_PEEK_ALL)
+	if (msgflg & MSG_PEEK_ALL) {
+		if (copy_to_user(buf, kbuf, bufsz - arrsz))
+			return -EFAULT;
+		kfree(kbuf);
 		return bufsz - arrsz;
+	}
 #endif
 
 	bufsz = msg_fill(buf, msg, bufsz);
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index 5652101..0b57e75 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -126,6 +126,30 @@ int store_msg(void __user *dest, struct msg_msg *msg, int len)
 	return 0;
 }
 
+void peek_msg(void *dest, struct msg_msg *msg, int len)
+{
+	int alen;
+	struct msg_msgseg *seg;
+
+	alen = len;
+	if (alen > DATALEN_MSG)
+		alen = DATALEN_MSG;
+	memcpy(dest, msg + 1, alen);
+
+	len -= alen;
+	dest = ((char __user *)dest) + alen;
+	seg = msg->next;
+	while (len > 0) {
+		alen = len;
+		if (alen > DATALEN_SEG)
+			alen = DATALEN_SEG;
+		memcpy(dest, seg + 1, alen);
+		len -= alen;
+		dest = ((char *)dest) + alen;
+		seg = seg->next;
+	}
+}
+
 void free_msg(struct msg_msg *msg)
 {
 	struct msg_msgseg *seg;
diff --git a/ipc/util.h b/ipc/util.h
index 2bc6a9a..78c888c 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -143,6 +143,7 @@ int ipc_parse_version (int *cmd);
 extern void free_msg(struct msg_msg *msg);
 extern struct msg_msg *load_msg(const void __user *src, int len);
 extern int store_msg(void __user *dest, struct msg_msg *msg, int len);
+extern void peek_msg(void *dest, struct msg_msg *msg, int len);
 
 extern void recompute_msgmni(struct ipc_namespace *);
 



More information about the CRIU mailing list