osmo_io: Document expectation that segmentation_cb() can modify msgb

This is used for parsing e.g. the ipa header and setting msg->cb.
Guard against segmentation_cb changing msg->data in
iofd_handle_segmentation().

Change-Id: Idd2115baae98a7818aabb26232d4423d2d48fb5c
diff --git a/include/osmocom/core/osmo_io.h b/include/osmocom/core/osmo_io.h
index 932b909..3715f93 100644
--- a/include/osmocom/core/osmo_io.h
+++ b/include/osmocom/core/osmo_io.h
@@ -46,7 +46,11 @@
 			 *  Needs to return the size of the next message. If it returns
 			 *  -EAGAIN or a value larger than msgb_length() (message is incomplete)
 			 *  osmo_io will wait for more data to be read. Other negative values
-			 *  cause the msg to be discarded. */
+			 *  cause the msg to be discarded.
+			 *  If a full message was received (segmentation_cb() returns a value <= msgb_length())
+			 *  the msgb will be trimmed to size by osmo_io and forwarded to the read call-back. Any
+			 *  parsing done to the msgb by segmentation_cb() will be preserved for the read_cb()
+			 *  (e.g. setting lxh or msgb->cb). */
 			int (*segmentation_cb)(struct msgb *msg);
 		};
 
diff --git a/src/core/osmo_io.c b/src/core/osmo_io.c
index fdb9e32..8217316 100644
--- a/src/core/osmo_io.c
+++ b/src/core/osmo_io.c
@@ -226,6 +226,9 @@
 	int extra_len, received_len;
 	struct msgb *msg_pending;
 
+	/* Save the start of message before segmentation_cb (which could change it) */
+	uint8_t *data = msg->data;
+
 	received_len = msgb_length(msg);
 
 	if (!iofd->io_ops.segmentation_cb) {
@@ -258,12 +261,14 @@
 	/* msgb contains more than one segment */
 	/* Copy the trailing data over */
 	msg_pending = iofd_msgb_alloc(iofd);
-	memcpy(msgb_data(msg_pending), msgb_data(msg) + expected_len, extra_len);
+	memcpy(msgb_data(msg_pending), data + expected_len, extra_len);
 	msgb_put(msg_pending, extra_len);
 	*pending_out = msg_pending;
 
-	/* Trim the original msgb to size */
-	msgb_trim(msg, expected_len);
+	/* Trim the original msgb to size. Don't use msgb_trim because we need to reference
+	 * msg->data from before it might have been modified by the segmentation_cb(). */
+	msg->len = expected_len;
+	msg->tail = data + expected_len;
 	return IOFD_SEG_ACT_HANDLE_MORE;
 
 defer: