diff options
author | Eric Dumazet <edumazet@google.com> | 2016-01-27 13:52:43 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-01-29 01:49:30 -0500 |
commit | ff5d749772018602c47509bdc0093ff72acd82ec (patch) | |
tree | 4f2f716a27d5be037fe8137634b3c72054297e1c /net | |
parent | 4f2c6ae5c64c353fb1b0425e4747e5603feadba1 (diff) |
tcp: beware of alignments in tcp_get_info()
With some combinations of user provided flags in netlink command,
it is possible to call tcp_get_info() with a buffer that is not 8-bytes
aligned.
It does matter on some arches, so we need to use put_unaligned() to
store the u64 fields.
Current iproute2 package does not trigger this particular issue.
Fixes: 0df48c26d841 ("tcp: add tcpi_bytes_acked to tcp_info")
Fixes: 977cb0ecf82e ("tcp: add pacing_rate information into tcp_info")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/tcp.c | 12 |
1 files changed, 8 insertions, 4 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index fd17eec93525..19746b3fcbbe 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -279,6 +279,7 @@ | |||
279 | 279 | ||
280 | #include <asm/uaccess.h> | 280 | #include <asm/uaccess.h> |
281 | #include <asm/ioctls.h> | 281 | #include <asm/ioctls.h> |
282 | #include <asm/unaligned.h> | ||
282 | #include <net/busy_poll.h> | 283 | #include <net/busy_poll.h> |
283 | 284 | ||
284 | int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT; | 285 | int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT; |
@@ -2638,6 +2639,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) | |||
2638 | const struct inet_connection_sock *icsk = inet_csk(sk); | 2639 | const struct inet_connection_sock *icsk = inet_csk(sk); |
2639 | u32 now = tcp_time_stamp; | 2640 | u32 now = tcp_time_stamp; |
2640 | unsigned int start; | 2641 | unsigned int start; |
2642 | u64 rate64; | ||
2641 | u32 rate; | 2643 | u32 rate; |
2642 | 2644 | ||
2643 | memset(info, 0, sizeof(*info)); | 2645 | memset(info, 0, sizeof(*info)); |
@@ -2703,15 +2705,17 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) | |||
2703 | info->tcpi_total_retrans = tp->total_retrans; | 2705 | info->tcpi_total_retrans = tp->total_retrans; |
2704 | 2706 | ||
2705 | rate = READ_ONCE(sk->sk_pacing_rate); | 2707 | rate = READ_ONCE(sk->sk_pacing_rate); |
2706 | info->tcpi_pacing_rate = rate != ~0U ? rate : ~0ULL; | 2708 | rate64 = rate != ~0U ? rate : ~0ULL; |
2709 | put_unaligned(rate64, &info->tcpi_pacing_rate); | ||
2707 | 2710 | ||
2708 | rate = READ_ONCE(sk->sk_max_pacing_rate); | 2711 | rate = READ_ONCE(sk->sk_max_pacing_rate); |
2709 | info->tcpi_max_pacing_rate = rate != ~0U ? rate : ~0ULL; | 2712 | rate64 = rate != ~0U ? rate : ~0ULL; |
2713 | put_unaligned(rate64, &info->tcpi_max_pacing_rate); | ||
2710 | 2714 | ||
2711 | do { | 2715 | do { |
2712 | start = u64_stats_fetch_begin_irq(&tp->syncp); | 2716 | start = u64_stats_fetch_begin_irq(&tp->syncp); |
2713 | info->tcpi_bytes_acked = tp->bytes_acked; | 2717 | put_unaligned(tp->bytes_acked, &info->tcpi_bytes_acked); |
2714 | info->tcpi_bytes_received = tp->bytes_received; | 2718 | put_unaligned(tp->bytes_received, &info->tcpi_bytes_received); |
2715 | } while (u64_stats_fetch_retry_irq(&tp->syncp, start)); | 2719 | } while (u64_stats_fetch_retry_irq(&tp->syncp, start)); |
2716 | info->tcpi_segs_out = tp->segs_out; | 2720 | info->tcpi_segs_out = tp->segs_out; |
2717 | info->tcpi_segs_in = tp->segs_in; | 2721 | info->tcpi_segs_in = tp->segs_in; |