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

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
...@@ -79,6 +79,16 @@ ...@@ -79,6 +79,16 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/compat.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 = { static struct raw_hashinfo raw_v4_hashinfo = {
.lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock),
...@@ -420,51 +430,55 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, ...@@ -420,51 +430,55 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
return err; 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; int err;
u8 __user *type = NULL;
u8 __user *code = NULL;
int probed = 0;
unsigned int i;
if (!msg->msg_iov) if (fl4->flowi4_proto != IPPROTO_ICMP)
return 0; return 0;
for (i = 0; i < msg->msg_iovlen; i++) { /* We only need the first two bytes. */
iov = &msg->msg_iov[i]; rfv->hlen = 2;
if (!iov)
continue;
switch (fl4->flowi4_proto) { err = memcpy_fromiovec(rfv->hdr.c, rfv->iov, rfv->hlen);
case IPPROTO_ICMP: if (err)
/* check if one-byte field is readable or not. */ return err;
if (iov->iov_base && iov->iov_len < 1)
break;
if (!type) { fl4->fl4_icmp_type = rfv->hdr.icmph.type;
type = iov->iov_base; fl4->fl4_icmp_code = rfv->hdr.icmph.code;
/* check if code field is readable or not. */
if (iov->iov_len > 1) return 0;
code = type + 1; }
} else if (!code)
code = iov->iov_base; static int raw_getfrag(void *from, char *to, int offset, int len, int odd,
struct sk_buff *skb)
if (type && code) { {
if (get_user(fl4->fl4_icmp_type, type) || struct raw_frag_vec *rfv = from;
get_user(fl4->fl4_icmp_code, code))
return -EFAULT; if (offset < rfv->hlen) {
probed = 1; int copy = min(rfv->hlen - offset, len);
}
break; if (skb->ip_summed == CHECKSUM_PARTIAL)
default: memcpy(to, rfv->hdr.c + offset, copy);
probed = 1; else
break; skb->csum = csum_block_add(
} skb->csum,
if (probed) csum_partial_copy_nocheck(rfv->hdr.c + offset,
break; to, copy, 0),
} odd);
odd = 0;
offset += copy;
to += copy;
len -= copy;
if (!len)
return 0; 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, static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
...@@ -480,6 +494,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ...@@ -480,6 +494,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
u8 tos; u8 tos;
int err; int err;
struct ip_options_data opt_copy; struct ip_options_data opt_copy;
struct raw_frag_vec rfv;
err = -EMSGSIZE; err = -EMSGSIZE;
if (len > 0xFFFF) if (len > 0xFFFF)
...@@ -585,7 +600,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ...@@ -585,7 +600,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
daddr, saddr, 0, 0); daddr, saddr, 0, 0);
if (!inet->hdrincl) { 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) if (err)
goto done; goto done;
} }
...@@ -616,8 +634,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ...@@ -616,8 +634,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (!ipc.addr) if (!ipc.addr)
ipc.addr = fl4.daddr; ipc.addr = fl4.daddr;
lock_sock(sk); lock_sock(sk);
err = ip_append_data(sk, &fl4, ip_generic_getfrag, err = ip_append_data(sk, &fl4, raw_getfrag,
msg->msg_iov, len, 0, &rfv, len, 0,
&ipc, &rt, msg->msg_flags); &ipc, &rt, msg->msg_flags);
if (err) if (err)
ip_flush_pending_frames(sk); ip_flush_pending_frames(sk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment