[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