Re: [PATCH net v2 0/5] Make TCP-MD5-diag slightly less broken
From: Eric Dumazet
Date: Thu Dec 05 2024 - 04:12:06 EST
On Thu, Dec 5, 2024 at 2:13 AM Jakub Kicinski <kuba@xxxxxxxxxx> wrote:
>
> On Wed, 13 Nov 2024 18:46:39 +0000 Dmitry Safonov via B4 Relay wrote:
> > 2. Inet-diag allocates netlink message for sockets in
> > inet_diag_dump_one_icsk(), which uses a TCP-diag callback
> > .idiag_get_aux_size(), that pre-calculates the needed space for
> > TCP-diag related information. But as neither socket lock nor
> > rcu_readlock() are held between allocation and the actual TCP
> > info filling, the TCP-related space requirement may change before
> > reaching tcp_diag_put_md5sig(). I.e., the number of TCP-MD5 keys on
> > a socket. Thankfully, TCP-MD5-diag won't overwrite the skb, but will
> > return EMSGSIZE, triggering WARN_ON() in inet_diag_dump_one_icsk().
>
> Hi Eric!
>
> This was posted while you were away -- any thoughts or recommendation on
> how to address the required nl message size changing? Or other problems
> pointed out by Dmitry? My suggestion in the subthread is to re-dump
> with a fixed, large buffer on EMSGSIZE, but that's not super clean..
Hi Jakub
inet_diag_dump_one_icsk() could retry, doubling the size until the
~32768 byte limit is reached ?
Also, we could make sure inet_sk_attr_size() returns at least
NLMSG_DEFAULT_SIZE, there is no
point trying to save memory for a single skb in inet_diag_dump_one_icsk().
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 321acc8abf17e8c7d6a4e3326615123fff19deab..cd2e7fe9b090ea9127aebbba0faf2ef12c0f86a4
100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -102,7 +102,7 @@ static size_t inet_sk_attr_size(struct sock *sk,
bool net_admin)
{
const struct inet_diag_handler *handler;
- size_t aux = 0;
+ size_t aux = 0, res;
rcu_read_lock();
handler = rcu_dereference(inet_diag_table[req->sdiag_protocol]);
@@ -111,7 +111,7 @@ static size_t inet_sk_attr_size(struct sock *sk,
aux = handler->idiag_get_aux_size(sk, net_admin);
rcu_read_unlock();
- return nla_total_size(sizeof(struct tcp_info))
+ res = nla_total_size(sizeof(struct tcp_info))
+ nla_total_size(sizeof(struct inet_diag_msg))
+ inet_diag_msg_attrs_size()
+ nla_total_size(sizeof(struct inet_diag_meminfo))
@@ -120,6 +120,7 @@ static size_t inet_sk_attr_size(struct sock *sk,
+ nla_total_size(sizeof(struct tcpvegas_info))
+ aux
+ 64;
+ return max(res, NLMSG_DEFAULT_SIZE);
}
int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
@@ -570,6 +571,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
bool net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN);
struct net *net = sock_net(in_skb->sk);
struct sk_buff *rep;
+ size_t attr_size;
struct sock *sk;
int err;
@@ -577,7 +579,9 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
if (IS_ERR(sk))
return PTR_ERR(sk);
- rep = nlmsg_new(inet_sk_attr_size(sk, req, net_admin), GFP_KERNEL);
+ attr_size = inet_sk_attr_size(sk, req, net_admin);
+retry:
+ rep = nlmsg_new(attr_size, GFP_KERNEL);
if (!rep) {
err = -ENOMEM;
goto out;
@@ -585,8 +589,14 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
err = sk_diag_fill(sk, rep, cb, req, 0, net_admin);
if (err < 0) {
- WARN_ON(err == -EMSGSIZE);
nlmsg_free(rep);
+ if (err == -EMSGSIZE) {
+ attr_size <<= 1;
+ if (attr_size + NLMSG_HDRLEN <=
SKB_WITH_OVERHEAD(32768)) {
+ cond_resched();
+ goto retry;
+ }
+ }
goto out;
}
err = nlmsg_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid);