From: Martin KaFai Lau <martin.lau@linux.dev>
To: Eric Dumazet <edumazet@google.com>
Cc: bpf <bpf@vger.kernel.org>, netdev <netdev@vger.kernel.org>,
Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
David Miller <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>,
kernel-team <kernel-team@fb.com>, Paolo Abeni <pabeni@redhat.com>
Subject: Re: [PATCH v2 bpf-next 4/5] bpf: tcp: Stop bpf_setsockopt(TCP_CONGESTION) in init ops to recur itself
Date: Wed, 28 Sep 2022 23:17:38 -0700 [thread overview]
Message-ID: <4e6e14e4-7301-9981-c52f-7715f26f63f3@linux.dev> (raw)
In-Reply-To: <CANn89i+7G7kkN5mG=tEOd4xHAV7LyLQ7yj2a4hjsGb1_gFQ82A@mail.gmail.com>
On 9/28/22 10:37 PM, Eric Dumazet wrote:
> On Wed, Sep 28, 2022 at 10:31 PM Martin KaFai Lau <martin.lau@linux.dev> wrote:
>>
>> On 9/28/22 7:04 PM, Eric Dumazet wrote:
>>> On Fri, Sep 23, 2022 at 3:48 PM Martin KaFai Lau <kafai@fb.com> wrote:
>>>>
>>>> From: Martin KaFai Lau <martin.lau@kernel.org>
>>>>
>>>> When a bad bpf prog '.init' calls
>>>> bpf_setsockopt(TCP_CONGESTION, "itself"), it will trigger this loop:
>>>>
>>>> .init => bpf_setsockopt(tcp_cc) => .init => bpf_setsockopt(tcp_cc) ...
>>>> ... => .init => bpf_setsockopt(tcp_cc).
>>>>
>>>> It was prevented by the prog->active counter before but the prog->active
>>>> detection cannot be used in struct_ops as explained in the earlier
>>>> patch of the set.
>>>>
>>>> In this patch, the second bpf_setsockopt(tcp_cc) is not allowed
>>>> in order to break the loop. This is done by using a bit of
>>>> an existing 1 byte hole in tcp_sock to check if there is
>>>> on-going bpf_setsockopt(TCP_CONGESTION) in this tcp_sock.
>>>>
>>>> Note that this essentially limits only the first '.init' can
>>>> call bpf_setsockopt(TCP_CONGESTION) to pick a fallback cc (eg. peer
>>>> does not support ECN) and the second '.init' cannot fallback to
>>>> another cc. This applies even the second
>>>> bpf_setsockopt(TCP_CONGESTION) will not cause a loop.
>>>>
>>>> Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
>>>> ---
>>>> include/linux/tcp.h | 6 ++++++
>>>> net/core/filter.c | 28 +++++++++++++++++++++++++++-
>>>> 2 files changed, 33 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/include/linux/tcp.h b/include/linux/tcp.h
>>>> index a9fbe22732c3..3bdf687e2fb3 100644
>>>> --- a/include/linux/tcp.h
>>>> +++ b/include/linux/tcp.h
>>>> @@ -388,6 +388,12 @@ struct tcp_sock {
>>>> u8 bpf_sock_ops_cb_flags; /* Control calling BPF programs
>>>> * values defined in uapi/linux/tcp.h
>>>> */
>>>> + u8 bpf_chg_cc_inprogress:1; /* In the middle of
>>>> + * bpf_setsockopt(TCP_CONGESTION),
>>>> + * it is to avoid the bpf_tcp_cc->init()
>>>> + * to recur itself by calling
>>>> + * bpf_setsockopt(TCP_CONGESTION, "itself").
>>>> + */
>>>> #define BPF_SOCK_OPS_TEST_FLAG(TP, ARG) (TP->bpf_sock_ops_cb_flags & ARG)
>>>> #else
>>>> #define BPF_SOCK_OPS_TEST_FLAG(TP, ARG) 0
>>>> diff --git a/net/core/filter.c b/net/core/filter.c
>>>> index 96f2f7a65e65..ac4c45c02da5 100644
>>>> --- a/net/core/filter.c
>>>> +++ b/net/core/filter.c
>>>> @@ -5105,6 +5105,9 @@ static int bpf_sol_tcp_setsockopt(struct sock *sk, int optname,
>>>> static int sol_tcp_sockopt_congestion(struct sock *sk, char *optval,
>>>> int *optlen, bool getopt)
>>>> {
>>>> + struct tcp_sock *tp;
>>>> + int ret;
>>>> +
>>>> if (*optlen < 2)
>>>> return -EINVAL;
>>>>
>>>> @@ -5125,8 +5128,31 @@ static int sol_tcp_sockopt_congestion(struct sock *sk, char *optval,
>>>> if (*optlen >= sizeof("cdg") - 1 && !strncmp("cdg", optval, *optlen))
>>>> return -ENOTSUPP;
>>>>
>>>> - return do_tcp_setsockopt(sk, SOL_TCP, TCP_CONGESTION,
>>>> + /* It stops this looping
>>>> + *
>>>> + * .init => bpf_setsockopt(tcp_cc) => .init =>
>>>> + * bpf_setsockopt(tcp_cc)" => .init => ....
>>>> + *
>>>> + * The second bpf_setsockopt(tcp_cc) is not allowed
>>>> + * in order to break the loop when both .init
>>>> + * are the same bpf prog.
>>>> + *
>>>> + * This applies even the second bpf_setsockopt(tcp_cc)
>>>> + * does not cause a loop. This limits only the first
>>>> + * '.init' can call bpf_setsockopt(TCP_CONGESTION) to
>>>> + * pick a fallback cc (eg. peer does not support ECN)
>>>> + * and the second '.init' cannot fallback to
>>>> + * another.
>>>> + */
>>>> + tp = tcp_sk(sk);
>>>> + if (tp->bpf_chg_cc_inprogress)
>>>> + return -EBUSY;
>>>> +
>>>
>>> Is the socket locked (and owned by current thread) at this point ?
>>> If not, changing bpf_chg_cc_inprogress would be racy.
>>
>> Yes, the socket is locked and owned. There is a sock_owned_by_me check earlier
>> in _bpf_setsockopt().
>
> Good to know. Note a listener can be cloned without socket lock being held.
>
> In order to avoid surprises, I would clear bpf_chg_cc_inprogress in
> tcp_create_openreq_child()
Ah, make sense. I will re-spin.
next prev parent reply other threads:[~2022-09-29 6:18 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-23 22:44 [PATCH v2 bpf-next 0/5] bpf: Remove recursion check for struct_ops prog Martin KaFai Lau
2022-09-23 22:44 ` [PATCH v2 bpf-next 1/5] bpf: Add __bpf_prog_{enter,exit}_struct_ops for struct_ops trampoline Martin KaFai Lau
2022-09-23 22:45 ` [PATCH v2 bpf-next 2/5] bpf: Move the "cdg" tcp-cc check to the common sol_tcp_sockopt() Martin KaFai Lau
2022-09-23 22:45 ` [PATCH v2 bpf-next 3/5] bpf: Refactor bpf_setsockopt(TCP_CONGESTION) handling into another function Martin KaFai Lau
2022-09-23 22:45 ` [PATCH v2 bpf-next 4/5] bpf: tcp: Stop bpf_setsockopt(TCP_CONGESTION) in init ops to recur itself Martin KaFai Lau
2022-09-27 3:34 ` Alexei Starovoitov
2022-09-29 1:12 ` Alexei Starovoitov
2022-09-29 2:04 ` Eric Dumazet
2022-09-29 5:31 ` Martin KaFai Lau
2022-09-29 5:37 ` Eric Dumazet
2022-09-29 6:17 ` Martin KaFai Lau [this message]
2022-09-23 22:45 ` [PATCH v2 bpf-next 5/5] selftests/bpf: Check -EBUSY for the recurred bpf_setsockopt(TCP_CONGESTION) Martin KaFai Lau
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4e6e14e4-7301-9981-c52f-7715f26f63f3@linux.dev \
--to=martin.lau@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kernel-team@fb.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.