Commit 48bdf072 authored by Chris Metcalf's avatar Chris Metcalf Committed by David S. Miller

ip_options_compile: properly handle unaligned pointer

The current code takes an unaligned pointer and does htonl() on it to
make it big-endian, then does a memcpy().  The problem is that the
compiler decides that since the pointer is to a __be32, it is legal
to optimize the copy into a processor word store.  However, on an
architecture that does not handled unaligned writes in kernel space,
this produces an unaligned exception fault.

The solution is to track the pointer as a "char *" (which removes a bunch
of unpleasant casts in any case), and then just use put_unaligned_be32()
to write the value to memory.
Signed-off-by: default avatarChris Metcalf <cmetcalf@tilera.com>
Signed-off-by: default avatarDavid S. Miller <davem@zippy.davemloft.net>
parent 11ad2f52
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/icmp.h> #include <linux/icmp.h>
...@@ -350,7 +351,7 @@ int ip_options_compile(struct net *net, ...@@ -350,7 +351,7 @@ int ip_options_compile(struct net *net,
goto error; goto error;
} }
if (optptr[2] <= optlen) { if (optptr[2] <= optlen) {
__be32 *timeptr = NULL; unsigned char *timeptr = NULL;
if (optptr[2]+3 > optptr[1]) { if (optptr[2]+3 > optptr[1]) {
pp_ptr = optptr + 2; pp_ptr = optptr + 2;
goto error; goto error;
...@@ -359,7 +360,7 @@ int ip_options_compile(struct net *net, ...@@ -359,7 +360,7 @@ int ip_options_compile(struct net *net,
case IPOPT_TS_TSONLY: case IPOPT_TS_TSONLY:
opt->ts = optptr - iph; opt->ts = optptr - iph;
if (skb) if (skb)
timeptr = (__be32*)&optptr[optptr[2]-1]; timeptr = &optptr[optptr[2]-1];
opt->ts_needtime = 1; opt->ts_needtime = 1;
optptr[2] += 4; optptr[2] += 4;
break; break;
...@@ -371,7 +372,7 @@ int ip_options_compile(struct net *net, ...@@ -371,7 +372,7 @@ int ip_options_compile(struct net *net,
opt->ts = optptr - iph; opt->ts = optptr - iph;
if (rt) { if (rt) {
memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
timeptr = (__be32*)&optptr[optptr[2]+3]; timeptr = &optptr[optptr[2]+3];
} }
opt->ts_needaddr = 1; opt->ts_needaddr = 1;
opt->ts_needtime = 1; opt->ts_needtime = 1;
...@@ -389,7 +390,7 @@ int ip_options_compile(struct net *net, ...@@ -389,7 +390,7 @@ int ip_options_compile(struct net *net,
if (inet_addr_type(net, addr) == RTN_UNICAST) if (inet_addr_type(net, addr) == RTN_UNICAST)
break; break;
if (skb) if (skb)
timeptr = (__be32*)&optptr[optptr[2]+3]; timeptr = &optptr[optptr[2]+3];
} }
opt->ts_needtime = 1; opt->ts_needtime = 1;
optptr[2] += 8; optptr[2] += 8;
...@@ -403,10 +404,10 @@ int ip_options_compile(struct net *net, ...@@ -403,10 +404,10 @@ int ip_options_compile(struct net *net,
} }
if (timeptr) { if (timeptr) {
struct timespec tv; struct timespec tv;
__be32 midtime; u32 midtime;
getnstimeofday(&tv); getnstimeofday(&tv);
midtime = htonl((tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC); midtime = (tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC;
memcpy(timeptr, &midtime, sizeof(__be32)); put_unaligned_be32(midtime, timeptr);
opt->is_changed = 1; opt->is_changed = 1;
} }
} else { } else {
......
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