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

Commit e344458f authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'raw_probe_proto_opt'



Herbert Xu says:

====================
ipv4: Simplify raw_probe_proto_opt and avoid reading user iov twice

This series rewrites the function raw_probe_proto_opt in a more
readable fasion, and then fixes the long-standing bug where we
read the probed bytes twice which means that what we're using to
probe may in fact be invalid.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1ef8019b c008ba5b
Loading
Loading
Loading
Loading
+61 −43
Original line number Diff line number Diff line
@@ -79,6 +79,16 @@
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/compat.h>
#include <linux/uio.h>

struct raw_frag_vec {
	struct iovec *iov;
	union {
		struct icmphdr icmph;
		char c[1];
	} hdr;
	int hlen;
};

static struct raw_hashinfo raw_v4_hashinfo = {
	.lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock),
@@ -420,53 +430,57 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
	return err;
}

static int raw_probe_proto_opt(struct flowi4 *fl4, struct msghdr *msg)
static int raw_probe_proto_opt(struct raw_frag_vec *rfv, struct flowi4 *fl4)
{
	struct iovec *iov;
	u8 __user *type = NULL;
	u8 __user *code = NULL;
	int probed = 0;
	unsigned int i;
	int err;

	if (!msg->msg_iov)
	if (fl4->flowi4_proto != IPPROTO_ICMP)
		return 0;

	for (i = 0; i < msg->msg_iovlen; i++) {
		iov = &msg->msg_iov[i];
		if (!iov)
			continue;
	/* We only need the first two bytes. */
	rfv->hlen = 2;

		switch (fl4->flowi4_proto) {
		case IPPROTO_ICMP:
			/* check if one-byte field is readable or not. */
			if (iov->iov_base && iov->iov_len < 1)
				break;
	err = memcpy_fromiovec(rfv->hdr.c, rfv->iov, rfv->hlen);
	if (err)
		return err;

			if (!type) {
				type = iov->iov_base;
				/* check if code field is readable or not. */
				if (iov->iov_len > 1)
					code = type + 1;
			} else if (!code)
				code = iov->iov_base;

			if (type && code) {
				if (get_user(fl4->fl4_icmp_type, type) ||
				    get_user(fl4->fl4_icmp_code, code))
					return -EFAULT;
				probed = 1;
			}
			break;
		default:
			probed = 1;
			break;
		}
		if (probed)
			break;
	fl4->fl4_icmp_type = rfv->hdr.icmph.type;
	fl4->fl4_icmp_code = rfv->hdr.icmph.code;

	return 0;
}

static int raw_getfrag(void *from, char *to, int offset, int len, int odd,
		       struct sk_buff *skb)
{
	struct raw_frag_vec *rfv = from;

	if (offset < rfv->hlen) {
		int copy = min(rfv->hlen - offset, len);

		if (skb->ip_summed == CHECKSUM_PARTIAL)
			memcpy(to, rfv->hdr.c + offset, copy);
		else
			skb->csum = csum_block_add(
				skb->csum,
				csum_partial_copy_nocheck(rfv->hdr.c + offset,
							  to, copy, 0),
				odd);

		odd = 0;
		offset += copy;
		to += copy;
		len -= copy;

		if (!len)
			return 0;
	}

	offset -= rfv->hlen;

	return ip_generic_getfrag(rfv->iov, to, offset, len, odd, skb);
}

static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
		       size_t len)
{
@@ -480,6 +494,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
	u8  tos;
	int err;
	struct ip_options_data opt_copy;
	struct raw_frag_vec rfv;

	err = -EMSGSIZE;
	if (len > 0xFFFF)
@@ -585,7 +600,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
			   daddr, saddr, 0, 0);

	if (!inet->hdrincl) {
		err = raw_probe_proto_opt(&fl4, msg);
		rfv.iov = msg->msg_iov;
		rfv.hlen = 0;

		err = raw_probe_proto_opt(&rfv, &fl4);
		if (err)
			goto done;
	}
@@ -616,8 +634,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
		if (!ipc.addr)
			ipc.addr = fl4.daddr;
		lock_sock(sk);
		err = ip_append_data(sk, &fl4, ip_generic_getfrag,
				     msg->msg_iov, len, 0,
		err = ip_append_data(sk, &fl4, raw_getfrag,
				     &rfv, len, 0,
				     &ipc, &rt, msg->msg_flags);
		if (err)
			ip_flush_pending_frames(sk);