Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision 654b7dbfd1ebbe9e1b89c799d0698fa6cc06de69)
+++ kernel/generic/include/ipc/ipc.h	(revision a55d5f9fbe9f149843781206c69705643f929991)
@@ -280,5 +280,5 @@
 	ipc_data_t data;
 
-	/** Buffer for IPC_M_DATA_SEND. */
+	/** Buffer for IPC_M_DATA_WRITE and IPC_M_DATA_READ. */
 	uint8_t *buffer;
 } call_t;
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 654b7dbfd1ebbe9e1b89c799d0698fa6cc06de69)
+++ kernel/generic/src/ipc/ipc.c	(revision a55d5f9fbe9f149843781206c69705643f929991)
@@ -415,4 +415,6 @@
 	while (!list_empty(lst)) {
 		call = list_get_instance(lst->next, call_t, link);
+		if (call->buffer)
+			free(call->buffer);
 		list_remove(&call->link);
 
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 654b7dbfd1ebbe9e1b89c799d0698fa6cc06de69)
+++ kernel/generic/src/ipc/sysipc.c	(revision a55d5f9fbe9f149843781206c69705643f929991)
@@ -236,17 +236,52 @@
 			IPC_SET_RETVAL(answer->data, rc);
 		}
+	} else if (IPC_GET_METHOD(*olddata) == IPC_M_DATA_READ) {
+		ASSERT(!answer->buffer);
+		if (!IPC_GET_RETVAL(answer->data)) {
+			/* The recipient agreed to send data. */
+			uintptr_t src = IPC_GET_ARG1(answer->data);
+			uintptr_t dst = IPC_GET_ARG1(*olddata);
+			size_t max_size = IPC_GET_ARG2(*olddata);
+			size_t size = IPC_GET_ARG2(answer->data);
+			if (size <= max_size) {
+				/*
+				 * Copy the destination VA so that this piece of
+				 * information is not lost.
+				 */
+				IPC_SET_ARG1(answer->data, dst);
+
+				answer->buffer = malloc(size, 0);
+				int rc = copy_from_uspace(answer->buffer,
+				    (void *) src, size);
+				if (rc) {
+					IPC_SET_RETVAL(answer->data, rc);
+					free(answer->buffer);
+					answer->buffer = NULL;
+				}
+			} else {
+				IPC_SET_RETVAL(answer->data, ELIMIT);
+			}
+		}
 	} else if (IPC_GET_METHOD(*olddata) == IPC_M_DATA_WRITE) {
 		ASSERT(answer->buffer);
 		if (!IPC_GET_RETVAL(answer->data)) {
+			/* The recipient agreed to receive data. */
 			int rc;
 			uintptr_t dst;
 			uintptr_t size;
+			uintptr_t max_size;
 
 			dst = IPC_GET_ARG1(answer->data);
 			size = IPC_GET_ARG3(answer->data);
-
-			rc = copy_to_uspace((void *) dst, answer->buffer, size);
-			if (rc != 0)
-				IPC_SET_RETVAL(answer->data, rc);
+			max_size = IPC_GET_ARG3(*olddata);
+
+			if (size <= max_size) {
+				rc = copy_to_uspace((void *) dst,
+				    answer->buffer, size);
+				if (rc)
+					IPC_SET_RETVAL(answer->data, rc);
+			} else {
+				IPC_SET_RETVAL(answer->data, ELIMIT);
+			}
 		}
 		free(answer->buffer);
@@ -285,4 +320,9 @@
 		IPC_SET_ARG2(call->data, size);
 		break;
+	case IPC_M_DATA_READ:
+		size = IPC_GET_ARG2(call->data);
+		if ((size <= 0 || (size > DATA_XFER_LIMIT)))
+			return ELIMIT;
+		break;
 	case IPC_M_DATA_WRITE:
 		src = IPC_GET_ARG2(call->data);
@@ -325,4 +365,15 @@
 			IPC_SET_ARG5(call->data, call->priv);
 	}
