* [PATCH v7 net-next 0/2] Explicit TSO segment count
@ 2026-08-14 17:30 chia-yu.chang
2026-08-14 17:30 ` [PATCH v7 net-next 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
2026-08-14 17:30 ` [PATCH v7 net-next 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
0 siblings, 2 replies; 5+ messages in thread
From: chia-yu.chang @ 2026-08-14 17:30 UTC (permalink / raw)
To: john.fastabend, jakub, jiayuan.chen, netdev, bpf, ast, daniel,
andrii, eddyz87, memxor, martin.lau, song, yonghong.song, jolsa,
emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs, kuba,
stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel
Cc: Chia-Yu Chang
From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Hello,
Please find the v7 explicit tso segment count series.
It replacece min_tso_segs() with tso_segs() CC callback and make
tcp_tso_autosize() available to BPF congestion controls.
Best regards,
Chia-Yu
---
Chia-Yu Chang (2):
tcp: Replace min_tso_segs() with tso_segs() CC callback
bpf: make tcp_tso_autosize() available to BPF congestion controls
include/net/tcp.h | 12 ++++++++++--
net/ipv4/bpf_tcp_ca.c | 5 +++--
net/ipv4/tcp_bbr.c | 13 ++++++++++---
net/ipv4/tcp_output.c | 18 +++++++++---------
.../testing/selftests/bpf/progs/tcp_ca_kfunc.c | 16 ++++++++++++----
5 files changed, 44 insertions(+), 20 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v7 net-next 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback
2026-08-14 17:30 [PATCH v7 net-next 0/2] Explicit TSO segment count chia-yu.chang
@ 2026-08-14 17:30 ` chia-yu.chang
2026-08-15 17:31 ` sashiko-bot
2026-08-14 17:30 ` [PATCH v7 net-next 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
1 sibling, 1 reply; 5+ messages in thread
From: chia-yu.chang @ 2026-08-14 17:30 UTC (permalink / raw)
To: john.fastabend, jakub, jiayuan.chen, netdev, bpf, ast, daniel,
andrii, eddyz87, memxor, martin.lau, song, yonghong.song, jolsa,
emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs, kuba,
stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel
Cc: Chia-Yu Chang
From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
This patch replaces existing min_tso_segs() with tso_segs() CC callback
for CC algorithm to provide explicit tso segment number of each data
burst and overrides tcp_tso_autosize().
This change provides below impacts on BPF struct_ops users:
- The callback is renamed from min_tso_segs() to tso_segs()
- The signature gains an extra u32 mss_now argument
- The return value semantics is changed from "floor value passed into
tcp_tso_autosize()" to "final tso_segs value", bypassing autosizing
As a result, BPF programs shall be updated, because returning a small
constant will now directly limit the final tso_segs value instead of
specifying the minimum value passed to tcp_tso_autosize().
Signed-off-by: Ilpo Järvinen <ij@kernel.org>
Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
v7:
- Update the comments for tso_segs()
- Restore bpf_tcp_ca_tso_segs() to return 0
- Move READ_ONCE() to the else branch if ca_ops->tso_segs() is undefined
- Update tcp_tso_autosize() to use EXPORT_SYMBOL_GPL
v6:
- Add clamp_t to avoid returning 0 by ca_ops->tso_segs()
- Update commit message
v5:
- Revert back to v3 and add Reviewed-by tag
v4:
- Use union for both min_tso_segs() and tso_segs() and a
v3:
- Update bpf_tcp_ca_tso_segs() to use tcp_tso_autosize()
- Add divide by 0 protection in case of mss_now=0
v2:
- Export tcp_tso_autosize()
---
include/net/tcp.h | 12 ++++++++++--
net/ipv4/bpf_tcp_ca.c | 4 ++--
net/ipv4/tcp_bbr.c | 13 ++++++++++---
net/ipv4/tcp_output.c | 18 +++++++++---------
.../testing/selftests/bpf/progs/tcp_ca_kfunc.c | 8 ++++----
5 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 2c5b889530b5..37b95b98f9f9 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -824,6 +824,9 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu);
unsigned int tcp_current_mss(struct sock *sk);
u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when);
+u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
+ int min_tso_segs);
+
/* Bound MSS / TSO packet size with the half of the window */
static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
{
@@ -1361,8 +1364,13 @@ struct tcp_congestion_ops {
/* hook for packet ack accounting (optional) */
void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample);
- /* override sysctl_tcp_min_tso_segs (optional) */
- u32 (*min_tso_segs)(struct sock *sk);
+ /* Override tcp_tso_autosize (optional)
+ *
+ * If provided, this callback returns the final TSO segment count
+ * and bypasses tcp_tso_autosize() entirely. Implementations must
+ * handle the mss_now==0 case and derive an appropriate value.
+ */
+ u32 (*tso_segs)(struct sock *sk, u32 mss_now);
/* new value of cwnd after loss (required) */
u32 (*undo_cwnd)(struct sock *sk);
diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
index 791e15063237..ed4fea98dfde 100644
--- a/net/ipv4/bpf_tcp_ca.c
+++ b/net/ipv4/bpf_tcp_ca.c
@@ -284,7 +284,7 @@ static void bpf_tcp_ca_pkts_acked(struct sock *sk, const struct ack_sample *samp
{
}
-static u32 bpf_tcp_ca_min_tso_segs(struct sock *sk)
+static u32 bpf_tcp_ca_tso_segs(struct sock *sk, u32 mss_now)
{
return 0;
}
@@ -320,7 +320,7 @@ static struct tcp_congestion_ops __bpf_ops_tcp_congestion_ops = {
.cwnd_event_tx_start = bpf_tcp_ca_cwnd_event_tx_start,
.in_ack_event = bpf_tcp_ca_in_ack_event,
.pkts_acked = bpf_tcp_ca_pkts_acked,
- .min_tso_segs = bpf_tcp_ca_min_tso_segs,
+ .tso_segs = bpf_tcp_ca_tso_segs,
.cong_control = bpf_tcp_ca_cong_control,
.undo_cwnd = bpf_tcp_ca_undo_cwnd,
.sndbuf_expand = bpf_tcp_ca_sndbuf_expand,
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index 82378a2bfd1e..ecf11be46f38 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -297,11 +297,18 @@ static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
}
/* override sysctl_tcp_min_tso_segs */
-__bpf_kfunc static u32 bbr_min_tso_segs(struct sock *sk)
+static u32 bbr_min_tso_segs(struct sock *sk)
{
return READ_ONCE(sk->sk_pacing_rate) < (bbr_min_tso_rate >> 3) ? 1 : 2;
}
+__bpf_kfunc static u32 bbr_tso_segs(struct sock *sk, u32 mss_now)
+{
+ if (unlikely(!mss_now))
+ return bbr_min_tso_segs(sk);
+ return tcp_tso_autosize(sk, mss_now, bbr_min_tso_segs(sk));
+}
+
static u32 bbr_tso_segs_goal(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -1151,7 +1158,7 @@ static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = {
.undo_cwnd = bbr_undo_cwnd,
.cwnd_event_tx_start = bbr_cwnd_event_tx_start,
.ssthresh = bbr_ssthresh,
- .min_tso_segs = bbr_min_tso_segs,
+ .tso_segs = bbr_tso_segs,
.get_info = bbr_get_info,
.set_state = bbr_set_state,
};
@@ -1163,7 +1170,7 @@ BTF_ID_FLAGS(func, bbr_sndbuf_expand)
BTF_ID_FLAGS(func, bbr_undo_cwnd)
BTF_ID_FLAGS(func, bbr_cwnd_event_tx_start)
BTF_ID_FLAGS(func, bbr_ssthresh)
-BTF_ID_FLAGS(func, bbr_min_tso_segs)
+BTF_ID_FLAGS(func, bbr_tso_segs)
BTF_ID_FLAGS(func, bbr_set_state)
BTF_KFUNCS_END(tcp_bbr_check_kfunc_ids)
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index fcaa04e65189..7d3e0e715c4b 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2253,8 +2253,8 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
* for every 2^9 usec (aka 512 us) of RTT, so that the RTT-based allowance
* is below 1500 bytes after 6 * ~500 usec = 3ms.
*/
-static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
- int min_tso_segs)
+u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
+ int min_tso_segs)
{
unsigned long bytes;
u32 r;
@@ -2269,6 +2269,7 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
return max_t(u32, bytes / mss_now, min_tso_segs);
}
+EXPORT_SYMBOL_GPL(tcp_tso_autosize);
/* Return the number of segments we want in the skb we are transmitting.
* See if congestion control module wants to decide; otherwise, autosize.
@@ -2276,14 +2277,13 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
static u32 tcp_tso_segs(struct sock *sk, unsigned int mss_now)
{
const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
- u32 min_tso, tso_segs;
+ u32 tso_segs;
- min_tso = ca_ops->min_tso_segs ?
- ca_ops->min_tso_segs(sk) :
- READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_min_tso_segs);
-
- tso_segs = tcp_tso_autosize(sk, mss_now, min_tso);
- return min_t(u32, tso_segs, sk->sk_gso_max_segs);
+ tso_segs = ca_ops->tso_segs ?
+ ca_ops->tso_segs(sk, mss_now) :
+ tcp_tso_autosize(sk, mss_now,
+ READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_min_tso_segs));
+ return clamp_t(u32, tso_segs, 1, sk->sk_gso_max_segs);
}
/* Returns the portion of skb which can be sent right away */
diff --git a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
index 0a3e9d35bf6f..58262e490336 100644
--- a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
+++ b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
@@ -10,7 +10,7 @@ extern u32 bbr_sndbuf_expand(struct sock *sk) __ksym;
extern u32 bbr_undo_cwnd(struct sock *sk) __ksym;
extern void bbr_cwnd_event_tx_start(struct sock *sk) __ksym;
extern u32 bbr_ssthresh(struct sock *sk) __ksym;
-extern u32 bbr_min_tso_segs(struct sock *sk) __ksym;
+extern u32 bbr_tso_segs(struct sock *sk, u32 mss_now) __ksym;
extern void bbr_set_state(struct sock *sk, u8 new_state) __ksym;
extern void dctcp_init(struct sock *sk) __ksym;
@@ -90,9 +90,9 @@ u32 BPF_PROG(ssthresh, struct sock *sk)
}
SEC("struct_ops")
-u32 BPF_PROG(min_tso_segs, struct sock *sk)
+u32 BPF_PROG(tso_segs, struct sock *sk, u32 mss_now)
{
- return bbr_min_tso_segs(sk);
+ return bbr_tso_segs(sk, mss_now);
}
SEC("struct_ops")
@@ -120,7 +120,7 @@ struct tcp_congestion_ops tcp_ca_kfunc = {
.cwnd_event = (void *)cwnd_event,
.cwnd_event_tx_start = (void *)cwnd_event_tx_start,
.ssthresh = (void *)ssthresh,
- .min_tso_segs = (void *)min_tso_segs,
+ .tso_segs = (void *)tso_segs,
.set_state = (void *)set_state,
.pkts_acked = (void *)pkts_acked,
.name = "tcp_ca_kfunc",
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v7 net-next 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls
2026-08-14 17:30 [PATCH v7 net-next 0/2] Explicit TSO segment count chia-yu.chang
2026-08-14 17:30 ` [PATCH v7 net-next 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
@ 2026-08-14 17:30 ` chia-yu.chang
2026-08-15 17:31 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: chia-yu.chang @ 2026-08-14 17:30 UTC (permalink / raw)
To: john.fastabend, jakub, jiayuan.chen, netdev, bpf, ast, daniel,
andrii, eddyz87, memxor, martin.lau, song, yonghong.song, jolsa,
emil, linux-kselftest, shuah, horms, dsahern, pabeni, jhs, kuba,
stephen, davem, edumazet, andrew+netdev, donald.hunter, kuniyu,
ij, ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel
Cc: Chia-Yu Chang
From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Expose tcp_tso_autosize() as a BPF kfunc and register it in the TCP
congestion-control kfunc set. This allows BPF congestion controls to
reuse the kernel TSO autosizing logic while applying their own
minimum TSO segment policy.
Add a selftest exercising tcp_tso_autosize() from a BPF congestion
control through the tso_segs() callback.
Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
---
net/ipv4/bpf_tcp_ca.c | 1 +
net/ipv4/tcp_output.c | 4 ++--
tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c | 8 ++++++++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
index ed4fea98dfde..9deed2244c2d 100644
--- a/net/ipv4/bpf_tcp_ca.c
+++ b/net/ipv4/bpf_tcp_ca.c
@@ -194,6 +194,7 @@ BTF_ID_FLAGS(func, tcp_reno_cong_avoid)
BTF_ID_FLAGS(func, tcp_reno_undo_cwnd)
BTF_ID_FLAGS(func, tcp_slow_start)
BTF_ID_FLAGS(func, tcp_cong_avoid_ai)
+BTF_ID_FLAGS(func, tcp_tso_autosize)
BTF_KFUNCS_END(bpf_tcp_ca_check_kfunc_ids)
static const struct btf_kfunc_id_set bpf_tcp_ca_kfunc_set = {
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 7d3e0e715c4b..7914287dd571 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2253,8 +2253,8 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
* for every 2^9 usec (aka 512 us) of RTT, so that the RTT-based allowance
* is below 1500 bytes after 6 * ~500 usec = 3ms.
*/
-u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
- int min_tso_segs)
+__bpf_kfunc u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
+ int min_tso_segs)
{
unsigned long bytes;
u32 r;
diff --git a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
index 58262e490336..c0f8f20f3052 100644
--- a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
+++ b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
@@ -28,6 +28,8 @@ extern void cubictcp_state(struct sock *sk, u8 new_state) __ksym;
extern void cubictcp_cwnd_event_tx_start(struct sock *sk) __ksym;
extern void cubictcp_acked(struct sock *sk, const struct ack_sample *sample) __ksym;
+extern u32 tcp_tso_autosize(const struct sock *sk, u32 mss_now, int min_tso_segs) __ksym;
+
SEC("struct_ops")
void BPF_PROG(init, struct sock *sk)
{
@@ -109,6 +111,12 @@ void BPF_PROG(pkts_acked, struct sock *sk, const struct ack_sample *sample)
cubictcp_acked(sk, sample);
}
+SEC("struct_ops")
+void BPF_PROG(tso_segs, struct sock *sk, u32 mss_now)
+{
+ return tcp_tso_autosize(sk, mss_now, 1);
+}
+
SEC(".struct_ops")
struct tcp_congestion_ops tcp_ca_kfunc = {
.init = (void *)init,
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v7 net-next 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback
2026-08-14 17:30 ` [PATCH v7 net-next 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
@ 2026-08-15 17:31 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-15 17:31 UTC (permalink / raw)
To: chia-yu.chang; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] BPF API breakage: `tcp_tso_autosize` is not exposed as a BPF kfunc, making it impossible for custom BPF congestion control algorithms to correctly implement the new `tso_segs` callback.
--
--- Patch [1]: [PATCH v7 net-next 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback ---
commit c7a0bf37c3be2ea7dea45bba16c15ffe35b92e31
Author: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
tcp: Replace min_tso_segs() with tso_segs() CC callback
This commit replaces the min_tso_segs() callback in TCP congestion control
with a new tso_segs() callback. It exports tcp_tso_autosize() for use by
loadable kernel modules and updates the CC algorithms to use the new callback.
> diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
> index fcaa04e65189b..7d3e0e715c4bf 100644
> --- a/net/ipv4/tcp_output.c
> +++ b/net/ipv4/tcp_output.c
[ ... ]
> @@ -2269,6 +2269,7 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
>
> return max_t(u32, bytes / mss_now, min_tso_segs);
> }
> +EXPORT_SYMBOL_GPL(tcp_tso_autosize);
[Severity: High]
Since the new tso_segs callback completely bypasses the core kernel's
tcp_tso_autosize() logic, BPF congestion control algorithms must now return
the absolute final TSO segment count instead of just a minimum bound.
Although this patch exports tcp_tso_autosize() for loadable kernel modules,
is there a reason it wasn't also registered as a BPF kfunc (via BTF_ID_FLAGS)
in the kfunc registry for BPF TCP congestion control?
Without being able to call tcp_tso_autosize() from BPF programs, custom BPF
struct_ops CC algorithms cannot easily compute the autosized value. If they
return a small constant to match the old API, they might inadvertently hardcap
TSO burst size and cause severe performance regressions. Otherwise, they are
forced to redundantly reimplement complex internal kernel state logic within
BPF.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814173034.749151-1-chia-yu.chang@nokia-bell-labs.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v7 net-next 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls
2026-08-14 17:30 ` [PATCH v7 net-next 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
@ 2026-08-15 17:31 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-15 17:31 UTC (permalink / raw)
To: chia-yu.chang; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Exposing `tcp_tso_autosize` as a BPF kfunc allows a BPF program to trigger a kernel panic via division by zero by passing `mss_now = 0`.
- [Low] The selftest `tcp_ca_kfunc.c` fails to compile due to function redefinition and an invalid return type.
Pre-existing issues:
- [High] The already exposed kfunc `tcp_cong_avoid_ai` is vulnerable to a divide-by-zero kernel panic if a BPF program passes `w = 0`.
--
--- Patch [2]: [PATCH v7 net-next 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls ---
commit 8757143027760bc7421911e55324877a61cb35ac
Author: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
bpf: make tcp_tso_autosize() available to BPF congestion controls
Expose tcp_tso_autosize() as a BPF kfunc and register it in the TCP
congestion-control kfunc set. This allows BPF congestion controls to
reuse the kernel TSO autosizing logic while applying their own
minimum TSO segment policy. Add a selftest exercising tcp_tso_autosize()
from a BPF congestion control through the tso_segs() callback.
> diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
> index ed4fea98dfde1..9deed2244c2de 100644
> --- a/net/ipv4/bpf_tcp_ca.c
> +++ b/net/ipv4/bpf_tcp_ca.c
> @@ -194,6 +194,7 @@ BTF_ID_FLAGS(func, tcp_reno_cong_avoid)
> BTF_ID_FLAGS(func, tcp_reno_undo_cwnd)
> BTF_ID_FLAGS(func, tcp_slow_start)
> BTF_ID_FLAGS(func, tcp_cong_avoid_ai)
[Severity: High]
This is a pre-existing issue, but since tcp_cong_avoid_ai() is already
exposed as a kfunc, is it vulnerable to a divide-by-zero kernel panic?
If a BPF program passes w = 0 to tcp_cong_avoid_ai() in net/ipv4/tcp_cong.c,
the condition tp->snd_cwnd_cnt >= w will evaluate to true, leading to:
u32 delta = tp->snd_cwnd_cnt / w;
Since the BPF verifier does not enforce value range constraints on plain
scalar arguments, could a privileged user load a BPF program that crashes the
kernel this way?
> +BTF_ID_FLAGS(func, tcp_tso_autosize)
> BTF_KFUNCS_END(bpf_tcp_ca_check_kfunc_ids)
[ ... ]
> diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
> index 7d3e0e715c4bf..7914287dd5710 100644
> --- a/net/ipv4/tcp_output.c
> +++ b/net/ipv4/tcp_output.c
[ ... ]
> @@ -2253,8 +2253,8 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
> * for every 2^9 usec (aka 512 us) of RTT, so that the RTT-based allowance
> * is below 1500 bytes after 6 * ~500 usec = 3ms.
> */
> -u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
> - int min_tso_segs)
> +__bpf_kfunc u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
> + int min_tso_segs)
[Severity: High]
Similarly, does exposing tcp_tso_autosize() as a kfunc introduce a new
divide-by-zero risk?
Inside tcp_tso_autosize(), there is an unchecked division by mss_now:
return max_t(u32, bytes / mss_now, min_tso_segs);
Could a BPF congestion control program pass 0 for mss_now, bypassing the
verifier's checks since it is an unconstrained scalar, and trigger a
divide-by-zero kernel panic?
> {
> unsigned long bytes;
> u32 r;
[ ... ]
> diff --git a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
> index 58262e4903364..c0f8f20f3052c 100644
> --- a/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
> +++ b/tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c
[ ... ]
> @@ -109,6 +111,12 @@ void BPF_PROG(pkts_acked, struct sock *sk, const struct ack_sample *sample)
> cubictcp_acked(sk, sample);
> }
>
> +SEC("struct_ops")
> +void BPF_PROG(tso_segs, struct sock *sk, u32 mss_now)
> +{
> + return tcp_tso_autosize(sk, mss_now, 1);
> +}
[Severity: Low]
Does this selftest compile correctly?
It looks like tso_segs is defined with a void return type, but then returns a
value from tcp_tso_autosize(), which violates C constraints. Additionally,
could this cause a function redefinition error if u32 BPF_PROG(tso_segs...)
is already defined earlier in this file?
> +
> SEC(".struct_ops")
> struct tcp_congestion_ops tcp_ca_kfunc = {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814173034.749151-1-chia-yu.chang@nokia-bell-labs.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-15 17:31 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14 17:30 [PATCH v7 net-next 0/2] Explicit TSO segment count chia-yu.chang
2026-08-14 17:30 ` [PATCH v7 net-next 1/2] tcp: Replace min_tso_segs() with tso_segs() CC callback chia-yu.chang
2026-08-15 17:31 ` sashiko-bot
2026-08-14 17:30 ` [PATCH v7 net-next 2/2] bpf: make tcp_tso_autosize() available to BPF congestion controls chia-yu.chang
2026-08-15 17:31 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox