Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f9c41a62 authored by Ursula Braun's avatar Ursula Braun Committed by David S. Miller
Browse files

af_iucv: fix recvmsg by replacing skb_pull() function



When receiving data messages, the "BUG_ON(skb->len < skb->data_len)" in
the skb_pull() function triggers a kernel panic.

Replace the skb_pull logic by a per skb offset as advised by
Eric Dumazet.

Signed-off-by: default avatarUrsula Braun <ursula.braun@de.ibm.com>
Signed-off-by: default avatarFrank Blaschka <blaschka@linux.vnet.ibm.com>
Reviewed-by: default avatarHendrik Brueckner <brueckner@linux.vnet.ibm.com>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 88c5b5ce
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -130,6 +130,14 @@ struct iucv_sock {
					       enum iucv_tx_notify n);
					       enum iucv_tx_notify n);
};
};


struct iucv_skb_cb {
	u32	class;		/* target class of message */
	u32	tag;		/* tag associated with message */
	u32	offset;		/* offset for skb receival */
};

#define IUCV_SKB_CB(__skb)	((struct iucv_skb_cb *)&((__skb)->cb[0]))

/* iucv socket options (SOL_IUCV) */
/* iucv socket options (SOL_IUCV) */
#define SO_IPRMDATA_MSG	0x0080		/* send/recv IPRM_DATA msgs */
#define SO_IPRMDATA_MSG	0x0080		/* send/recv IPRM_DATA msgs */
#define SO_MSGLIMIT	0x1000		/* get/set IUCV MSGLIMIT */
#define SO_MSGLIMIT	0x1000		/* get/set IUCV MSGLIMIT */
+16 −18
Original line number Original line Diff line number Diff line
@@ -49,12 +49,6 @@ static const u8 iprm_shutdown[8] =


#define TRGCLS_SIZE	(sizeof(((struct iucv_message *)0)->class))
#define TRGCLS_SIZE	(sizeof(((struct iucv_message *)0)->class))


/* macros to set/get socket control buffer at correct offset */
#define CB_TAG(skb)	((skb)->cb)		/* iucv message tag */
#define CB_TAG_LEN	(sizeof(((struct iucv_message *) 0)->tag))
#define CB_TRGCLS(skb)	((skb)->cb + CB_TAG_LEN) /* iucv msg target class */
#define CB_TRGCLS_LEN	(TRGCLS_SIZE)

