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,53 +430,57 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, ...@@ -420,53 +430,57 @@ 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; err = memcpy_fromiovec(rfv->hdr.c, rfv->iov, rfv->hlen);
if (err)
switch (fl4->flowi4_proto) { return err;
case IPPROTO_ICMP:
/* check if one-byte field is readable or not. */ fl4->fl4_icmp_type = rfv->hdr.icmph.type;
if (iov->iov_base && iov->iov_len < 1) fl4->fl4_icmp_code = rfv->hdr.icmph.code;
break;
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;
}
return 0; 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, static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len) size_t len)
{ {
...@@ -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