+
+	if (call->buffer) {
+		/* This must be an affirmative answer to IPC_M_DATA_READ. */
+		uintptr_t dst = IPC_GET_ARG1(call->data);
+		size_t size = IPC_GET_ARG2(call->data);
+		int rc = copy_to_uspace((void *) dst, call->buffer, size);
+		if (rc)
+			IPC_SET_RETVAL(call->data, rc);
+		free(call->buffer);
+		call->buffer = NULL;
+	}
 }
 
Index: uspace/lib/libc/generic/ipc.c
===================================================================
--- uspace/lib/libc/generic/ipc.c	(revision 654b7dbfd1ebbe9e1b89c799d0698fa6cc06de69)
+++ uspace/lib/libc/generic/ipc.c	(revision a55d5f9fbe9f149843781206c69705643f929991)
@@ -667,4 +667,62 @@
 }
 
+/** Wrapper for making IPC_M_DATA_READ calls.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param dst		Address of the beginning of the destination buffer.
+ * @param size		Size of the destination buffer.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int ipc_data_read_send(int phoneid, void *dst, size_t size)
+{
+	return ipc_call_sync_2_0(phoneid, IPC_M_DATA_READ, (ipcarg_t) dst,
+	    (ipcarg_t) size);
+}
+
+/** Wrapper for receiving the IPC_M_DATA_READ calls.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid	Storage where the hash of the IPC_M_DATA_READ call will
+ * 			be stored.
+ * @param size		Storage where the maximum size will be stored.
+ *
+ * @return		Non-zero on success, zero on failure.
+ */
+int ipc_data_read_receive(ipc_callid_t *callid, size_t *size)
+{
+	ipc_call_t data;
+	
+	assert(callid);
+
+	*callid = async_get_call(&data);
+	if (IPC_GET_METHOD(data) != IPC_M_DATA_READ)
+		return 0;
+	assert(size);
+	*size = (size_t) IPC_GET_ARG2(data);
+	return 1;
+}
+
+/** Wrapper for answering the IPC_M_DATA_READ calls.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_READ call to answer.
+ * @param src		Source address for the IPC_M_DATA_READ call.
+ * @param size		Size for the IPC_M_DATA_READ call. Can be smaller than
+ *			the maximum size announced by the sender.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int ipc_data_read_deliver(ipc_callid_t callid, void *src, size_t size)
+{
+	return ipc_answer_2(callid, EOK, (ipcarg_t) src, (ipcarg_t) size);
+}
+
 /** Wrapper for making IPC_M_DATA_WRITE calls.
  *
@@ -724,5 +782,5 @@
  * @return		Zero on success or a value from @ref errno.h on failure.
  */
-ipcarg_t ipc_data_write_deliver(ipc_callid_t callid, void *dst, size_t size)
+int ipc_data_write_deliver(ipc_callid_t callid, void *dst, size_t size)
 {
 	return ipc_answer_3(callid, EOK, (ipcarg_t) dst, 0, (ipcarg_t) size);
Index: uspace/lib/libc/include/ipc/ipc.h
===================================================================
--- uspace/lib/libc/include/ipc/ipc.h	(revision 654b7dbfd1ebbe9e1b89c799d0698fa6cc06de69)
+++ uspace/lib/libc/include/ipc/ipc.h	(revision a55d5f9fbe9f149843781206c69705643f929991)
@@ -261,9 +261,13 @@
 extern int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method,
     ipcarg_t arg1, ipcarg_t arg2, int mode);
+
+
+extern int ipc_data_read_send(int phoneid, void *dst, size_t size);
+extern int ipc_data_read_receive(ipc_callid_t *callid, size_t *size);
+extern int ipc_data_read_deliver(ipc_callid_t callid, void *src, size_t size);
 extern int ipc_data_write_send(int phoneid, void *src, size_t size);
 extern int ipc_data_write_receive(ipc_callid_t *callid, void **dst,
     size_t *size);
-extern ipcarg_t ipc_data_write_deliver(ipc_callid_t callid, void *dst,
-    size_t size);
+extern int ipc_data_write_deliver(ipc_callid_t callid, void *dst, size_t size);
 
 #endif