#define __iucv_sock_wait(sk, condition, timeo, ret)			\
#define __iucv_sock_wait(sk, condition, timeo, ret)			\
do {									\
do {									\
	DEFINE_WAIT(__wait);						\
	DEFINE_WAIT(__wait);						\
@@ -1141,7 +1135,7 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,


	/* increment and save iucv message tag for msg_completion cbk */
	/* increment and save iucv message tag for msg_completion cbk */
	txmsg.tag = iucv->send_tag++;
	txmsg.tag = iucv->send_tag++;
	memcpy(CB_TAG(skb), &txmsg.tag, CB_TAG_LEN);
	IUCV_SKB_CB(skb)->tag = txmsg.tag;


	if (iucv->transport == AF_IUCV_TRANS_HIPER) {
	if (iucv->transport == AF_IUCV_TRANS_HIPER) {
		atomic_inc(&iucv->msg_sent);
		atomic_inc(&iucv->msg_sent);
@@ -1224,7 +1218,7 @@ static int iucv_fragment_skb(struct sock *sk, struct sk_buff *skb, int len)
			return -ENOMEM;
			return -ENOMEM;


		/* copy target class to control buffer of new skb */
		/* copy target class to control buffer of new skb */
		memcpy(CB_TRGCLS(nskb), CB_TRGCLS(skb), CB_TRGCLS_LEN);
		IUCV_SKB_CB(nskb)->class = IUCV_SKB_CB(skb)->class;


		/* copy data fragment */
		/* copy data fragment */
		memcpy(nskb->data, skb->data + copied, size);
		memcpy(nskb->data, skb->data + copied, size);
@@ -1256,7 +1250,7 @@ static void iucv_process_message(struct sock *sk, struct sk_buff *skb,


	/* store msg target class in the second 4 bytes of skb ctrl buffer */
	/* store msg target class in the second 4 bytes of skb ctrl buffer */
	/* Note: the first 4 bytes are reserved for msg tag */
	/* Note: the first 4 bytes are reserved for msg tag */
	memcpy(CB_TRGCLS(skb), &msg->class, CB_TRGCLS_LEN);
	IUCV_SKB_CB(skb)->class = msg->class;


	/* check for special IPRM messages (e.g. iucv_sock_shutdown) */
	/* check for special IPRM messages (e.g. iucv_sock_shutdown) */
	if ((msg->flags & IUCV_IPRMDATA) && len > 7) {
	if ((msg->flags & IUCV_IPRMDATA) && len > 7) {
@@ -1292,6 +1286,7 @@ static void iucv_process_message(struct sock *sk, struct sk_buff *skb,
		}
		}
	}
	}


	IUCV_SKB_CB(skb)->offset = 0;
	if (sock_queue_rcv_skb(sk, skb))
	if (sock_queue_rcv_skb(sk, skb))
		skb_queue_head(&iucv_sk(sk)->backlog_skb_q, skb);
		skb_queue_head(&iucv_sk(sk)->backlog_skb_q, skb);
}
}
@@ -1327,6 +1322,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
	unsigned int copied, rlen;
	unsigned int copied, rlen;
	struct sk_buff *skb, *rskb, *cskb;
	struct sk_buff *skb, *rskb, *cskb;
	int err = 0;
	int err = 0;
	u32 offset;


	msg->msg_namelen = 0;
	msg->msg_namelen = 0;


@@ -1348,13 +1344,14 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
		return err;
		return err;
	}
	}


	rlen   = skb->len;		/* real length of skb */
	offset = IUCV_SKB_CB(skb)->offset;
	rlen   = skb->len - offset;		/* real length of skb */
	copied = min_t(unsigned int, rlen, len);
	copied = min_t(unsigned int, rlen, len);
	if (!rlen)
	if (!rlen)
		sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN;
		sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN;


	cskb = skb;
	cskb = skb;
	if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
	if (skb_copy_datagram_iovec(cskb, offset, msg->msg_iov, copied)) {
		if (!(flags & MSG_PEEK))
		if (!(flags & MSG_PEEK))
			skb_queue_head(&sk->sk_receive_queue, skb);
			skb_queue_head(&sk->sk_receive_queue, skb);
		return -EFAULT;
		return -EFAULT;
@@ -1372,7 +1369,8 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
	 * get the trgcls from the control buffer of the skb due to
	 * get the trgcls from the control buffer of the skb due to
	 * fragmentation of original iucv message. */
	 * fragmentation of original iucv message. */
	err = put_cmsg(msg, SOL_IUCV, SCM_IUCV_TRGCLS,
	err = put_cmsg(msg, SOL_IUCV, SCM_IUCV_TRGCLS,
			CB_TRGCLS_LEN, CB_TRGCLS(skb));
		       sizeof(IUCV_SKB_CB(skb)->class),
		       (void *)&IUCV_SKB_CB(skb)->class);
	if (err) {
	if (err) {
		if (!(flags & MSG_PEEK))
		if (!(flags & MSG_PEEK))
			skb_queue_head(&sk->sk_receive_queue, skb);
			skb_queue_head(&sk->sk_receive_queue, skb);
@@ -1384,9 +1382,8 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,


		/* SOCK_STREAM: re-queue skb if it contains unreceived data */
		/* SOCK_STREAM: re-queue skb if it contains unreceived data */
		if (sk->sk_type == SOCK_STREAM) {
		if (sk->sk_type == SOCK_STREAM) {
			skb_pull(skb, copied);
			if (copied < rlen) {
			if (skb->len) {
				IUCV_SKB_CB(skb)->offset = offset + copied;
				skb_queue_head(&sk->sk_receive_queue, skb);
				goto done;
				goto done;
			}
			}
		}
		}
@@ -1405,6 +1402,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
		spin_lock_bh(&iucv->message_q.lock);
		spin_lock_bh(&iucv->message_q.lock);
		rskb = skb_dequeue(&iucv->backlog_skb_q);
		rskb = skb_dequeue(&iucv->backlog_skb_q);
		while (rskb) {
		while (rskb) {
			IUCV_SKB_CB(rskb)->offset = 0;
			if (sock_queue_rcv_skb(sk, rskb)) {
			if (sock_queue_rcv_skb(sk, rskb)) {
				skb_queue_head(&iucv->backlog_skb_q,
				skb_queue_head(&iucv->backlog_skb_q,
						rskb);
						rskb);
@@ -1832,7 +1830,7 @@ static void iucv_callback_txdone(struct iucv_path *path,
		spin_lock_irqsave(&list->lock, flags);
		spin_lock_irqsave(&list->lock, flags);


		while (list_skb != (struct sk_buff *)list) {
		while (list_skb != (struct sk_buff *)list) {
			if (!memcmp(&msg->tag, CB_TAG(list_skb), CB_TAG_LEN)) {
			if (msg->tag != IUCV_SKB_CB(list_skb)->tag) {
				this = list_skb;
				this = list_skb;
				break;
				break;
			}
			}
@@ -2093,6 +2091,7 @@ static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
	skb_pull(skb, sizeof(struct af_iucv_trans_hdr));
	skb_pull(skb, sizeof(struct af_iucv_trans_hdr));
	skb_reset_transport_header(skb);
	skb_reset_transport_header(skb);
	skb_reset_network_header(skb);
	skb_reset_network_header(skb);
	IUCV_SKB_CB(skb)->offset = 0;
	spin_lock(&iucv->message_q.lock);
	spin_lock(&iucv->message_q.lock);
	if (skb_queue_empty(&iucv->backlog_skb_q)) {
	if (skb_queue_empty(&iucv->backlog_skb_q)) {
		if (sock_queue_rcv_skb(sk, skb)) {
		if (sock_queue_rcv_skb(sk, skb)) {
@@ -2197,8 +2196,7 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
		/* fall through and receive zero length data */
		/* fall through and receive zero length data */
	case 0:
	case 0:
		/* plain data frame */
		/* plain data frame */
		memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class,
		IUCV_SKB_CB(skb)->class = trans_hdr->iucv_hdr.class;
		       CB_TRGCLS_LEN);
		err = afiucv_hs_callback_rx(sk, skb);
		err = afiucv_hs_callback_rx(sk, skb);
		break;
		break;
	default:
	default: