Commit de248a75 authored by Pavel Emelyanov's avatar Pavel Emelyanov Committed by David S. Miller

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: default avatarPavel Emelyanov <xemul@parallels.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2d319508
...@@ -111,6 +111,11 @@ enum { ...@@ -111,6 +111,11 @@ enum {
#define TCP_QUEUE_SEQ 21 #define TCP_QUEUE_SEQ 21
#define TCP_REPAIR_OPTIONS 22 #define TCP_REPAIR_OPTIONS 22
struct tcp_repair_opt {
__u32 opt_code;
__u32 opt_val;
};
enum { enum {
TCP_NO_QUEUE, TCP_NO_QUEUE,
TCP_RECV_QUEUE, TCP_RECV_QUEUE,
......
...@@ -2283,60 +2283,40 @@ static inline int tcp_can_repair_sock(struct sock *sk) ...@@ -2283,60 +2283,40 @@ static inline int tcp_can_repair_sock(struct sock *sk)
((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED));
} }
static int tcp_repair_options_est(struct tcp_sock *tp, char __user *optbuf, unsigned int len) static int tcp_repair_options_est(struct tcp_sock *tp,
struct tcp_repair_opt __user *optbuf, unsigned int len)
{ {
/* struct tcp_repair_opt opt;
* Options are stored in CODE:VALUE form where CODE is 8bit and VALUE
* fits the respective TCPOLEN_ size
*/
while (len > 0) {
u8 opcode;
if (get_user(opcode, optbuf)) while (len >= sizeof(opt)) {
if (copy_from_user(&opt, optbuf, sizeof(opt)))
return -EFAULT; return -EFAULT;
optbuf++; optbuf++;
len--; len -= sizeof(opt);
switch (opcode) { switch (opt.opt_code) {
case TCPOPT_MSS: { case TCPOPT_MSS:
u16 in_mss; tp->rx_opt.mss_clamp = opt.opt_val;
if (len < sizeof(in_mss))
return -ENODATA;
if (get_user(in_mss, optbuf))
return -EFAULT;
tp->rx_opt.mss_clamp = in_mss;
optbuf += sizeof(in_mss);
len -= sizeof(in_mss);
break; break;
} case TCPOPT_WINDOW:
case TCPOPT_WINDOW: { if (opt.opt_val > 14)
u8 wscale;
if (len < sizeof(wscale))
return -ENODATA;
if (get_user(wscale, optbuf))
return -EFAULT;
if (wscale > 14)
return -EFBIG; return -EFBIG;
tp->rx_opt.snd_wscale = wscale; tp->rx_opt.snd_wscale = opt.opt_val;
optbuf += sizeof(wscale);
len -= sizeof(wscale);
break; break;
}
case TCPOPT_SACK_PERM: case TCPOPT_SACK_PERM:
if (opt.opt_val != 0)
return -EINVAL;
tp->rx_opt.sack_ok |= TCP_SACK_SEEN; tp->rx_opt.sack_ok |= TCP_SACK_SEEN;
if (sysctl_tcp_fack) if (sysctl_tcp_fack)
tcp_enable_fack(tp); tcp_enable_fack(tp);
break; break;
case TCPOPT_TIMESTAMP: case TCPOPT_TIMESTAMP:
if (opt.opt_val != 0)
return -EINVAL;
tp->rx_opt.tstamp_ok = 1; tp->rx_opt.tstamp_ok = 1;
break; break;
} }
...@@ -2557,7 +2537,9 @@ static int do_tcp_setsockopt(struct sock *sk, int level, ...@@ -2557,7 +2537,9 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
if (!tp->repair) if (!tp->repair)
err = -EINVAL; err = -EINVAL;
else if (sk->sk_state == TCP_ESTABLISHED) else if (sk->sk_state == TCP_ESTABLISHED)
err = tcp_repair_options_est(tp, optval, optlen); err = tcp_repair_options_est(tp,
(struct tcp_repair_opt __user *)optval,
optlen);
else else
err = -EPERM; err = -EPERM;
break; break;
......
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