aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp.c
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@parallels.com>2012-04-25 19:43:04 -0400
committerDavid S. Miller <davem@davemloft.net>2012-04-26 06:13:51 -0400
commitde248a75c35e0208294cf304b112916254b69184 (patch)
tree0d00a02fdb994f8b1cbf239c11080fec86977639 /net/ipv4/tcp.c
parent2d319508a3551d2995e5cd12d649821b3be00e5b (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.c60
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
2286static int tcp_repair_options_est(struct tcp_sock *tp, char __user *optbuf, unsigned int len) 2286static 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;