diff options
author | Pavel Emelyanov <xemul@parallels.com> | 2012-04-25 19:43:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-04-26 06:13:51 -0400 |
commit | de248a75c35e0208294cf304b112916254b69184 (patch) | |
tree | 0d00a02fdb994f8b1cbf239c11080fec86977639 /net/ipv4/tcp.c | |
parent | 2d319508a3551d2995e5cd12d649821b3be00e5b (diff) |
tcp repair: Fix unaligned access when repairing options (v2)
Don't pick __u8/__u16 values directly from raw pointers, but instead use
an array of structures of code:value pairs. This is OK, since the buffer
we take options from is not an skb memory, but a user-to-kernel one.
For those options which don't require any value now, require this to be
zero (for potential future extension of this API).
v2: Changed tcp_repair_opt to use two __u32-s as spotted by David Laight.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r-- | net/ipv4/tcp.c | 60 |
1 files changed, 21 insertions, 39 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index de6a238f0e1d..9670af341931 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -2283,60 +2283,40 @@ static inline int tcp_can_repair_sock(struct sock *sk) | |||
2283 | ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); | 2283 | ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); |
2284 | } | 2284 | } |
2285 | 2285 | ||
2286 | static int tcp_repair_options_est(struct tcp_sock *tp, char __user *optbuf, unsigned int len) | 2286 | static int tcp_repair_options_est(struct tcp_sock *tp, |
2287 | struct tcp_repair_opt __user *optbuf, unsigned int len) | ||
2287 | { | 2288 | { |
2288 | /* | 2289 | struct tcp_repair_opt opt; |
2289 | * Options are stored in CODE:VALUE form where CODE is 8bit and VALUE | ||
2290 | * fits the respective TCPOLEN_ size | ||
2291 | */ | ||
2292 | 2290 | ||
2293 | while (len > 0) { | 2291 | while (len >= sizeof(opt)) { |
2294 | u8 opcode; | 2292 | if (copy_from_user(&opt, optbuf, sizeof(opt))) |
2295 | |||
2296 | if (get_user(opcode, optbuf)) | ||
2297 | return -EFAULT; | 2293 | return -EFAULT; |
2298 | 2294 | ||
2299 | optbuf++; | 2295 | optbuf++; |
2300 | len--; | 2296 | len -= sizeof(opt); |
2301 | |||
2302 | switch (opcode) { | ||
2303 | case TCPOPT_MSS: { | ||
2304 | u16 in_mss; | ||
2305 | 2297 | ||
2306 | if (len < sizeof(in_mss)) | 2298 | switch (opt.opt_code) { |
2307 | return -ENODATA; | 2299 | case TCPOPT_MSS: |
2308 | if (get_user(in_mss, optbuf)) | 2300 | tp->rx_opt.mss_clamp = opt.opt_val; |
2309 | return -EFAULT; | ||
2310 | |||
2311 | tp->rx_opt.mss_clamp = in_mss; | ||
2312 | |||
2313 | optbuf += sizeof(in_mss); | ||
2314 | len -= sizeof(in_mss); | ||
2315 | break; | 2301 | break; |
2316 | } | 2302 | case TCPOPT_WINDOW: |
2317 | case TCPOPT_WINDOW: { | 2303 | if (opt.opt_val > 14) |
2318 | u8 wscale; | ||
2319 | |||
2320 | if (len < sizeof(wscale)) | ||
2321 | return -ENODATA; | ||
2322 | if (get_user(wscale, optbuf)) | ||
2323 | return -EFAULT; | ||
2324 | |||
2325 | if (wscale > 14) | ||
2326 | return -EFBIG; | 2304 | return -EFBIG; |
2327 | 2305 | ||
2328 | tp->rx_opt.snd_wscale = wscale; | 2306 | tp->rx_opt.snd_wscale = opt.opt_val; |
2329 | |||
2330 | optbuf += sizeof(wscale); | ||
2331 | len -= sizeof(wscale); | ||
2332 | break; | 2307 | break; |
2333 | } | ||
2334 | case TCPOPT_SACK_PERM: | 2308 | case TCPOPT_SACK_PERM: |
2309 | if (opt.opt_val != 0) | ||
2310 | return -EINVAL; | ||
2311 | |||
2335 | tp->rx_opt.sack_ok |= TCP_SACK_SEEN; | 2312 | tp->rx_opt.sack_ok |= TCP_SACK_SEEN; |
2336 | if (sysctl_tcp_fack) | 2313 | if (sysctl_tcp_fack) |
2337 | tcp_enable_fack(tp); | 2314 | tcp_enable_fack(tp); |
2338 | break; | 2315 | break; |
2339 | case TCPOPT_TIMESTAMP: | 2316 | case TCPOPT_TIMESTAMP: |
2317 | if (opt.opt_val != 0) | ||
2318 | return -EINVAL; | ||
2319 | |||
2340 | tp->rx_opt.tstamp_ok = 1; | 2320 | tp->rx_opt.tstamp_ok = 1; |
2341 | break; | 2321 | break; |
2342 | } | 2322 | } |
@@ -2557,7 +2537,9 @@ static int do_tcp_setsockopt(struct sock *sk, int level, | |||
2557 | if (!tp->repair) | 2537 | if (!tp->repair) |
2558 | err = -EINVAL; | 2538 | err = -EINVAL; |
2559 | else if (sk->sk_state == TCP_ESTABLISHED) | 2539 | else if (sk->sk_state == TCP_ESTABLISHED) |
2560 | err = tcp_repair_options_est(tp, optval, optlen); | 2540 | err = tcp_repair_options_est(tp, |
2541 | (struct tcp_repair_opt __user *)optval, | ||
2542 | optlen); | ||
2561 | else | 2543 | else |
2562 | err = -EPERM; | 2544 | err = -EPERM; |
2563 | break; | 2545 | break; |