* [PATCH net-next v6 1/3] dt-bindings: vendor-prefixes: add Guangdong Dapu Telecom Co., Ltd.
From: Artem Shimko @ 2026-08-05 8:55 UTC (permalink / raw)
To: netdev, Andrew Lunn, Heiner Kallweit, Russell King,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Chevallier
Cc: Artem Shimko, linux-kernel, devicetree
In-Reply-To: <20260805085540.452260-1-a.shimko.dev@gmail.com>
Add vendor prefix for Guangdong Dapu Telecom Co., Ltd. [1], a manufacturer
of Ethernet PHYs, networking and other equipment. The prefix will be
used in the DAP8211R(I) Gigabit Ethernet PHY binding.
[1] https://www.dptel.com/
Signed-off-by: Artem Shimko <a.shimko.dev@gmail.com>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 396044f368e7..f8efecb560b6 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -457,6 +457,8 @@ patternProperties:
description: Dongwoon Anatech
"^dptechnics,.*":
description: DPTechnics
+ "^dptel,.*":
+ description: Guangdong Dapu Telecom Co., Ltd.
"^dragino,.*":
description: Dragino Technology Co., Limited
"^dream,.*":
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v6 0/3] Add DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY driver
From: Artem Shimko @ 2026-08-05 8:55 UTC (permalink / raw)
To: netdev, Andrew Lunn, Heiner Kallweit, Russell King,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Chevallier
Cc: Artem Shimko, linux-kernel, devicetree
Hello,
This series adds support for the DAPU Telecom DAP8211R(I) Gigabit
Ethernet PHY, commonly used in enterprise and industrial networking
applications. The PHY supports 10/100/1000 Mbps operation with RGMII
interface and includes features such as IEEE 802.3az Energy Efficient
Ethernet, IEEE 1588 SyncE.
The driver implements extended register access via indirect addressing
(registers 0x1E/0x1F) and provides comprehensive device tree support
for RGMII delay configuration. The rx-internal-delay-ps and
tx-internal-delay-ps properties allow precise tuning of clock delays
in 150 ps steps from 0 to 2250 ps.
This PHY is used on the NDA platform with 1G Ethernet tile and has
been tested on that hardware with successful link establishment and
RGMII delay tuning.
$ make dt_binding_check DT_SCHEMA_FILES=dptel,dap8211r.yaml
SCHEMA Documentation/devicetree/bindings/processed-schema.json
CHKDT ./Documentation/devicetree/bindings
LINT ./Documentation/devicetree/bindings
STYLE ./Documentation/devicetree/bindings
DTEX Documentation/devicetree/bindings/net/dptel,dap8211r.example.dts
DTC [C] Documentation/devicetree/bindings/net/dptel,dap8211r.example.dtb
$ yamllint Documentation/devicetree/bindings/net/dptel,dap8211r.yaml
$ grep -i "dap8211r" Documentation/devicetree/bindings/processed-schema.json
"http://devicetree.org/schemas/net/dptel,dap8211r.yaml": {
"$filename": "/home/a-shimko/patchwork/linux/Documentation/devicetree/bindings/net/dptel,dap8211r.yaml",
"$id": "http://devicetree.org/schemas/net/dptel,dap8211r.yaml#",
"title": "DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY",
Working with xgmac.
Board side:
$ arping -I eth0 192.168.5.100
ARPING 192.168.5.1 from 192.168.5.100 eth0
Unicast reply from 192.168.5.1 [board.mac.addr] 8.543ms
Unicast reply from 192.168.5.1 [board.mac.addr] 3.295ms
Unicast reply from 192.168.5.1 [board.mac.addr] 4.301ms
Unicast reply from 192.168.5.1 [board.mac.addr] 4.096ms
Unicast reply from 192.168.5.1 [board.mac.addr] 2.872ms
...
Unfortunately, there is a dependence on the axibus speed here
$ iperf3 -c 192.168.5.1
Connecting to host 192.168.5.1, port 5201
[ 5] local 192.168.5.100 port 58936 connected to 192.168.5.1 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 7.88 MBytes 65.8 Mbits/sec 0 150 KBytes
[ 5] 1.00-2.00 sec 8.50 MBytes 71.4 Mbits/sec 0 165 KBytes
[ 5] 2.00-3.00 sec 8.25 MBytes 69.2 Mbits/sec 0 165 KBytes
[ 5] 3.00-4.01 sec 8.50 MBytes 71.1 Mbits/sec 0 165 KBytes
[ 5] 4.01-5.00 sec 8.38 MBytes 70.3 Mbits/sec 0 165 KBytes
[ 5] 5.00-6.00 sec 8.50 MBytes 71.5 Mbits/sec 0 165 KBytes
[ 5] 6.00-7.01 sec 8.62 MBytes 72.0 Mbits/sec 0 174 KBytes
[ 5] 7.01-8.00 sec 8.62 MBytes 72.8 Mbits/sec 0 174 KBytes
[ 5] 8.00-9.00 sec 8.62 MBytes 72.2 Mbits/sec 0 174 KBytes
[ 5] 9.00-10.04 sec 8.62 MBytes 69.9 Mbits/sec 0 174 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.04 sec 84.6 MBytes 70.7 Mbits/sec 0 sender
[ 5] 0.00-10.12 sec 84.8 MBytes 70.3 Mbits/sec receiveriperf Done.
$ ethtool -t eth0
...
The test extra info:
1. MAC Loopback 0
2. MAC Loopback (diff. queues) 0
3. PHY Loopback 0
...
ELP side:
...
17:29:11.974973 ARP, Reply ELP is-at elp.mac.addr(oui Unknown), length 28
17:29:12.975199 ARP, Request who-has ELP tell 192.168.5.100, length 46
17:29:12.975217 ARP, Reply ELP is-at elp.mac.addr(oui Unknown), length 28
17:29:13.975022 ARP, Request who-has ELP tell 192.168.5.100, length 46
17:29:13.975035 ARP, Reply ELP is-at elp.mac.addr(oui Unknown), length 28
17:29:14.974837 ARP, Request who-has ELP tell 192.168.5.100, length 46
17:29:14.974849 ARP, Reply ELP is-at elp.mac.addr(oui Unknown), length 28
17:29:15.975026 ARP, Request who-has ELP tell 192.168.5.100, length 46
...
Accepted connection from 192.168.5.100, port 58932
[ 5] local 192.168.5.1 port 5201 connected to 192.168.5.100 port 58936
[ ID] Interval Transfer Bitrate
[ 5] 0.00-1.00 sec 7.12 MBytes 59.7 Mbits/sec
[ 5] 1.00-2.00 sec 8.50 MBytes 71.3 Mbits/sec
[ 5] 2.00-3.00 sec 8.50 MBytes 71.3 Mbits/sec
[ 5] 3.00-4.00 sec 8.38 MBytes 70.3 Mbits/sec
[ 5] 4.00-5.00 sec 8.50 MBytes 71.3 Mbits/sec
[ 5] 5.00-6.00 sec 8.38 MBytes 70.3 Mbits/sec
[ 5] 6.00-7.00 sec 8.62 MBytes 72.4 Mbits/sec
[ 5] 7.00-8.00 sec 8.62 MBytes 72.3 Mbits/sec
[ 5] 8.00-9.00 sec 8.62 MBytes 72.4 Mbits/sec
[ 5] 9.00-10.00 sec 8.62 MBytes 72.4 Mbits/sec
[ 5] 10.00-10.12 sec 896 KBytes 62.3 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.12 sec 84.8 MBytes 70.3 Mbits/sec receiver
DTS node example:
ðernet_1g_tile {
...
phy-mode = "rgmii-rxid";
phy-handle = <&phy1>;
...
mdio: mdio {
phy1: ethernet-phy@1 {
...
compatible = "ethernet-phy-id0008.011b";
rx-internal-delay-ps = <1950>;
...
};
};
};
Thank you!
--
Best regards,
Artem Shimko
ChangeLog:
v5 --> v6
- Rename vendor prefix from dapu to dptel
- Update DT binding filename to dptel,dap8211r.yaml
- Add clarifying comments for hardware reset defaults in driver
- Update rx-internal-delay-ps example in cover letter
v4 --> v5
- Add Guangdong Dapu to vendor-prefixes
- Remov unimplemented features from Kconfig help text
- Fix inconsistent handling of RGMII delay properties
- Add hardware-specified initial delay values for RX (0) and TX (1)
v3 --> v4
- Drop dapu,tx-inverted-clk (vendor-specific property removed)
- Fix driver behavior in relation to rgmii modes
- Apply software reset before RGMII register writes
v2 --> v3
- Use phy_get_internal_delay() for delay validation and selection
- Add poll timeout for reset using read_poll_timeout()
v1 --> v2
- Drop debugfs interface
- Simplify RGMII delay reading logic using of_property_read_u32()
- Fix missing newline at end of dapu,dap8211r.yaml (yamllint error)
- Simplify delay property description in DT binding
- Rename tx-inverted-clk to dapu,tx-inverted-clk (vendor prefix)
- Replace enum with multipleOf + maximum for delay validation
- Fix compatible string and tx-internal-delay-ps value in example
- Remove rounding logic, return -EINVAL for unsupported delay values
- Respect DT delay properties for all RGMII modes
- Add polling for self-clearing reset bit instead of fixed sleep
- Remove unused packet generator macros (DAP8211R_PKGC5 and related)
Artem Shimko (3):
dt-bindings: vendor-prefixes: add Guangdong Dapu Telecom Co., Ltd.
dt-bindings: net: add DAPU Telecom DAP8211R(I) PHY binding
net: phy: add DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY driver
.../bindings/net/dptel,dap8211r.yaml | 62 +++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
drivers/net/phy/Kconfig | 9 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/dap8211r.c | 220 ++++++++++++++++++
5 files changed, 294 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/dptel,dap8211r.yaml
create mode 100644 drivers/net/phy/dap8211r.c
--
2.43.0
^ permalink raw reply
* [PATCH nf-next] net: pass net_device_path_ctx to dev_fill_forward_path()
From: Lorenzo Bianconi @ 2026-08-05 8:53 UTC (permalink / raw)
To: Lorenzo Bianconi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, Pablo Neira Ayuso,
Florian Westphal, Phil Sutter
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, Lorenzo Bianconi
Refactor dev_fill_forward_path() to take a struct net_device_path_ctx
pointer instead of a (dev, daddr) pair, so the caller can build and
populate the context up front and keep it after the forward path walk.
This allows additional fields (e.g. vlan and ether_type) to be carried
in the context and shared with ndo_fill_forward_path implementations,
instead of being reconstructed on the stack inside the core helper.
Update the mtk_ppe_offload, airoha_ppe and nf_flow_table_path callers to
allocate and fill the context before invoking dev_fill_forward_path().
The network topology resolution behaviour is unchanged.
This is a preliminary patch to enable HW flowtable offload for IPv4
over IPv6 tunnels.
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
---
drivers/net/ethernet/airoha/airoha_ppe.c | 7 ++++++-
drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 7 ++++++-
include/linux/netdevice.h | 2 +-
net/core/dev.c | 18 +++++++-----------
net/netfilter/nf_flow_table_path.c | 7 ++++++-
5 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 33ddf0d07855..53183a132100 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -283,14 +283,19 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,
struct airoha_wdma_info *info)
{
struct net_device_path_stack stack;
+ struct net_device_path_ctx ctx = {
+ .dev = dev,
+ };
struct net_device_path *path;
int err;
if (!dev)
return -ENODEV;
+ ether_addr_copy(ctx.daddr, addr);
+
rcu_read_lock();
- err = dev_fill_forward_path(dev, addr, &stack);
+ err = dev_fill_forward_path(&ctx, &stack);
rcu_read_unlock();
if (err)
return err;
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
index cc8c4ef8038f..1ec46784f018 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -92,6 +92,9 @@ static int
mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
{
struct net_device_path_stack stack;
+ struct net_device_path_ctx ctx = {
+ .dev = dev,
+ };
struct net_device_path *path;
int err;
@@ -101,8 +104,10 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
return -1;
+ ether_addr_copy(ctx.daddr, addr);
+
rcu_read_lock();
- err = dev_fill_forward_path(dev, addr, &stack);
+ err = dev_fill_forward_path(&ctx, &stack);
rcu_read_unlock();
if (err)
return err;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 8db25b79573e..d2f545016f7f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3425,7 +3425,7 @@ void dev_remove_offload(struct packet_offload *po);
int dev_get_iflink(const struct net_device *dev);
int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+int dev_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path_stack *stack);
struct net_device *dev_get_by_name(struct net *net, const char *name);
struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
diff --git a/net/core/dev.c b/net/core/dev.c
index c1c1be1a6962..e2c9fe3f7a7b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -750,41 +750,37 @@ static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
return &stack->path[k];
}
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+int dev_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path_stack *stack)
{
const struct net_device *last_dev;
- struct net_device_path_ctx ctx = {
- .dev = dev,
- };
struct net_device_path *path;
int ret = 0;
- memcpy(ctx.daddr, daddr, sizeof(ctx.daddr));
stack->num_paths = 0;
- while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
- last_dev = ctx.dev;
+ while (ctx->dev && ctx->dev->netdev_ops->ndo_fill_forward_path) {
+ last_dev = ctx->dev;
path = dev_fwd_path(stack);
if (!path)
return -1;
memset(path, 0, sizeof(struct net_device_path));
- ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
+ ret = ctx->dev->netdev_ops->ndo_fill_forward_path(ctx, path);
if (ret < 0)
return -1;
- if (WARN_ON_ONCE(last_dev == ctx.dev))
+ if (WARN_ON_ONCE(last_dev == ctx->dev))
return -1;
}
- if (!ctx.dev)
+ if (!ctx->dev)
return ret;
path = dev_fwd_path(stack);
if (!path)
return -1;
path->type = DEV_PATH_ETHERNET;
- path->dev = ctx.dev;
+ path->dev = ctx->dev;
return ret;
}
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index 98c03b487f52..b9df7453f762 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -50,6 +50,9 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
{
const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
struct net_device *dev = dst_cache->dev;
+ struct net_device_path_ctx ctx = {
+ .dev = dev,
+ };
struct neighbour *n;
u8 nud_state;
@@ -72,7 +75,9 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
return -1;
out:
- return dev_fill_forward_path(dev, ha, stack);
+ ether_addr_copy(ctx.daddr, ha);
+
+ return dev_fill_forward_path(&ctx, stack);
}
struct nft_forward_info {
---
base-commit: a23b36233d4103def55dc8cf65698106d0bd1e62
change-id: 20260805-dev_fill_forward_path-ctx-arg-73366345e3b8
Best regards,
--
Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
^ permalink raw reply related
* Re: [PATCH net] mptcp: hold msk reference when cloning request sockets
From: Yuan Tan @ 2026-08-05 8:52 UTC (permalink / raw)
To: Matthieu Baerts, Kyle Zeng, David Lee
Cc: Dominik 'Disconnect3d' Czarnota, geliang, horms, kuniyu,
netdev, mptcp, linux-kernel, stable, Ren Wei, yifanwucs,
tomapufckgml, bird, caoruide123, enjou1224z, Vega, martineau,
davem, edumazet, kuba, pabeni, ncardwell
In-Reply-To: <ec043cd9-ffc5-4a16-a80e-b736ea50bf3f@kernel.org>
On Tue, Aug 4, 2026 at 11:00 AM Matthieu Baerts <matttbe@kernel.org> wrote:
>
> Hi David, Kyle,
>
> (+cc Vega's people)
>
> On 04/08/2026 11:50, David Lee wrote:
> > From: Kyle Zeng <kylebot@openai.com>
> >
> > An MP_JOIN request owns the reference stored in subflow_req->msk.
> > inet_reqsk_clone() byte-copies that pointer when migrating a request,
> > but does not acquire a reference for the clone. The original and cloned
> > request destructors can consequently drop the same reference, leaving
> > one request with a dangling msk pointer.
> >
> > Let cloned MPTCP requests take their own msk reference. The source
> > request still owns its reference while it is being cloned, so sock_hold()
> > is safe. The clone's normal destructor balances the new reference on
> > both successful and failed migration paths.
> >
> > Fixes: c905dee62232 ("tcp: Migrate TCP_NEW_SYN_RECV requests at retransmitting SYN+ACKs.")
>
> Thank you for this patch. It looks like it is similar to this one sent a
> few months ago, but where changes have been requested:
>
> https://lore.kernel.org/40fd38e7a368e5b7bc9bc83364a32241f977d53f.1778404619.git.caoruide123@gmail.com
>
> Do you mind checking what they did, and explaining the different approach, please?
>
> It seems there are two issues the Vega's team tried to solve: one with
> MP_JOIN requests (what you are trying to fix here) and with MP_CAPABLE.
> For me, it is fine to split that in two patches. I also don't mind who
> is writing the final patch(es), as long as credits are given. If you,
> David/Kyle, are doing that, it might be OK to add a:
>
> Reported-by: Vega <vega@nebusec.ai>
> Closes: https://lore.kernel.org/40fd38e7a368e5b7bc9bc83364a32241f977d53f.1778404619.git.caoruide123@gmail.com
>
> @Vega's team: OK with that? Or are you actively working on a v3?
We seem to have lost track of this patch, and we sincerely apologize.
Our patch addresses two issues: one involving MP_JOIN requests and the
other involving MP_CAPABLE.
I reviewed Kyle and David’s fix for the MP_JOIN issue, and it looks
reasonable to me.
Ruide and I also do not have a preference regarding whose patch is accepted.
If David/Kyle' patch is accepted, please add:
Reported-by: Vega <vega@nebusec.ai>
Reported-by: Ruide Cao <caoruide123@gmail.com>
Closes: https://lore.kernel.org/40fd38e7a368e5b7bc9bc83364a32241f977d53f.1778404619.git.caoruide123@gmail.com
Btw, Kyle and David, would you be open to collaborating with us on
Linux kernel patches? Over the past few months, our team has fixed
more than 100 high-severity vulnerabilities in the kernel. We’ve
learned a great deal from maintainers’ feedback and have built up
extensive experience. We’d love to help improve kernel security while
minimizing the burden on maintainers as much as possible.
>
> One last thing: it looks like your patch is conflicting with this one:
>
> https://lore.kernel.org/netdev/20260803061739.134737-1-jiayuan.chen@linux.dev/
>
> > Cc: stable@vger.kernel.org
> > Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber
> > Signed-off-by: Kyle Zeng <kylebot@openai.com>
> > Co-developed-by: David Lee <david.lee@trailofbits.com>
> > Signed-off-by: David Lee <david.lee@trailofbits.com>
> > ---
> > Bug found and triaged by OpenAI Security Research and
> > validated by Trail of Bits.
> >
> > Trail of Bits has a reproducer for this bug that triggers a
> > KASAN use-after-free and can share if needed.
>
> I think it would be good to include the (decoded) KASAN warning in the
> commit message. Regarding the reproducer, if it is with packetdrill, I
> think it is always useful to share it. For others, don't hesitate to
> share it to the same people: it can help better understanding the issue.
> Maybe remove the mailing list when doing that if it is security related?
>
> Cheers,
> Matt
> --
> Sponsored by the NGI0 Core fund.
>
^ permalink raw reply
* Re: [syzbot] [net?] WARNING in netdev_queue_get_dma_dev
From: Pavel Begunkov @ 2026-08-05 8:51 UTC (permalink / raw)
To: Jakub Kicinski
Cc: syzbot, davem, edumazet, horms, linux-kernel, netdev, pabeni,
syzkaller-bugs
In-Reply-To: <20260804140006.0210f6ca@kernel.org>
On 8/4/26 22:00, Jakub Kicinski wrote:
> Hi Pavel, could you TAL?
It complains on not holding rtnl, so the netdev should be not
qops-enabled, and we acquire it with netdev_get_by_index_lock().
It's going to be rejected later, but not sure whether netdev->lock
protects its device and leasing logic well in this case.
As a quick fix, let's fail it early in zcrx if there is no qops.
> On Tue, 04 Aug 2026 07:32:44 -0700 syzbot wrote:
>> Hello,
>>
>> syzbot found the following issue on:
>>
>> HEAD commit: 075b74841bd0 Linux 7.2-rc6
>> git tree: upstream
>> console+strace: https://syzkaller.appspot.com/x/log.txt?x=176a7db9580000
>> kernel config: https://syzkaller.appspot.com/x/.config?x=145fa60d73086782
>> dashboard link: https://syzkaller.appspot.com/bug?extid=a78926bdac2adb52dc0e
>> compiler: gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
>> syz repro: https://syzkaller.appspot.com/x/repro.syz?x=16225db9580000
>> C reproducer: https://syzkaller.appspot.com/x/repro.c?x=17bade32580000
>>
>> Downloadable assets:
>> disk image: https://storage.googleapis.com/syzbot-assets/1dc04730d019/disk-075b7484.raw.xz
>> vmlinux: https://storage.googleapis.com/syzbot-assets/d76da42271f4/vmlinux-075b7484.xz
>> kernel image: https://storage.googleapis.com/syzbot-assets/07b1cdbcbe45/bzImage-075b7484.xz
>>
>> IMPORTANT: if you fix the issue, please add the following tag to the commit:
>> Reported-by: syzbot+a78926bdac2adb52dc0e@syzkaller.appspotmail.com
>>
>> ------------[ cut here ]------------
>> RTNL: assertion failed at ./include/net/netdev_lock.h (72)
>> WARNING: ./include/net/netdev_lock.h:72 at netdev_assert_locked_ops_compat include/net/netdev_lock.h:72 [inline], CPU#1: syz-executor223/5620
>> WARNING: ./include/net/netdev_lock.h:72 at netdev_queue_get_dma_dev+0x52e/0x660 net/core/netdev_queues.c:43, CPU#1: syz-executor223/5620
>> Modules linked in:
>> CPU: 1 UID: 0 PID: 5620 Comm: syz-executor223 Not tainted syzkaller #0 PREEMPT(full)
>> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/2026
>> RIP: 0010:netdev_assert_locked_ops_compat include/net/netdev_lock.h:72 [inline]
>> RIP: 0010:netdev_queue_get_dma_dev+0x53a/0x660 net/core/netdev_queues.c:43
>> Code: 89 c5 89 c6 e8 27 f7 68 f8 45 85 ed 0f 85 be fb ff ff e8 a9 fc 68 f8 48 8d 3d 12 0e 90 07 ba 48 00 00 00 48 c7 c6 a0 88 18 8d <67> 48 0f b9 3a e9 9c fb ff ff e8 87 fc 68 f8 48 8d bd 00 08 00 00
>> RSP: 0018:ffffc90003b47a80 EFLAGS: 00010293
>> RAX: 0000000000000000 RBX: ffff888029874000 RCX: ffffffff89a16569
>> RDX: 0000000000000048 RSI: ffffffff8d1888a0 RDI: ffffffff91317390
>> RBP: 0000000000000000 R08: 0000000000000005 R09: 0000000000000000
>> R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000
>> R13: 0000000000000000 R14: ffff88807b3f2568 R15: ffffc90003b47be0
>> FS: 00005555885ac400(0000) GS:ffff888123ed8000(0000) knlGS:0000000000000000
>> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>> CR2: 0000200000001000 CR3: 000000006f8b6000 CR4: 00000000003526f0
>> Call Trace:
>> <TASK>
>> zcrx_register_netdev io_uring/zcrx.c:831 [inline]
>> io_register_zcrx+0xe94/0x1d30 io_uring/zcrx.c:983
>> __io_uring_register io_uring/register.c:942 [inline]
>> __do_sys_io_uring_register+0x1193/0x1bc0 io_uring/register.c:1040
>> do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
>> do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
>> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>> RIP: 0033:0x7fd0b54bbcb9
>> Code: c0 79 93 eb d5 48 8d 7c 1d 00 eb 99 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 d8 ff ff ff f7 d8 64 89 01 48
>> RSP: 002b:00007ffe25f0a7f8 EFLAGS: 00000246 ORIG_RAX: 00000000000001ab
>> RAX: ffffffffffffffda RBX: 00007ffe25f0a928 RCX: 00007fd0b54bbcb9
>> RDX: 0000200000000400 RSI: 0000000000000020 RDI: 0000000000000003
>> RBP: 0000000000000001 R08: 0000000000000000 R09: 00007ffe25f0a928
>> R10: 0000000000000001 R11: 0000000000000246 R12: 00007ffe25f0a918
>> R13: 0000000000000002 R14: 00007fd0b554cca0 R15: 0000000000000002
>> </TASK>
>> ----------------
>> Code disassembly (best guess):
>> 0: 89 c5 mov %eax,%ebp
>> 2: 89 c6 mov %eax,%esi
>> 4: e8 27 f7 68 f8 call 0xf868f730
>> 9: 45 85 ed test %r13d,%r13d
>> c: 0f 85 be fb ff ff jne 0xfffffbd0
>> 12: e8 a9 fc 68 f8 call 0xf868fcc0
>> 17: 48 8d 3d 12 0e 90 07 lea 0x7900e12(%rip),%rdi # 0x7900e30
>> 1e: ba 48 00 00 00 mov $0x48,%edx
>> 23: 48 c7 c6 a0 88 18 8d mov $0xffffffff8d1888a0,%rsi
>> * 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
>> 2f: e9 9c fb ff ff jmp 0xfffffbd0
>> 34: e8 87 fc 68 f8 call 0xf868fcc0
>> 39: 48 8d bd 00 08 00 00 lea 0x800(%rbp),%rdi
>>
>>
>> ---
>> This report is generated by a bot. It may contain errors.
>> See https://goo.gl/tpsmEJ for more information about syzbot.
>> syzbot engineers can be reached at syzkaller@googlegroups.com.
>>
>> syzbot will keep track of this issue. See:
>> https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
>>
>> If the report is already addressed, let syzbot know by replying with:
>> #syz fix: exact-commit-title
>>
>> If you want syzbot to run the reproducer, reply with:
>> #syz test: git://repo/address.git branch-or-commit-hash
>> If you attach or paste a git patch, syzbot will apply it before testing.
>>
>> If you want to overwrite report's subsystems, reply with:
>> #syz set subsystems: new-subsystem
>> (See the list of subsystem names on the web dashboard)
>>
>> If the report is a duplicate of another one, reply with:
>> #syz dup: exact-subject-of-another-report
>>
>> If you want to undo deduplication, reply with:
>> #syz undup
>
--
Pavel Begunkov
^ permalink raw reply
* [PATCH net v2] net: tun: bound receive headroom
From: Asim Viladi Oglu Manizada @ 2026-08-05 8:45 UTC (permalink / raw)
To: netdev
Cc: Willem de Bruijn, Jason Wang, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
tun_get_user() uses tun->align both as skb headroom and when choosing how
much packet data to keep linear. OVS can propagate an oversized headroom
request from another port to TUN or TAP.
When align is larger than the usable space in a one-page skb head,
SKB_MAX_HEAD(align) underflows and the result becomes negative when stored
in good_linear. That value later wraps when assigned to the size_t linear
variable, and tun_alloc_skb() can place skb->data outside the allocated
head.
Bound the headroom stored by TUN to the one-page skb-head budget and the
largest non-sentinel 16-bit skb header offset. Leave one linear byte for
raw TUN and a complete Ethernet header for TAP, including NET_IP_ALIGN.
Also pull the raw-TUN protocol byte and the TAP Ethernet header before
accessing them, so these checks remain safe for nonlinear skbs supplied by
other allocation paths.
Fixes: eaea34b23c46 ("net/tun: implement ndo_set_rx_headroom")
Cc: stable@vger.kernel.org
Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix
Signed-off-by: Asim Viladi Oglu Manizada <manizada@pm.me>
---
v2:
- bound tun->align instead of clamping good_linear to zero
- derive the bound from the one-page head, 16-bit offset, and TUN/TAP
linear-header requirements
- pull the raw-TUN protocol byte before reading it
- make the TAP Ethernet-header pull unconditional
v1: https://lore.kernel.org/netdev/20260721014117.2234892-1-manizada@pm.me/
drivers/net/tun.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index fed9dfdfcc3b..efd2e7d75c9a 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1107,11 +1107,16 @@ static netdev_features_t tun_net_fix_features(struct net_device *dev,
static void tun_set_headroom(struct net_device *dev, int new_hr)
{
struct tun_struct *tun = netdev_priv(dev);
+ size_t max_headroom;
- if (new_hr < NET_SKB_PAD)
- new_hr = NET_SKB_PAD;
+ max_headroom = min_t(size_t, SKB_MAX_HEAD(0), U16_MAX - 1);
- tun->align = new_hr;
+ if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP)
+ max_headroom -= ETH_HLEN + NET_IP_ALIGN;
+ else
+ max_headroom -= 1;
+
+ tun->align = clamp_t(int, new_hr, NET_SKB_PAD, max_headroom);
}
static void
@@ -1822,7 +1827,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
switch (tun->flags & TUN_TYPE_MASK) {
case IFF_TUN:
if (tun->flags & IFF_NO_PI) {
- u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0;
+ u8 ip_version;
+
+ if (skb->len && !pskb_may_pull(skb, 1)) {
+ err = -ENOMEM;
+ goto drop;
+ }
+ ip_version = skb->len ? (skb->data[0] >> 4) : 0;
switch (ip_version) {
case 4:
@@ -1842,7 +1853,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
skb->dev = tun->dev;
break;
case IFF_TAP:
- if (frags && !pskb_may_pull(skb, ETH_HLEN)) {
+ if (!pskb_may_pull(skb, ETH_HLEN)) {
err = -ENOMEM;
drop_reason = SKB_DROP_REASON_HDR_TRUNC;
goto drop;
--
2.53.0
^ permalink raw reply related
* RE: [Intel-wired-lan] [PATCH iwl-net] ice: clear the default forwarding VSI rule when releasing a VSI
From: Romanowski, Rafal @ 2026-08-05 8:41 UTC (permalink / raw)
To: Oros, Petr, Simon Horman
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, Keller, Jacob E,
michal.swiatkowski@linux.intel.com,
intel-wired-lan@lists.osuosl.org, linux-kernel@vger.kernel.org
In-Reply-To: <987aa5d0-9ca8-4038-b77f-94adba497e40@redhat.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Petr
> Oros
> Sent: Tuesday, June 23, 2026 12:58 PM
> To: Simon Horman <horms@kernel.org>
> Cc: netdev@vger.kernel.org; Nguyen, Anthony L <anthony.l.nguyen@intel.com>;
> Kitszel, Przemyslaw <przemyslaw.kitszel@intel.com>; andrew+netdev@lunn.ch;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; Keller, Jacob E <jacob.e.keller@intel.com>;
> michal.swiatkowski@linux.intel.com; intel-wired-lan@lists.osuosl.org; linux-
> kernel@vger.kernel.org
> Subject: Re: [Intel-wired-lan] [PATCH iwl-net] ice: clear the default forwarding VSI
> rule when releasing a VSI
>
>
> On 6/23/26 12:29, Simon Horman wrote:
> > This is an AI-generated review of your patch. The human sending this
> > email has considered the AI review valid, or at least plausible.
> > Full review at: https://sashiko.dev
> > ---
> > ice: clear the default forwarding VSI rule when releasing a VSI
> >
> > This patch restores the cleanup of the default forwarding VSI rule in
> > ice_vsi_release(), clearing it when the VSI owns it. This prevents a
> > rule leak if a trusted VF enters promiscuous mode and is then destroyed.
> >
> >> diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c
> >> b/drivers/net/ethernet/intel/ice/ice_lib.c
> >> --- a/drivers/net/ethernet/intel/ice/ice_lib.c
> >> +++ b/drivers/net/ethernet/intel/ice/ice_lib.c
> >> @@ -2871,6 +2871,9 @@ int ice_vsi_release(struct ice_vsi *vsi)
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
^ permalink raw reply
* Re: [PATCH net v2] net: ravb: fix use-after-free in ravb_get_ts_info
From: Niklas Söderlund @ 2026-08-05 8:40 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Xuanqiang Luo, linux-renesas-soc, netdev, paul, andrew+netdev,
davem, edumazet, pabeni, richardcochran, masaru.nagai.vx,
sergei.shtylyov, Xuanqiang Luo, stable
In-Reply-To: <20260804180855.75f6c91c@kernel.org>
On 2026-08-04 18:08:55 -0700, Jakub Kicinski wrote:
> On Sun, 2 Aug 2026 14:17:27 +0200 Niklas Söderlund wrote:
> > > The PHC is registered by ravb_open() and unregistered by ravb_close().
> > > However, ravb_ptp_stop() leaves priv->ptp.clock pointing at the freed
> > > clock. Since the netdev remains registered after ndo_stop, get_ts_info
> > > can still pass the dangling pointer to ptp_clock_index(), resulting in a
> > > use-after-free.
> > >
> > > Clear the pointer after unregistering the clock and only advertise hardware
> > > timestamping support when a PHC is available.
> > >
> > > Fixes: a0d2f20650e8 ("Renesas Ethernet AVB PTP clock driver")
> > > Cc: stable@vger.kernel.org
> > > Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
> >
> > Thanks for re spinning this,
>
> The AI reivewer has more comments, but at high level - why unregister
> the PHC when the device goes down? The clocks stop?
Yes, PTP clock is in the same power domain as the ndev. When the device
go down runtime PM suspends the device so the clock also stops.
--
Kind Regards,
Niklas Söderlund
^ permalink raw reply
* Re: [PATCH net v2] net: ravb: fix use-after-free in ravb_get_ts_info
From: Niklas Söderlund @ 2026-08-05 8:36 UTC (permalink / raw)
To: Jakub Kicinski
Cc: xuanqiang.luo, linux-renesas-soc, netdev, paul, andrew+netdev,
davem, edumazet, pabeni, richardcochran, masaru.nagai.vx,
sergei.shtylyov, luoxuanqiang, stable
In-Reply-To: <20260805010620.4093075-1-kuba@kernel.org>
Hello,
Some of the issues pointed out in this review are addressed in the RAVB
PTP rework series [1]. I will respin it as soon as the PTP clock for
R-Car Gen4 is merged [2].
1. https://lore.kernel.org/all/20260610102432.3538432-1-niklas.soderlund+renesas@ragnatech.se/
2. https://lore.kernel.org/all/20260803095232.3527111-1-niklas.soderlund+renesas@ragnatech.se/
On 2026-08-04 18:06:20 -0700, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> net: ravb: fix use-after-free in ravb_get_ts_info
>
> This clears priv->ptp.clock in ravb_ptp_stop() after unregistering the PHC,
> so that ravb_get_ts_info() no longer hands a dangling pointer to
> ptp_clock_index(). It also gates the whole hardware timestamping capability
> block in ravb_get_ts_info() on priv->ptp.clock being set.
>
> A few questions on the changelog and on the new guards.
>
> > The PHC is registered by ravb_open() and unregistered by ravb_close().
> > However, ravb_ptp_stop() leaves priv->ptp.clock pointing at the freed
> > clock. Since the netdev remains registered after ndo_stop, get_ts_info
> > can still pass the dangling pointer to ptp_clock_index(), resulting in a
> > use-after-free.
>
> [Severity: Low]
> Should the changelog also mention the plain NULL dereference case?
> ravb_ptp_init() is only reached from ravb_open(), ravb_wol_restore() /
> ravb_resume(), ravb_set_ringparam() and ravb_tx_timeout_work(), never from
> ravb_probe().
>
> So between register_netdev() and the first open, priv->ptp.clock is NULL,
> and pre-patch ravb_get_ts_info() passed it directly to ptp_clock_index(),
> which is just:
>
> drivers/ptp/ptp_clock.c:ptp_clock_index() {
> return ptp->index;
> }
>
> That means ethtool -T on a gptp/ccc_gac ravb interface that has never been
> brought up faulted too. Since this carries Cc: stable, would it help
> backport triage to state that the most easily reachable manifestation is a
> NULL dereference in the never-opened state, not only a dangling pointer read
> after close?
>
> > diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
> > index 5f88733094d0f..f22cff05ba35d 100644
> > --- a/drivers/net/ethernet/renesas/ravb_main.c
> > +++ b/drivers/net/ethernet/renesas/ravb_main.c
> > @@ -1768,7 +1768,7 @@ static int ravb_get_ts_info(struct net_device *ndev,
> > struct ravb_private *priv = netdev_priv(ndev);
> > const struct ravb_hw_info *hw_info = priv->info;
> >
> > - if (hw_info->gptp || hw_info->ccc_gac) {
> > + if ((hw_info->gptp || hw_info->ccc_gac) && priv->ptp.clock) {
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does a NULL-only test cover
> the failure mode that actually puts an invalid pointer in priv->ptp.clock?
> ravb_ptp_init() stores the return value unchecked:
>
> drivers/net/ethernet/renesas/ravb_ptp.c:ravb_ptp_init() {
> ...
> priv->ptp.clock = ptp_clock_register(&priv->ptp.info, &pdev->dev);
> }
>
> With CONFIG_PTP_1588_CLOCK=y, ptp_clock_register() returns ERR_PTR(-EINVAL),
> ERR_PTR(-ENOMEM) or ERR_PTR(err) on failure, so priv->ptp.clock can be a
> non-NULL error pointer. An error pointer passes the new test here and then
> reaches ptp_clock_index(), which is a bare return ptp->index, and it also
> passes the new test in ravb_ptp_stop() and reaches ptp_clock_unregister(),
> which dereferences it right away via ptp_vclock_in_use(ptp) and
> get_device(&ptp->dev).
>
> Would !IS_ERR_OR_NULL(priv->ptp.clock) be the right guard, plus having
> ravb_ptp_init() normalize a failed registration to NULL? The sibling driver
> does check: rcar_gen4_ptp_register() tests IS_ERR() and propagates
> PTR_ERR().
>
> [Severity: Medium]
> Is suppressing the entire block, rather than just phc_index, the intended
> behaviour change? With no PHC registered, ethtool -T now reports
> tx_types = 0, rx_filters = 0 and loses SOF_TIMESTAMPING_TX_SOFTWARE, since
> __ethtool_get_ts_info() only ORs back:
>
> net/ethtool/common.c:__ethtool_get_ts_info() {
> ...
> info->so_timestamping |= SOF_TIMESTAMPING_RX_SOFTWARE |
> SOF_TIMESTAMPING_SOFTWARE;
> }
>
> Meanwhile the peer paths in the same driver still implement all of it:
> ravb_hwtstamp_set() still accepts HWTSTAMP_TX_ON and
> HWTSTAMP_FILTER_PTP_V2_L2_EVENT (and silently promotes unknown filters to
> HWTSTAMP_FILTER_ALL), ravb_start_xmit() still calls skb_tx_timestamp(), and
> the receive path still fills skb_hwtstamps().
>
> There is a second effect on CONFIG_PTP_1588_CLOCK=n builds. CONFIG_RAVB
> depends on PTP_1588_CLOCK_OPTIONAL and ravb_ptp.o is linked
> unconditionally, and the stub is:
>
> include/linux/ptp_clock_kernel.h {
> static inline struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
> struct device *parent)
> { return NULL; }
> }
>
> so priv->ptp.clock is permanently NULL there and this block becomes dead
> code, even though the timestamping registers are still programmed and the
> hwtstamp ioctl still works. Before the patch those builds reported the
> hardware filters with phc_index == -1.
>
> Also, ethtool -T is generally read as static device capability, but it now
> changes with interface up/down because the PHC lifetime follows
> ravb_open()/ravb_close(). Would keeping the capability bits and only
> computing info->phc_index when a PHC exists be a narrower fix?
>
> > info->so_timestamping =
> > SOF_TIMESTAMPING_TX_SOFTWARE |
> > SOF_TIMESTAMPING_TX_HARDWARE |
>
> [ ... ]
>
> > diff --git a/drivers/net/ethernet/renesas/ravb_ptp.c b/drivers/net/ethernet/renesas/ravb_ptp.c
> > index 226c6c0ab945b..fc8c601ed3a5e 100644
> > --- a/drivers/net/ethernet/renesas/ravb_ptp.c
> > +++ b/drivers/net/ethernet/renesas/ravb_ptp.c
> > @@ -337,5 +337,8 @@ void ravb_ptp_stop(struct net_device *ndev)
> > ravb_write(ndev, 0, GIC);
> > ravb_write(ndev, 0, GIS);
> >
> > - ptp_clock_unregister(priv->ptp.clock);
> > + if (priv->ptp.clock) {
> > + ptp_clock_unregister(priv->ptp.clock);
> > + priv->ptp.clock = NULL;
> > + }
> > }
>
> [Severity: Medium]
> Can this new NULL store race the interrupt handler? ravb_ptp_interrupt()
> reads the same field in hardirq context and passes it on without a NULL
> check:
>
> drivers/net/ethernet/renesas/ravb_ptp.c:ravb_ptp_interrupt() {
> u32 gis = ravb_read(ndev, GIS);
>
> gis &= ravb_read(ndev, GIC);
> if (gis & GIS_PTCF) {
> ...
> event.timestamp = ravb_read(ndev, GCPT);
> ptp_clock_event(priv->ptp.clock, &event);
> }
> ...
> }
>
> The locksets do not intersect: ravb_ptp_interrupt() runs from
> ravb_interrupt()/ravb_multi_interrupt() under spin_lock(&priv->lock), while
> ravb_ptp_stop() runs in process context and takes no lock. The irqs are
> installed with devm_request_irq() at probe and stay installed while the
> interface is down, and ravb_close() calls ravb_ptp_stop() before
> ravb_stop_dma() and before napi_disable().
>
> Writing GIC = 0 first does not appear to close the window, because the
> handler's guard gis &= ravb_read(ndev, GIC) may already have been sampled:
>
> CPU0 (hardirq)
> reads GIS (GIS_PTCF set), reads GIC (GIC_PTCE set)
> ravb_read(ndev, GCPT) <-- slow MMIO read
>
> CPU1 (ravb_close)
> ravb_write(ndev, 0, GIC);
> ptp_clock_unregister(priv->ptp.clock);
> priv->ptp.clock = NULL;
>
> CPU0 (hardirq, continues)
> ptp_clock_event(NULL, &event); <-- spin_lock_irqsave(&ptp->tsevqs_lock)
>
> Would masking GIC, then serializing with the handler (clearing the pointer
> under spin_lock_irqsave(&priv->lock), or synchronize_irq() on the
> DMAC/gPTP irq), and only then calling the sleeping ptp_clock_unregister()
> on a local copy be safer? Should ravb_ptp_interrupt() also tolerate a NULL
> clock now that NULL is a reachable state while the irq is live?
>
> [Severity: Medium]
> Does clearing the pointer only after ptp_clock_unregister() returns leave
> part of the use-after-free in place? ravb_get_ts_info() does two separate
> loads of priv->ptp.clock, one for the new test and one for
> ptp_clock_index(), and there is no lock shared with this writer.
>
> The assumption that RTNL serializes the two sides does not seem to hold.
> __ethtool_get_ts_info() calls ops->get_ts_info() under nothing stronger
> than rcu_read_lock(), and it is reachable from setsockopt(SO_TIMESTAMPING,
> SOF_TIMESTAMPING_BIND_PHC):
>
> net/core/sock.c:sock_timestamping_bind_phc() {
> ...
> num = ethtool_get_phc_vclocks(dev, &vclock_index);
> dev_put(dev);
> ...
> }
>
> which holds only a dev_get_by_index() reference, no rtnl_lock(). So:
>
> CPU0 (setsockopt) CPU1 (ip link set down)
> ravb_get_ts_info()
> priv->ptp.clock != NULL ravb_ptp_stop()
> ptp_clock_unregister(...)
> priv->ptp.clock = NULL;
> ptp_clock_index(priv->ptp.clock)
> return ptp->index; <-- NULL
>
> And during the window where the pointer is still non-NULL but
> ptp_clock_unregister() is sleeping in the device_del()/put_device() path, a
> reader can still use a clock that is being torn down; ptp_clock_release()
> ends in a plain kfree(ptp) with no RCU deferral and the reader holds no
> kref.
>
> Would loading and clearing the pointer once under a lock shared with the
> reader (for example priv->lock), and clearing it before the unregister,
> close both of these?
> --
> pw-bot: cr
--
Kind Regards,
Niklas Söderlund
^ permalink raw reply
* Re: [PATCH v4] vsock: use sock_error() to consume sk_err after a failed connect
From: Stefano Garzarella @ 2026-08-05 8:34 UTC (permalink / raw)
To: phind.uet
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Andy King, George Zhang, Dmitry Torokhov,
syzbot+1b2c9c4a0f8708082678, Michal Luczaj, Wupeng Ma,
virtualization, netdev, linux-kernel
In-Reply-To: <20260804135238.386417-1-phind.uet@gmail.com>
On Tue, Aug 04, 2026 at 09:52:36PM +0800, phind.uet@gmail.com wrote:
>From: Nguyen Dinh Phi <phind.uet@gmail.com>
>
>Syzbot reported an issue which can be reproduced with these steps:
>
> r0 = socket(AF_VSOCK, SOCK_STREAM, 0)
> bind(r0, {VMADDR_CID_ANY, PORT})
> connect(r0, {VMADDR_CID_LOCAL, PORT}) -> -1, EPROTO (self-connect)
> listen(r0, backlog) -> 0
> r1 = socket(AF_VSOCK, SOCK_STREAM, 0)
> connect(r1, {VMADDR_CID_LOCAL, PORT}) -> 0
> accept(r0) -> -1, EPROTO (stale sk_err)
>
>Basically, it creates a socket (r0) and triggers a self-connect after
>binding it. This self-connect fails with EPROTO because it loops back to
>r0 while the socket is still in the TCP_SYN_SENT state, causing it to be
>incorrectly dispatched to the connecting-client path. The unexpected
>packet type encountered there sets sk_err to EPROTO.
>
>After that, it invokes a listen() call on the same socket. This listen()
>call succeeds because the kernel's listening path never inspects or
>clears sk_err. Then, a new socket (r1) is created as a normal client and
>connects to r0. However, vsock_accept() rejects this incoming connection
>because the listener's sk_err still holds the EPROTO error from the
>earlier failed self-connect.
>
>This rejection causes the child socket created for r1's connection to
>never be freed on virtio or hyperv transports; only the VMCI transport
>implements pending_work to revisit and clean up a rejected socket.
>
>Fix the issue in blocking connect() by using sock_error() to read the
>sk_err to prevent the rejection branch from occurring in this scenario.
>
>sock_error() atomically reads and clears sk_err, ensuring the error is
>consumed when vsock_connect() returns and cannot affect subsequent
>operations on the same socket. This matches the established pattern
>used by other protocol connect() implementations in the network
>stack like __inet_stream_connect(), tipc_wait_for_connect()...
>
>For non-blocking connection, vsock_connect_timeout() may set
>sk->sk_err after vsock_connect() has returned. To handle it, we also
>remove the sk_err checks from vsock_accept(). Nothing in vsock sets
>sk_err on a listening socket, so accept() has no reason to inspect it
>at all.
>
>Reported-by: syzbot+1b2c9c4a0f8708082678@syzkaller.appspotmail.com
>Closes: https://syzkaller.appspot.com/bug?extid=1b2c9c4a0f8708082678
>Fixes: d021c344051af ("VSOCK: Introduce VM Sockets")
>Suggested-by: Michal Luczaj <mhal@rbox.co>
>Signed-off-by: Nguyen Dinh Phi <phind.uet@gmail.com>
>Tested-by: Wupeng Ma <mawupeng1@huawei.com>
>---
>V2: Add reproducer steps to commit message.
>V3: Fix truncated title and add annotations to reproducer steps.
>V4: Remove sk_err checks from vsock_accept()
>
> net/vmw_vsock/af_vsock.c | 13 ++++---------
> 1 file changed, 4 insertions(+), 9 deletions(-)
>
>diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
>index 622dbd046799..594fe27d2ebe 100644
>--- a/net/vmw_vsock/af_vsock.c
>+++ b/net/vmw_vsock/af_vsock.c
>@@ -1847,12 +1847,10 @@ static int vsock_connect(struct socket *sock, struct sockaddr_unsized *addr,
> prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
> }
>
>- if (sk->sk_err) {
>- err = -sk->sk_err;
>+ err = sock_error(sk);
>+ if (err) {
> sk->sk_state = TCP_CLOSE;
> sock->state = SS_UNCONNECTED;
>- } else {
>- err = 0;
> }
>
> out_wait:
>@@ -1893,7 +1891,7 @@ static int vsock_accept(struct socket *sock, struct socket *newsock,
> timeout = sock_rcvtimeo(listener, arg->flags & O_NONBLOCK);
>
> while ((connected = vsock_dequeue_accept(listener)) == NULL &&
>- listener->sk_err == 0 && timeout != 0) {
>+ timeout != 0) {
> prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE);
> release_sock(listener);
> timeout = schedule_timeout(timeout);
>@@ -1906,11 +1904,8 @@ static int vsock_accept(struct socket *sock, struct socket *newsock,
> }
> }
>
>- if (listener->sk_err) {
>- err = -listener->sk_err;
>- } else if (!connected) {
>+ if (!connected)
> err = -EAGAIN;
>- }
>
> if (connected) {
Can this become an `} else {` ?
Or just add a `goto out` when setting `err = -EAGAIN`.
> sk_acceptq_removed(listener);
lock_sock_nested(connected, SINGLE_DEPTH_NESTING);
vconnected = vsock_sk(connected);
/* If the listener socket has received an error, then we should
* reject this socket and return. Note that we simply mark the
* socket rejected, drop our reference, and let the cleanup
* function handle the cleanup; the fact that we found it in
* the listener's accept queue guarantees that the cleanup
* function hasn't run yet.
*/
if (err) {
vconnected->rejected = true;
} else {
Should we update this comment too and maybe remove the `if (err)` at
all. With that change I guess `rejected` is never set at the end and
maybe we can remove it at all from `struct vsock_sock`.
Looking at commit d021c344051a ("VSOCK: Introduce VM Sockets") where
`rejected` was introduced, I can't see any path where sk_err is set on
a listener socket, so I guess that path was dead since the beginning.
So now I'm thinking if it's better to split in 2 patches (both with the
same Fixes tag):
- Patch 1: "vsock: remove stale sk_err checks from vsock_accept()"
Where we can also remove `rejected` since it's never set to true since
the beginning
- Patch 2: "vsock: use sock_error() to consume sk_err after a failed
connect"
WDYT?
Thanks,
Stefano
^ permalink raw reply
* [PATCH v10] ptp: ocp: add CPLD ISP support for ADVA TimeCard X1
From: Sagi Maimon @ 2026-08-05 8:31 UTC (permalink / raw)
To: jonathan.lemon, vadim.fedorenko, richardcochran, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: linux-kernel, netdev, Sagi Maimon
The ADVA TimeCard X1 (PCI device 0x0410) uses a Lattice MachXO3 CPLD
that is programmed over I2C using in-system programming (ISP).
The CPLD is connected to a secondary I2C bus shared with the onboard
MicroBlaze soft CPU.
Add support for CPLD access and firmware updates on the ADVA TimeCard X1
board by arbitration of the shared I2C bus, CPLD ISP command handling,
status polling, and firmware upload operations using the firmware-upload
subsystem.
Add the following X1-only user-visible interfaces:
/sys/class/timecard/ocpN/cpld_device_id
report the 32-bit Lattice MachXO3 CPLD device ID
/sys/class/timecard/ocpN/cpld_status
report the CPLD status register, including the DONE,
BUSY, and FAILED indicators
Firmware updates are performed through the firmware-upload framework,
which acquires ownership of the shared I2C bus, erases the CPLD
configuration flash, programs the image page-by-page, and activates
the new image using the MachXO3 REFRESH command.
All CPLD operations are serialized and coordinated with the MicroBlaze
firmware to ensure exclusive access to the shared I2C bus. The added
interfaces are available only on ADVA TimeCard X1 boards.
Signed-off-by: Sagi Maimon <maimon.sagi@gmail.com>
---
Addressed comments from:
- Vadim Fedorenko :https://lore.kernel.org/all/CAMuE1bEaRBJVSJYONJ2o3adOOzayv0ayNZ2qCB1zxuodpbiR=Q@mail.gmail.com/
Changes since v9:
- Remove duplicate header includes.
- Fix reverse Christmas tree variable ordering.
- Use get_unaligned_be32() for CPLD device ID decoding.
- Make constant command argument arrays static.
Documentation/ABI/testing/sysfs-timecard | 34 ++
drivers/ptp/ptp_ocp.c | 495 ++++++++++++++++++++++-
2 files changed, 525 insertions(+), 4 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-timecard b/Documentation/ABI/testing/sysfs-timecard
index 3ae41b7634ac..f7c9955acb0a 100644
--- a/Documentation/ABI/testing/sysfs-timecard
+++ b/Documentation/ABI/testing/sysfs-timecard
@@ -11,6 +11,40 @@ Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This directory contains the attributes of the Nth timecard
registered.
+What: /sys/class/timecard/ocpN/cpld_device_id
+Date: July 2026
+Contact: Sagi Maimon <maimon.sagi@gmail.com>
+Description: (RO) The 32-bit Lattice device ID of the TAP CPLD, reported as
+ a hex string, e.g. "0xe12bc043".
+
+ Only present on ADVA x1 TAP boards (PCI ID 0xad5a:0x0410).
+ The Lattice LCMXO3LF-210 reports 0xe12bc043.
+
+ The driver acquires the MicroBlaze I2C bus internally before
+ issuing the READ_IDCODE command; no bus arbitration is required
+ from userspace.
+
+What: /sys/class/timecard/ocpN/cpld_status
+Date: July 2026
+Contact: Sagi Maimon <maimon.sagi@gmail.com>
+Description: (RO) The status register of the TAP CPLD, in human-readable
+ form:
+
+ done=<0|1> busy=<0|1> failed=<0|1>
+
+ Only present on ADVA x1 TAP boards (PCI ID 0xad5a:0x0410).
+
+ done=1 indicates the configuration flash was successfully
+ programmed and is active. busy=1 means an internal operation
+ is in progress. failed=1 means the last ISC operation failed.
+
+ The driver acquires the MicroBlaze I2C bus internally; no bus
+ arbitration is required from userspace.
+
+ To program new CPLD firmware use the standard kernel
+ firmware-upload interface registered at:
+ /sys/class/firmware/adva-cpld/
+
What: /sys/class/timecard/ocpN/available_clock_sources
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 35e911f1ad78..cb259413042b 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -24,6 +24,9 @@
#include <linux/nvmem-consumer.h>
#include <linux/crc16.h>
#include <linux/dpll.h>
+#include <linux/unaligned.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
#define PCI_DEVICE_ID_META_TIMECARD 0x0400
@@ -85,6 +88,7 @@ struct ptp_ocp_adva_info {
u8 signals_nr;
u8 freq_in_nr;
const struct ocp_attr_group *attr_groups;
+ bool has_cpld; /* x1: supports CPLD firmware upload */
};
#define OCP_CTRL_ENABLE BIT(0)
@@ -163,7 +167,8 @@ struct gpio_reg {
u32 gpio1;
u32 __pad0;
u32 gpio2;
- u32 __pad1;
+ /* adva_x1: I2C bus ownership register; reserved on other variants */
+ u32 i2c_bus_ctrl;
};
struct irig_master_reg {
@@ -416,6 +421,12 @@ struct ptp_ocp {
dpll_tracker tracker;
int signals_nr;
int freq_in_nr;
+ /* adva_x1 CPLD I2C (internal use only) */
+ struct mutex cpld_lock; /* serialises CPLD operations */
+ int cpld_i2c_adap_nr; /* I2C adapter nr; -1 if absent */
+ struct fw_upload *cpld_fw_upload; /* firmware upload handle; NULL if absent */
+ bool cpld_cancel; /* cancellation requested */
+ bool cpld_in_config_mode; /* EN_CFG_TP issued but not yet REFRESH'd */
};
#define OCP_REQ_TIMESTAMP BIT(0)
@@ -449,6 +460,8 @@ static int ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r);
static int ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r);
+static const struct fw_upload_ops adva_cpld_upload_ops;
+
static const struct ocp_sma_op ocp_adva_sma_op;
static const struct ocp_sma_op ocp_adva_x1_sma_op;
@@ -1273,6 +1286,7 @@ static struct ocp_resource ocp_adva_x1_resource[] = {
.signals_nr = 4,
.freq_in_nr = 4,
.attr_groups = adva_timecard_x1_groups,
+ .has_cpld = true,
},
},
{ }
@@ -3197,6 +3211,20 @@ ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
return err;
ptp_ocp_sma_init(bp);
+ if (info->has_cpld) {
+ struct fw_upload *fwl;
+
+ fwl = firmware_upload_register(THIS_MODULE, &bp->pdev->dev,
+ "adva-cpld",
+ &adva_cpld_upload_ops, bp);
+ if (IS_ERR(fwl))
+ dev_warn(&bp->pdev->dev,
+ "CPLD firmware upload unavailable: %pe\n",
+ fwl);
+ else
+ bp->cpld_fw_upload = fwl;
+ }
+
return ptp_ocp_init_clock(bp, &info->servo);
}
@@ -4224,6 +4252,442 @@ static const struct ocp_attr_group art_timecard_groups[] = {
{ },
};
+/*
+ * Internal helpers for the adva_x1 TAP CPLD (Lattice LCMXO3LF-210).
+ *
+ * The CPLD sits at I2C address 0x40 behind a PCA9548 mux (0x74) on
+ * channel 0. The I2C bus is shared with the MicroBlaze firmware;
+ * cpld_lock + mblaze acquire/release provide mutual exclusion for the
+ * full duration of any CPLD operation. No raw I2C access is exposed
+ * to userspace; only the high-level attributes below are.
+ */
+
+#define ADVA_MUX_ADDR 0x74
+#define ADVA_CPLD_ADDR 0x40
+#define ADVA_MUX_CHANNEL 0
+
+#define MBLAZE_REQUEST 0x0000aaaaU
+#define MBLAZE_GRANTED 0x5555aaaaU
+#define MBLAZE_RELEASE 0x55550000U
+#define MBLAZE_RETRIES 200
+#define MBLAZE_RETRY_US 10000
+
+/* Lattice LCMXO3LF ISC command codes */
+#define CPLD_CMD_READ_ID 0xE0000000UL
+#define CPLD_CMD_READ_STATUS 0x3C000000UL
+#define CPLD_CMD_EN_CFG_TP 0x74 /* enable config, transparent mode */
+#define CPLD_CMD_DIS_CFG 0x26
+#define CPLD_CMD_ERASE 0x0E
+#define CPLD_CMD_RESET_ADDR 0x46
+#define CPLD_CMD_WRITE_PAGE 0x70
+#define CPLD_CMD_SET_DONE 0x5E
+#define CPLD_CMD_REFRESH 0x79
+#define CPLD_PAGE_SIZE 16
+
+/* Status register bit positions (Lattice LCMXO3LF datasheet) */
+#define CPLD_STATUS_DONE BIT(8)
+#define CPLD_STATUS_BUSY BIT(12)
+#define CPLD_STATUS_FAILED BIT(13)
+
+/*
+ * adva_x1_i2c_xfer() - issue a single I2C transaction on the CPLD bus.
+ *
+ * All buffers are heap-allocated internally to guarantee DMA safety for
+ * the Xilinx I2C controller. Caller must hold bp->cpld_lock.
+ */
+static int adva_x1_i2c_xfer(struct ptp_ocp *bp,
+ u8 addr, const void *wdata, u8 wlen,
+ void *rdata, u8 rlen, bool nostart)
+{
+ u8 *wbuf = NULL, *rbuf = NULL;
+ struct i2c_adapter *adap;
+ struct i2c_msg msgs[2];
+ int nmsgs = 0, ret;
+
+ adap = i2c_get_adapter(READ_ONCE(bp->cpld_i2c_adap_nr));
+ if (!adap)
+ return -ENODEV;
+
+ if (wlen) {
+ wbuf = kmemdup(wdata, wlen, GFP_KERNEL);
+ if (!wbuf) {
+ ret = -ENOMEM;
+ goto put;
+ }
+ msgs[nmsgs++] = (struct i2c_msg){
+ .addr = addr,
+ .flags = I2C_M_DMA_SAFE,
+ .len = wlen,
+ .buf = wbuf,
+ };
+ }
+ if (rlen) {
+ rbuf = kzalloc(rlen, GFP_KERNEL);
+ if (!rbuf) {
+ ret = -ENOMEM;
+ goto put;
+ }
+ msgs[nmsgs++] = (struct i2c_msg){
+ .addr = addr,
+ .flags = I2C_M_RD | I2C_M_DMA_SAFE |
+ (nostart ? I2C_M_NOSTART : 0),
+ .len = rlen,
+ .buf = rbuf,
+ };
+ }
+
+ ret = i2c_transfer(adap, msgs, nmsgs);
+ if (ret == nmsgs) {
+ if (rdata && rlen)
+ memcpy(rdata, rbuf, rlen);
+ ret = 0;
+ } else {
+ ret = (ret < 0) ? ret : -EIO;
+ }
+put:
+ kfree(wbuf);
+ kfree(rbuf);
+ i2c_put_adapter(adap);
+ return ret;
+}
+
+/* Acquire the shared I2C bus from the MicroBlaze firmware. */
+static int adva_x1_mblaze_acquire(struct ptp_ocp *bp)
+{
+ u32 val;
+ int i;
+
+ if (!bp->pps_select)
+ return -ENODEV;
+
+ /* Release any stale grant left by a previous crashed caller. */
+ iowrite32(0, &bp->pps_select->i2c_bus_ctrl);
+ val = ioread32(&bp->pps_select->i2c_bus_ctrl);
+ if (val != 0)
+ return -EBUSY;
+
+ iowrite32(MBLAZE_REQUEST, &bp->pps_select->i2c_bus_ctrl);
+ for (i = 0; i < MBLAZE_RETRIES; i++) {
+ usleep_range(MBLAZE_RETRY_US, MBLAZE_RETRY_US + 1000);
+ val = ioread32(&bp->pps_select->i2c_bus_ctrl);
+ if (val == MBLAZE_GRANTED)
+ return 0;
+ }
+ return -ETIMEDOUT;
+}
+
+static void adva_x1_mblaze_release(struct ptp_ocp *bp)
+{
+ if (bp->pps_select)
+ iowrite32(MBLAZE_RELEASE, &bp->pps_select->i2c_bus_ctrl);
+}
+
+static int adva_x1_mux_select(struct ptp_ocp *bp, int ch)
+{
+ u8 val = (ch >= 0) ? BIT(ch) : 0;
+
+ return adva_x1_i2c_xfer(bp, ADVA_MUX_ADDR, &val, 1, NULL, 0, false);
+}
+
+/* Send 1-byte ISC command + optional arguments. */
+static int adva_x1_cpld_write(struct ptp_ocp *bp,
+ u8 cmd, const u8 *args, u8 nargs)
+{
+ u8 buf[1 + 64];
+
+ if (nargs > 64)
+ return -EINVAL;
+ buf[0] = cmd;
+ if (nargs)
+ memcpy(&buf[1], args, nargs);
+ return adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, buf, 1 + nargs,
+ NULL, 0, false);
+}
+
+/*
+ * Send a 4-byte command then read data back without an intermediate STOP
+ * (Lattice combined write→repeated-START→read).
+ */
+static int adva_x1_cpld_cmd_read(struct ptp_ocp *bp,
+ u32 cmd_be, u8 *out, u8 out_len)
+{
+ __be32 cmd = cpu_to_be32(cmd_be);
+
+ return adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, &cmd, 4, out, out_len, true);
+}
+
+static int adva_x1_cpld_read_status(struct ptp_ocp *bp, u32 *status)
+{
+ u8 buf[4];
+ int ret;
+
+ ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_STATUS, buf, 4);
+ if (ret)
+ return ret;
+ *status = get_unaligned_be32(buf);
+ return 0;
+}
+
+static int adva_x1_cpld_wait_ready(struct ptp_ocp *bp, unsigned int max_ms)
+{
+ unsigned int elapsed = 0;
+ u32 status;
+
+ while (elapsed < max_ms) {
+ if (adva_x1_cpld_read_status(bp, &status))
+ return -EIO;
+ if (status & CPLD_STATUS_FAILED)
+ return -EIO;
+ if (!(status & CPLD_STATUS_BUSY))
+ return 0;
+ usleep_range(100000, 101000);
+ elapsed += 100;
+ }
+ return -ETIMEDOUT;
+}
+
+/*
+ * cpld_device_id - show the Lattice device ID of the TAP CPLD.
+ *
+ * Returns the 32-bit ID as a hex string, e.g. "0x612bc043\n".
+ * Lattice LCMXO3LF-210 reports 0x612BC043.
+ */
+static ssize_t
+cpld_device_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+ u8 data[4];
+ u32 id = 0;
+ int ret;
+
+ mutex_lock(&bp->cpld_lock);
+ ret = adva_x1_mblaze_acquire(bp);
+ if (ret)
+ goto out;
+ ret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);
+ if (ret)
+ goto release;
+ ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_ID, data, 4);
+ if (!ret)
+ id = get_unaligned_be32(data);
+ adva_x1_mux_select(bp, -1);
+release:
+ adva_x1_mblaze_release(bp);
+out:
+ mutex_unlock(&bp->cpld_lock);
+ return ret ? ret : sysfs_emit(buf, "0x%08x\n", id);
+}
+static DEVICE_ATTR_RO(cpld_device_id);
+
+/*
+ * cpld_status - show the status register of the TAP CPLD.
+ *
+ * Returns a human-readable string: "done=<0|1> busy=<0|1> failed=<0|1>\n"
+ */
+static ssize_t
+cpld_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+ u32 st = 0;
+ int ret;
+
+ mutex_lock(&bp->cpld_lock);
+ ret = adva_x1_mblaze_acquire(bp);
+ if (ret)
+ goto out;
+ ret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);
+ if (ret)
+ goto release;
+ ret = adva_x1_cpld_read_status(bp, &st);
+ adva_x1_mux_select(bp, -1);
+release:
+ adva_x1_mblaze_release(bp);
+out:
+ mutex_unlock(&bp->cpld_lock);
+ return ret ? ret : sysfs_emit(buf, "done=%u busy=%u failed=%u\n",
+ !!(st & CPLD_STATUS_DONE),
+ !!(st & CPLD_STATUS_BUSY),
+ !!(st & CPLD_STATUS_FAILED));
+}
+static DEVICE_ATTR_RO(cpld_status);
+
+/*
+ * adva_x1 CPLD firmware-upload callbacks.
+ *
+ * The kernel firmware-upload subsystem (CONFIG_FW_UPLOAD) exposes:
+ * /sys/class/firmware/adva-cpld/{data,loading,status,error,...}
+ * Userspace writes the raw binary page data directly — no /lib/firmware/
+ * staging file is needed.
+ *
+ * Callback sequence driven by the framework:
+ * prepare() - validate size, acquire bus, enable config, erase flash
+ * write() - program one 16-byte page per call
+ * poll_complete()- set DONE, REFRESH, wait for CPLD to reboot
+ * cancel() - set flag; checked at the start of each callback
+ * cleanup() - release bus resources (called on success or failure)
+ */
+static enum fw_upload_err
+adva_cpld_prepare(struct fw_upload *fwl, const u8 *data, u32 size)
+{
+ static const u8 era_args[3] = { 0x04, 0x00, 0x00 }; /* cfg sector only */
+ static const u8 en_args[2] = { 0x08, 0x00 };
+ static const u8 dis_args[2] = { 0x00, 0x00 };
+ static const u8 zero3[3] = { 0 };
+ enum fw_upload_err ret = FW_UPLOAD_ERR_NONE;
+ struct ptp_ocp *bp = fwl->dd_handle;
+
+ if (!size || size % CPLD_PAGE_SIZE)
+ return FW_UPLOAD_ERR_INVALID_SIZE;
+
+ bp->cpld_cancel = false;
+ bp->cpld_in_config_mode = false;
+
+ mutex_lock(&bp->cpld_lock);
+
+ if (adva_x1_mblaze_acquire(bp)) {
+ ret = FW_UPLOAD_ERR_TIMEOUT;
+ goto err_unlock;
+ }
+
+ if (adva_x1_mux_select(bp, ADVA_MUX_CHANNEL)) {
+ ret = FW_UPLOAD_ERR_HW_ERROR;
+ goto err_release;
+ }
+
+ if (adva_x1_cpld_write(bp, CPLD_CMD_EN_CFG_TP, en_args, 2) ||
+ adva_x1_cpld_wait_ready(bp, 5000)) {
+ ret = FW_UPLOAD_ERR_HW_ERROR;
+ goto err_deselect;
+ }
+ bp->cpld_in_config_mode = true;
+
+ if (bp->cpld_cancel) {
+ ret = FW_UPLOAD_ERR_CANCELED;
+ goto err_deselect;
+ }
+
+ if (adva_x1_cpld_write(bp, CPLD_CMD_ERASE, era_args, 3) ||
+ adva_x1_cpld_wait_ready(bp, 15000)) {
+ ret = FW_UPLOAD_ERR_HW_ERROR;
+ goto err_deselect;
+ }
+
+ if (bp->cpld_cancel) {
+ ret = FW_UPLOAD_ERR_CANCELED;
+ goto err_deselect;
+ }
+
+ if (adva_x1_cpld_write(bp, CPLD_CMD_RESET_ADDR, zero3, 3)) {
+ ret = FW_UPLOAD_ERR_HW_ERROR;
+ goto err_deselect;
+ }
+
+ return FW_UPLOAD_ERR_NONE; /* cleanup() will unlock everything */
+
+err_deselect:
+ if (bp->cpld_in_config_mode) {
+ adva_x1_cpld_write(bp, CPLD_CMD_DIS_CFG, dis_args, 2);
+ bp->cpld_in_config_mode = false;
+ }
+ adva_x1_mux_select(bp, -1);
+err_release:
+ adva_x1_mblaze_release(bp);
+err_unlock:
+ mutex_unlock(&bp->cpld_lock);
+ return ret;
+}
+
+static enum fw_upload_err
+adva_cpld_write(struct fw_upload *fwl, const u8 *data,
+ u32 offset, u32 size, u32 *written)
+{
+ struct ptp_ocp *bp = fwl->dd_handle;
+ u8 page_args[3 + CPLD_PAGE_SIZE];
+
+ if (bp->cpld_cancel)
+ return FW_UPLOAD_ERR_CANCELED;
+
+ if (size < CPLD_PAGE_SIZE)
+ return FW_UPLOAD_ERR_INVALID_SIZE;
+
+ page_args[0] = 0x00;
+ page_args[1] = 0x00;
+ page_args[2] = 0x01;
+ memcpy(&page_args[3], data + offset, CPLD_PAGE_SIZE);
+
+ if (adva_x1_cpld_write(bp, CPLD_CMD_WRITE_PAGE,
+ page_args, 3 + CPLD_PAGE_SIZE) ||
+ adva_x1_cpld_wait_ready(bp, 100))
+ return FW_UPLOAD_ERR_HW_ERROR;
+
+ *written = CPLD_PAGE_SIZE;
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err
+adva_cpld_poll_complete(struct fw_upload *fwl)
+{
+ static const u8 ref_args[2] = { 0x00, 0x00 };
+ static const u8 zero3[3] = { 0 };
+ struct ptp_ocp *bp = fwl->dd_handle;
+ u32 st;
+
+ if (bp->cpld_cancel)
+ return FW_UPLOAD_ERR_CANCELED;
+
+ if (adva_x1_cpld_write(bp, CPLD_CMD_SET_DONE, zero3, 3) ||
+ adva_x1_cpld_wait_ready(bp, 1000))
+ return FW_UPLOAD_ERR_HW_ERROR;
+
+ if (adva_x1_cpld_read_status(bp, &st) || !(st & CPLD_STATUS_DONE))
+ return FW_UPLOAD_ERR_HW_ERROR;
+
+ if (adva_x1_cpld_write(bp, CPLD_CMD_REFRESH, ref_args, 2))
+ return FW_UPLOAD_ERR_HW_ERROR;
+
+ /* CPLD reboots after REFRESH; re-select mux once it comes back up */
+ msleep(1500);
+ adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);
+ if (adva_x1_cpld_wait_ready(bp, 3000))
+ return FW_UPLOAD_ERR_TIMEOUT;
+
+ bp->cpld_in_config_mode = false;
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void
+adva_cpld_cancel(struct fw_upload *fwl)
+{
+ struct ptp_ocp *bp = fwl->dd_handle;
+
+ bp->cpld_cancel = true;
+}
+
+static void
+adva_cpld_cleanup(struct fw_upload *fwl)
+{
+ static const u8 dis_args[2] = { 0x00, 0x00 };
+ struct ptp_ocp *bp = fwl->dd_handle;
+
+ if (bp->cpld_in_config_mode) {
+ adva_x1_cpld_write(bp, CPLD_CMD_DIS_CFG, dis_args, 2);
+ bp->cpld_in_config_mode = false;
+ }
+ adva_x1_mux_select(bp, -1);
+ adva_x1_mblaze_release(bp);
+ mutex_unlock(&bp->cpld_lock);
+}
+
+static const struct fw_upload_ops adva_cpld_upload_ops = {
+ .prepare = adva_cpld_prepare,
+ .write = adva_cpld_write,
+ .poll_complete = adva_cpld_poll_complete,
+ .cancel = adva_cpld_cancel,
+ .cleanup = adva_cpld_cleanup,
+};
+
static struct attribute *adva_timecard_attrs[] = {
&dev_attr_serialnum.attr,
&dev_attr_gnss_sync.attr,
@@ -4272,6 +4736,8 @@ static struct attribute *adva_timecard_x1_attrs[] = {
&dev_attr_ts_window_adjust.attr,
&dev_attr_utc_tai_offset.attr,
&dev_attr_tod_correction.attr,
+ &dev_attr_cpld_device_id.attr,
+ &dev_attr_cpld_status.attr,
NULL,
};
@@ -4902,6 +5368,11 @@ ptp_ocp_detach(struct ptp_ocp *bp)
clk_hw_unregister_fixed_rate(bp->i2c_clk);
if (bp->n_irqs)
pci_free_irq_vectors(bp->pdev);
+ if (bp->cpld_fw_upload) {
+ firmware_upload_unregister(bp->cpld_fw_upload);
+ bp->cpld_fw_upload = NULL;
+ }
+ mutex_destroy(&bp->cpld_lock);
device_unregister(&bp->dev);
}
@@ -5078,6 +5549,17 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
goto out_disable;
+ /* Must be before the first error path that calls ptp_ocp_detach(),
+ * so mutex_destroy() always runs on an initialised mutex.
+ * Must also be before ptp_ocp_register_resources(): the I2C bus
+ * notifier (ptp_ocp_i2c_notifier_call) fires when the adapter
+ * registers and stores the adapter number in cpld_i2c_adap_nr; the
+ * -1 sentinel below must already be written so that a notifier
+ * firing during registration is never overwritten by this init.
+ */
+ mutex_init(&bp->cpld_lock);
+ bp->cpld_i2c_adap_nr = -1;
+
INIT_DELAYED_WORK(&bp->sync_work, ptp_ocp_sync_work);
/* compat mode.
@@ -5217,11 +5699,16 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb,
found:
bp = dev_get_drvdata(dev);
- if (add)
+ if (add) {
ptp_ocp_symlink(bp, child, "i2c");
- else
+ /* Cache adapter nr; used by cpld_device_id/cpld_status/cpld_program
+ * for reference-counted unbind-safe adapter access.
+ */
+ WRITE_ONCE(bp->cpld_i2c_adap_nr, i2c_verify_adapter(child)->nr);
+ } else {
+ WRITE_ONCE(bp->cpld_i2c_adap_nr, -1); /* invalidate before free */
sysfs_remove_link(&bp->dev.kobj, "i2c");
-
+ }
return 0;
}
--
2.47.0
^ permalink raw reply related
* [PATCH 2/2] wifi: iwlwifi: mld: Fix GP2 to nanoseconds overflow on 32-bit
From: Zhan Xusheng @ 2026-08-05 8:25 UTC (permalink / raw)
To: Miri Korenblit, linux-wireless
Cc: Johannes Berg, Richard Cochran, netdev, linux-kernel, zhanxusheng
In-Reply-To: <20260805082515.4136842-1-zhanxusheng@xiaomi.com>
GP2 is a free-running 32-bit microsecond hardware counter. The PTP code
converts a u32 GP2 value to nanoseconds as:
gp2 * NSEC_PER_USEC
NSEC_PER_USEC is a plain 'long' (1000L), so on 32-bit builds this
multiplication is evaluated in 32-bit arithmetic and overflows once the
GP2 value exceeds ~4.29 million microseconds (~4.3 s). GP2 wraps only
every 2^32 microseconds (~71.5 min), so the product is truncated for
almost the entire counter range, producing bogus PTP timestamps.
Neighbouring conversions already cast to u64 first (e.g.
(u64)gp2 * NSEC_PER_USEC); these sites just missed the cast. Cast to u64
so the multiplication is performed in 64-bit, matching the existing call
sites. 64-bit builds are unaffected.
Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
Fixes: f1699ad5857d ("wifi: iwlwifi: mld: add debugfs for using ptp clock time for monitor interface")
Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
---
drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 4 ++--
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 6 ++----
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
index 1ac85d6ce1d7..81645f8caa54 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
@@ -59,7 +59,7 @@ static void iwl_mld_ptp_update_new_read(struct iwl_mld *mld, u32 gp2)
u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns)
{
struct ptp_data *data = &mld->ptp_data;
- u64 scale_time_gp2_ns = mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
+ u64 scale_time_gp2_ns = (u64)mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
u64 res;
u64 diff;
s64 scaled_diff;
@@ -159,7 +159,7 @@ static int iwl_mld_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
spin_lock_bh(&data->lock);
data->scale_update_adj_time_ns =
- iwl_mld_ptp_get_adj_time(mld, gp2 * NSEC_PER_USEC);
+ iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC);
data->scale_update_gp2 = gp2;
/* scale_update_adj_time_ns now relects the configured delta, the
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 269439d789f4..108ac70bfb14 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -1524,10 +1524,8 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,
iwl_mld_add_rtap_sniffer_config(mld, skb);
if (mld->monitor.ptp_time) {
- u64 adj_time =
- iwl_mld_ptp_get_adj_time(mld,
- phy_data->gp2_on_air_rise *
- NSEC_PER_USEC);
+ u64 gp2_ns = (u64)phy_data->gp2_on_air_rise * NSEC_PER_USEC;
+ u64 adj_time = iwl_mld_ptp_get_adj_time(mld, gp2_ns);
rx_status->mactime = div64_u64(adj_time, NSEC_PER_USEC);
rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64;
--
2.43.0
^ permalink raw reply related
* [PATCH 1/2] wifi: iwlwifi: mvm: Fix GP2 to nanoseconds overflow on 32-bit
From: Zhan Xusheng @ 2026-08-05 8:25 UTC (permalink / raw)
To: Miri Korenblit, linux-wireless
Cc: Johannes Berg, Richard Cochran, netdev, linux-kernel, zhanxusheng
In-Reply-To: <20260805082515.4136842-1-zhanxusheng@xiaomi.com>
GP2 is a free-running 32-bit microsecond hardware counter. The PTP code
converts a u32 GP2 value to nanoseconds as:
gp2 * NSEC_PER_USEC
NSEC_PER_USEC is a plain 'long' (1000L), so on 32-bit builds this
multiplication is evaluated in 32-bit arithmetic and overflows once the
GP2 value exceeds ~4.29 million microseconds (~4.3 s). GP2 wraps only
every 2^32 microseconds (~71.5 min), so the product is truncated for
almost the entire counter range, producing bogus PTP timestamps.
Neighbouring conversions already cast to u64 first (e.g.
(u64)gp2 * NSEC_PER_USEC); these sites just missed the cast. Cast to u64
so the multiplication is performed in 64-bit, matching the existing call
sites. 64-bit builds are unaffected.
Fixes: a2f49f7d52a9 ("wifi: iwlwifi: mvm: implement PHC clock adjustments")
Fixes: 0e49e940d1bc ("wifi: iwlwifi: mvm: add an option to use ptp clock for rx timestamp")
Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
---
drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 4 ++--
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
index 49dcb1388007..5f33e2f8fb2a 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
@@ -47,7 +47,7 @@ static void iwl_mvm_ptp_update_new_read(struct iwl_mvm *mvm, u32 gp2)
u64 iwl_mvm_ptp_get_adj_time(struct iwl_mvm *mvm, u64 base_time_ns)
{
struct ptp_data *data = &mvm->ptp_data;
- u64 last_gp2_ns = mvm->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
+ u64 last_gp2_ns = (u64)mvm->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
u64 res;
u64 diff;
@@ -259,7 +259,7 @@ static int iwl_mvm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
*/
gp2 = iwl_mvm_get_systime(mvm);
data->scale_update_adj_time_ns =
- iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
+ iwl_mvm_ptp_get_adj_time(mvm, (u64)gp2 * NSEC_PER_USEC);
data->scale_update_gp2 = gp2;
data->wrap_counter = 0;
data->delta = 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 7f0b4f5daa21..7c21cd89db93 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -2016,8 +2016,8 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm,
rx_status->device_timestamp = phy_data->gp2_on_air_rise;
if (mvm->rx_ts_ptp && mvm->monitor_on) {
- u64 adj_time =
- iwl_mvm_ptp_get_adj_time(mvm, phy_data->gp2_on_air_rise * NSEC_PER_USEC);
+ u64 gp2_ns = (u64)phy_data->gp2_on_air_rise * NSEC_PER_USEC;
+ u64 adj_time = iwl_mvm_ptp_get_adj_time(mvm, gp2_ns);
rx_status->mactime = div64_u64(adj_time, NSEC_PER_USEC);
rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] w5100: restore GPIO-based link detection
From: Arthur Crépin Leblond @ 2026-08-05 8:25 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Arnd Bergmann, netdev, devicetree, linux-kernel
In-Reply-To: <d762ff5f-30ba-42fc-bf58-500f807483ad@lunn.ch>
On Tue, Aug 04, 2026 at 07:54:29PM +0200, Andrew Lunn wrote:
>On Tue, Aug 04, 2026 at 04:38:35PM +0200, Arthur Crépin Leblond wrote:
>> Commit dacf281771a9 ("w5100: remove unused gpio link detection")
>> dropped the link_gpio/link_irq handling on the grounds that no
>> devicetree user passed a "link" GPIO at the time.
>>
>> Signed-off-by: Arthur Crépin Leblond <arthur@marmottus.net>
>
>Hi Arthur
>
>Do you understand the architecture of this device? What exactly is on
>the other end of this GPIO?
>
>https://wiznet.io/products/ethernet-chips/w5100
>
>suggests it has an integrated PHY. So why is a GPIO needed to report
>link?
>
>Thanks
> Andrew
Hi Andrew,
the W5100/W5500 exposes directly a LINKLED pin for the carrier status.
On my board (RPi), that pin is wired to a GPIO to detect changes on the host
directly via an interrupt.
Arthur
^ permalink raw reply
* [PATCH 0/2] wifi: iwlwifi: Fix GP2 to nanoseconds overflow on 32-bit
From: Zhan Xusheng @ 2026-08-05 8:25 UTC (permalink / raw)
To: Miri Korenblit, linux-wireless
Cc: Johannes Berg, Richard Cochran, netdev, linux-kernel, zhanxusheng
GP2 is a free-running 32-bit microsecond hardware counter. Both the mvm
and mld PTP code convert a u32 GP2 value to nanoseconds with
gp2 * NSEC_PER_USEC
NSEC_PER_USEC is a plain 'long' (1000L), so on 32-bit builds the
multiplication is performed in 32-bit arithmetic and overflows once the
GP2 value exceeds ~4.29 million microseconds (~4.3 s). Since GP2 wraps
only every 2^32 microseconds (~71.5 min), the product is truncated for
almost the entire counter range, producing bogus PTP timestamps.
Most conversions in the same files already cast to u64 first
((u64)gp2 * NSEC_PER_USEC); a handful of sites (the adjfine path, the
stored scale_update_gp2 and the monitor/RX timestamp path) simply missed
the cast. These patches add the missing u64 casts so the multiplications
are done in 64-bit, matching the existing call sites. 64-bit builds are
unaffected. The mld driver inherited the same pattern (and bug) from
mvm, so it is fixed in a separate patch with its own Fixes: tags.
Found by code inspection; not tested on 32-bit hardware. The change is a
straightforward u64 cast matching the surrounding code.
Zhan Xusheng (2):
wifi: iwlwifi: mvm: Fix GP2 to nanoseconds overflow on 32-bit
wifi: iwlwifi: mld: Fix GP2 to nanoseconds overflow on 32-bit
drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 4 ++--
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 6 ++----
drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 4 ++--
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 4 ++--
4 files changed, 8 insertions(+), 10 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH net] netfilter: nf_dup_netdev: scrub duplicates to preserve the direct path
From: Pablo Neira Ayuso @ 2026-08-05 8:24 UTC (permalink / raw)
To: Alexandre Ferrieux
Cc: coreteam, netfilter-devel, edumazet, alexandre.ferrieux, netdev
In-Reply-To: <20260804201150.16364-1-alexandre.ferrieux@orange.com>
Hi,
On Tue, Aug 04, 2026 at 10:11:50PM +0200, Alexandre Ferrieux wrote:
> The nftables 'dup' action clones the skb with its full glory of
> metadata, including references to its destination and conntrack
> information. As a consequence, a link failure on the duplicate's
> egress path ends up doing the same as it would for the direct path,
> for example invalidating the original packet's destination, which
> typically breaks all TCP connections to that address.
>
> In other words, the "dup" path has the potential to wreak havoc
> in the direct path as a consequence of secondary link failures. This
> is very bad behavior for a monitoring tool, which is the most
> obvious application of 'dup'.
Can you describe your use-case a bit and how it breaks?
> This patch fixes all similar scenarii by calling skb_scrub_pkt()
> on the clone, severing its link to precious direct-path state.
This patch is targetted at the net tree, but nf.git is preferred.
> Note: the second argument of skb_scrub_pkt(), the boolean "packet
> is crossing netns", is intentionally set to 'false', as a 'true'
> involves exaggerate scrubbing, e.g. of the timestamp, which a
> monitoring 'dup' typically wants to preserve.
Yes, the skb->mark should really remain in place for the duplication.
As for the conntrack and dst, you have to explain what it breaks on
your end.
> Signed-off-by: Alexandre Ferrieux <alexandre.ferrieux@orange.com>
> ---
> net/netfilter/nf_dup_netdev.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c
> index c6bd5c29bed6..0f47a2135955 100644
> --- a/net/netfilter/nf_dup_netdev.c
> +++ b/net/netfilter/nf_dup_netdev.c
> @@ -63,8 +63,10 @@ void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif)
> return;
>
> skb = skb_clone(pkt->skb, GFP_ATOMIC);
> - if (skb)
> + if (skb) {
> + skb_scrub_packet(skb, false);
> nf_do_netdev_egress(skb, dev, nft_hook(pkt));
> + }
> }
> EXPORT_SYMBOL_GPL(nf_dup_netdev_egress);
>
> --
> 2.47.3
>
>
^ permalink raw reply
* Re: [PATCH v7 0/2] bpf, sockmap: handle spurious tcp_msg_wait_data() wakeup
From: Nnamdi Onyeyiri @ 2026-08-05 8:22 UTC (permalink / raw)
To: bpf, davem, edumazet, horms, jakub, jiayuan.chen, john.fastabend,
kuba, kuniyu, ncardwell, netdev, pabeni, sashiko-reviews,
linux-kernel, emil
In-Reply-To: <20260721223807.75101-1-nnamdio@gmail.com>
On Tue, Jul 21, 2026 at 11:38:05PM +0100, Nnamdi Onyeyiri wrote:
> Spurious wakeups in tcp_msg_wait_data() aren't being handled by
> tcp_bpf_recvmsg() and tcp_bpf_recvmsg_parser(), leading to unexpected
> EAGAIN errors returned by recvfrom()/recv(). Adding handling for the
> wakeup and a selftest.
>
> This issue was first discovered in an application that adds sockets to
> a sockmap as a way to view the received data. No redirects or any
> other operations are performed.
>
> Sashiko has noted a few other pre-existing issues in the same area
> (https://patch.msgid.link/20260714205118.17DB11F000E9@smtp.kernel.org)
> that can lead to EAGAIN in tcp_bpf_recvmsg() and
> tcp_bpf_recvmsg_parser(). A local run of Sashiko also identified a
> potential issue in tcp_bpf_recvmsg() handling zero-data FIN packets when
> the MSG_PEEK flag is set. To prevent this patchset from growing too
> large, I intend to submit follow up patches to address these once this
> one has been accepted.
>
> Changes in v7:
> - In tcp_bpf_recvmsg check for data in the TCP_CLOSE, !timeo and
> signal_pending branches.
> - Refactored changes to tcp_bpf_recvmsg to reduce duplication.
> - Increase the number of iterations in the selftests.
> - Documented selftests numeric constants.
> - Link to v6: https://patch.msgid.link/20260720171535.67867-1-nnamdio@gmail.com
>
> Changes in v6:
> - Closing the file descriptor in the selftest worker thread.
> - Ensuring the selftest loop breaks early for an error in the worker
> thread.
> - Added comments to selftest regarding focus on EAGAIN error.
> - Updated selftest commit message to imperative mood.
> - Link to v5: https://patch.msgid.link/20260717155348.54975-1-nnamdio@gmail.com
>
> Changes in v5:
> - Move selftest from net into bpf/prog_tests/sockmap_basic.c
> - Link to v4: https://patch.msgid.link/20260715213538.37229-1-nnamdio@gmail.com
>
> Changes in v4:
> - Fix potential data loss in tcp_bpf_recvmsg() when a FIN or RST has
> been received.
> - Check the return code of pthread_create() in the selftest.
> - Fix race caused by using EXPECT macros in a thread in selftest.
> - Link to v3: https://patch.msgid.link/20260714203927.32289-1-nnamdio@gmail.com
>
> Changes in v3:
> - Added the sockmap_recvfrom selftest.
> - Link to v2: https://patch.msgid.link/alFRK66z45eDNZA7@localhost.localdomain
>
> Changes in v2:
> - In tcp_bpf_recvmsg, handle signals and the socket closing in the loop.
> - Fix spurious wakeups when SO_RCVTIMEO has been set on the socket.
> - Link to v1: https://patch.msgid.link/ak_rR-Skd8Mvn4mH@localhost.localdomain
>
> Signed-off-by: Nnamdi Onyeyiri <nnamdio@gmail.com>
> ---
> Nnamdi Onyeyiri (2):
> bpf, sockmap: handle spurious tcp_msg_wait_data() wakeup
> selftests/bpf: add sockmap recvfrom EAGAIN selftest
>
> net/ipv4/tcp_bpf.c | 72 ++++++++--
> .../selftests/bpf/prog_tests/sockmap_basic.c | 136 ++++++++++++++++++
> 2 files changed, 199 insertions(+), 9 deletions(-)
>
> --
> 2.52.0
>
Hi all, just wondering if you need anything more from me on this?
^ permalink raw reply
* Re: [PATCH v4 net] net: page_pool: fix UAF in __page_pool_release_netmem_dma on xa_cmpxchg race
From: Jijie Shao @ 2026-08-05 8:21 UTC (permalink / raw)
To: Jakub Kicinski
Cc: shaojijie, davem, edumazet, pabeni, andrew+netdev, horms, hawk,
ilias.apalodimas, almasrymina, toke, shenjian15, liuyonglong,
chenhao418, yangshuaisong, netdev, linux-kernel
In-Reply-To: <20260804191758.0f2009f7@kernel.org>
on 2026/8/5 10:17, Jakub Kicinski wrote:
> On Fri, 31 Jul 2026 19:15:07 +0800 Jijie Shao wrote:
>> page_pool_scrub() iterates pool->dma_mapped via xa_for_each() with no
>> page ref held. __page_pool_release_netmem_dma() currently reads and
>> writes netmem fields (dma_addr, DMA index bits in pp_magic) after
>> xa_cmpxchg() returns. The unref path calls put_page() unconditionally
>> regardless of the cmpxchg outcome; when it loses the cmpxchg, it still
>> frees the page before the scrub winner finishes these netmem accesses,
>> so scrub touches a freed page -- a Use-After-Free.
>>
>> Fix this by splitting the DMA release into two functions:
>>
>> 1. __page_pool_unmap_netmem_dma() caches dma_addr before xa_cmpxchg(),
>> does the cmpxchg to remove the DMA mapping, and calls dma_unmap on
>> the cached address. It never touches netmem fields after the cmpxchg,
>> making it safe for the scrub path which holds no page ref.
>>
>> 2. __page_pool_release_netmem_dma() wraps the above and additionally
>> clears dma_addr and DMA index bits in netmem fields. This is safe
>> only when the caller holds a page ref, so it is used by the return
>> path (page_pool_return_netmem).
>>
>> The scrub path calls __page_pool_unmap_netmem_dma() directly; the return
>> path calls __page_pool_release_netmem_dma().
> Please clearly state what led you to discovering this bug?
> Was it directly hit in production?
> Was there a prod issue which made you investigate?
> Were you able to trigger the race and if so -- how?
>
>> Fixes: ee62ce7a1d90 ("page_pool: Track DMA-mapped pages and unmap them when destroying the pool")
>> Suggested-by: Mina Almasry <almasrymina@google.com>
>> Assisted-by: OhMyOpenCode:GLM-5.2
>> Signed-off-by: Jijie Shao <shaojijie@huawei.com>
>> ---
>> Changes in v4:
>> - Restructure per Mina's review: merge page_pool_remove_dma_mapping()
>> into __page_pool_unmap_netmem_dma() with dma_unmap inlined via goto
>> label; simplify __page_pool_release_netmem_dma() to a thin wrapper.
>> - Link to v3: https://lore.kernel.org/r/20260729110249.2824835-1-shaojijie@huawei.com
>>
>> Changes in v3:
>> - Fix unlikely() to likely() for PP_DMA_INDEX_BITS to match
>> file convention.
>> - Link to v2: https://lore.kernel.org/r/20260727132612.3277927-1-shaojijie@huawei.com
>>
>> Changes in v2:
>> - Redesign the fix per Mina's review: v1's unconditional
>> netmem_set_dma_index() introduced a UAF when the scrub path
>> (no page ref) writes to a page freed by the unref path.
>> - Cache dma_addr before xa_cmpxchg; move dma_addr/DMA index
>> cleanup to page_pool_return_netmem() which holds a page ref.
>> - Rename page_pool_release_dma_index() to
>> page_pool_remove_dma_mapping() to reflect its new role as a
>> pure cmpxchg wrapper.
>> - Link to v1: https://lore.kernel.org/r/20260724092135.414699-1-shaojijie@huawei.com
>> ---
>> net/core/page_pool.c | 46 ++++++++++++++++++++++----------------------
>> 1 file changed, 23 insertions(+), 23 deletions(-)
>>
>> diff --git a/net/core/page_pool.c b/net/core/page_pool.c
>> index 21dc4a9c8714..497bb1906fc3 100644
>> --- a/net/core/page_pool.c
>> +++ b/net/core/page_pool.c
>> @@ -500,29 +500,40 @@ static int page_pool_register_dma_index(struct page_pool *pool,
>> return err;
>> }
>>
>> -static int page_pool_release_dma_index(struct page_pool *pool,
>> - netmem_ref netmem)
>> +static void __page_pool_unmap_netmem_dma(struct page_pool *pool,
>> + netmem_ref netmem)
>> {
>> struct page *old, *page = netmem_to_page(netmem);
>> unsigned long id;
>> + dma_addr_t dma;
>> +
>> + if (!pool->dma_map)
>> + return;
>> +
>> + /* Cache dma_addr before xa_cmpxchg. The scrub path holds no page ref;
>> + * the unref path calls put_page() regardless of cmpxchg outcome, so
>> + * after the cmpxchg we cannot safely touch netmem fields.
>> + */
>> + dma = page_pool_get_dma_addr_netmem(netmem);
>>
>> if (unlikely(!PP_DMA_INDEX_BITS))
>> - return 0;
>> + goto unmap;
> nit: just indent the intervening lines please.
> Don't use goto where adding a code block would do.
>
Hi Jakub,
Thanks for the review. v5 incoming addressing both points:
- Added bug discovery context to the commit message.
- Replaced goto with if (likely(PP_DMA_INDEX_BITS)) block.
Jijie
^ permalink raw reply
* Re: [PATCH bpf-next] selftests/bpf: Track test_xdp_features DUT processes
From: Jiayuan Chen @ 2026-08-05 8:18 UTC (permalink / raw)
To: bochaolucky, Andrii Nakryiko, Eduard Zingerman
Cc: Ihor Solodrai, Lorenzo Bianconi, bpf, netdev
In-Reply-To: <20260805-xdp-dut-process-lifecycle-gmail-v1-1-45984df8d295@gmail.com>
On 8/5/26 3:29 PM, Bochao Cao via B4 Relay wrote:
> From: Bochao Cao <bochaolucky@gmail.com>
>
> test_xdp_features.sh waits for any xdp_features listener to appear and
> uses pidof during cleanup. A concurrent test can therefore make another
> test proceed before its own DUT is ready, and cleanup kills every
> xdp_features process on the host. The readiness loop also has no timeout,
> so a DUT that exits before listening leaves the test hung indefinitely.
>
> Record each DUT PID when it is spawned, wait for ss to report that exact
> PID with a bounded retry loop, and only terminate and reap recorded
> children. Install an EXIT trap and signal handlers so failure paths also
> remove child processes and network setup.
>
> This also removes the runtime dependency on pidof.
>
> Fixes: 4dba3e7852b7 ("selftests/bpf: introduce XDP compliance test tool")
> Closes: https://bugs.debian.org/1136522
It looks like part of the motivation here is dropping the pidof dependency?
I don't think that's worth optimizing. Selftests already require a bunch of
tools from non-essential packages (ip, ss and ethtool in this very script)
^ permalink raw reply
* Re: [PATCH net v3 1/1] ipmr: only copy pktinfo to cache reports
From: Ido Schimmel @ 2026-08-05 8:17 UTC (permalink / raw)
To: Jakub Kicinski
Cc: zhilinz, netdev, dsahern, davem, edumazet, pabeni, horms,
leone4fernando, vega
In-Reply-To: <20260805013126.4161847-1-kuba@kernel.org>
On Tue, Aug 04, 2026 at 06:31:26PM -0700, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email says "Looks legit, let's fix both in one patch?"
I suggested [1], but the bug in the IGMPMSG_WHOLEPKT /
IGMPMSG_WRVIFWHOLE branch needs a different Fixes tag (it precedes
bb7403655b3c) and Sashiko [2][3] found another bug in this code:
ipv4_pktinfo_prepare() modifies the control block of 'pkt', which can
result in this packet being processed incorrectly after
ipmr_cache_report() returns. I *think* that [4] takes care of it, but
all of this needs to be explained in the commit message which should
carry the appropriate Fixes tags (bb7403655b3c + 1da177e4c3f4).
Note that this upcall is not fast path, so the extra work shouldn't be a
problem.
Is this what you prefer?
[1] https://lore.kernel.org/all/20260729091227.GA1258429@shredder/
[2] https://sashiko.dev/#/patchset/1b809975e5bd9c0a1dd6fdd1db534e701fe5a4b6.1785379072.git.zhilinz%40nebusec.ai
[3] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/1b809975e5bd9c0a1dd6fdd1db534e701fe5a4b6.1785379072.git.zhilinz%40nebusec.ai
[4]
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 1d9a4ac14fce..cdc84eb05c79 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1057,6 +1057,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
static int ipmr_cache_report(const struct mr_table *mrt,
struct sk_buff *pkt, vifi_t vifi, int assert)
{
+ unsigned char pkt_cb[sizeof(pkt->cb)];
const int ihl = ip_hdrlen(pkt);
struct sock *mroute_sk;
struct igmphdr *igmp;
@@ -1112,8 +1113,6 @@ static int ipmr_cache_report(const struct mr_table *mrt,
msg = (struct igmpmsg *)skb_network_header(skb);
msg->im_vif = vifi;
msg->im_vif_hi = vifi >> 8;
- ipv4_pktinfo_prepare(mroute_sk, pkt, false);
- memcpy(skb->cb, pkt->cb, sizeof(skb->cb));
/* Add our header.
* Note that code, csum and group fields are cleared.
*/
@@ -1124,6 +1123,12 @@ static int ipmr_cache_report(const struct mr_table *mrt,
skb->transport_header = skb->network_header;
}
+ memcpy(pkt_cb, pkt->cb, sizeof(pkt_cb));
+ ipv4_pktinfo_prepare(mroute_sk, pkt, false);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ *PKTINFO_SKB_CB(skb) = *PKTINFO_SKB_CB(pkt);
+ memcpy(pkt->cb, pkt_cb, sizeof(pkt_cb));
+
igmpmsg_netlink_event(mrt, skb);
/* Deliver to mrouted */
^ permalink raw reply related
* Re: [PATCH v4 net] net: page_pool: fix UAF in __page_pool_release_netmem_dma on xa_cmpxchg race
From: Jijie Shao @ 2026-08-05 8:10 UTC (permalink / raw)
To: Mina Almasry, Jakub Kicinski
Cc: shaojijie, davem, edumazet, pabeni, andrew+netdev, horms, hawk,
ilias.apalodimas, toke, shenjian15, liuyonglong, chenhao418,
yangshuaisong, netdev, linux-kernel
In-Reply-To: <CAHS8izMVqidVkiVHNWZMqP4Eoi5N_M3Wv1Cnn4jjxHf3=cWNQA@mail.gmail.com>
on 2026/8/5 11:50, Mina Almasry wrote:
> On Tue, Aug 4, 2026 at 7:16 PM Jakub Kicinski <kuba@kernel.org> wrote:
>> On Fri, 31 Jul 2026 10:37:58 -0700 Mina Almasry wrote:
>>>> + __page_pool_unmap_netmem_dma(pool, netmem);
>>>> page_pool_set_dma_addr_netmem(netmem, 0);
>>>> + if (likely(PP_DMA_INDEX_BITS))
>>>> + netmem_set_dma_index(netmem, 0);
>>> I now notice that maybe another cleanup we could have done is open
>>> code __page_pool_unmap_netmem_dma() in this function to cut down 1
>>> helper, and just have the scrub function call
>>> __page_pool_release_netmem_dma() to reduce some code. But this is more
>>> than fine too I think, especially since this is a fix the stable trees
>>> are going to want I guess.
>> Not sure this is a good idea? scrub is trying to touch just the DMA
>> mapping, right? It shouldn't try to update the page itself because
>> it has no reference to the page, the page may get freed in parallel.
>> Hopefully DMA unmap on a freed page is legal..
> Scrub actually frees the page in the loop in
> page_pool_empty_ring(pool), so it's not true AFAIU that it 'shouldn't
> try to update the page'.
>
> My mental model (roughly) is that the pp has a ref and that single
> last ref can be dropped in the page_pool_destroy() path or the
> page_pool_put_netmem() path, but the ref can't be dropped twice and
> the same goes for dma unmapping.
>
> Now that i look closer at the code I have no idea why we didn't 'just'
> put the dma-unmapping inside of page_pool_return_netmem(). That code
> path is already common between scrub() and put_netmem() and does the
> synchronization between these 2 paths.
>
> When I have time I'll take a look to see if an LLM can find a better
> way to do this.
Hi Mina,
Thanks for the Reviewed-by.
I'll send v5 shortly addressing Jakub's requests. The deeper
restructure you're musing about seems orthogonal to this fix,
so let's discuss it separately.
Thanks,
Jijie Shao
^ permalink raw reply
* RE: [PATCH net-next v2 5/5] bridge: Use ndisc_parse_options() to parse ND options in br_nd_send()
From: Danielle Ratson @ 2026-08-05 8:07 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: dsahern@kernel.org, Ido Schimmel, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, razor@blackwall.org, ja@ssi.bg, Petr Machata,
fw@strlen.de, kuniyu@google.com, bridge@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260803112505.613873-6-danieller@nvidia.com>
> -----Original Message-----
> From: Danielle Ratson <danieller@nvidia.com>
> Sent: Monday, 3 August 2026 14:25
> To: netdev@vger.kernel.org
> Cc: dsahern@kernel.org; Ido Schimmel <idosch@nvidia.com>;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; horms@kernel.org; razor@blackwall.org; ja@ssi.bg; Petr
> Machata <petrm@nvidia.com>; fw@strlen.de; kuniyu@google.com;
> bridge@lists.linux.dev; linux-kernel@vger.kernel.org; Danielle Ratson
> <danieller@nvidia.com>
> Subject: [PATCH net-next v2 5/5] bridge: Use ndisc_parse_options() to parse
> ND options in br_nd_send()
>
> Replace the manual ND option parsing loop in br_nd_send() with
> ndisc_parse_options(), which provides proper validation and avoids the class
> of bugs that were fixed by commit 53fc685243bd ("bridge: Avoid infinite loop
> when suppressing NS messages with invalid options") and commit
> 850837965af1 ("bridge: br_nd_send: validate ND option lengths").
>
> Use ndisc_opt_addr_data() to extract the source link-layer address from the
> parsed options, which correctly validates the option length for the underlying
> device type.
>
> Export ndisc_parse_options() so that it can be resolved from the bridge when
> it is built as a module (CONFIG_BRIDGE=m); otherwise modpost fails with an
> undefined symbol.
>
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
Sashiko wrote:
"
--- Patch [5]: [PATCH net-next v2 5/5] bridge: Use ndisc_parse_options() to parse ND options in br_nd_send() ---
commit a5e722187a4312d62366c8f19772265d9169d89e
Author: Danielle Ratson <danieller@nvidia.com>
bridge: Use ndisc_parse_options() to parse ND options in br_nd_send()
This commit replaces the manual ND option parsing loop in br_nd_send()
with ndisc_parse_options() to provide proper validation. It also exports
ndisc_parse_options() from the IPv6 module so it can be called from the
bridge subsystem.
> diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
> --- a/net/bridge/br_arp_nd_proxy.c
> +++ b/net/bridge/br_arp_nd_proxy.c
[ ... ]
> @@ -284,20 +285,21 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
> daddr = eth_hdr(request)->h_source;
> ns = (struct nd_msg *)skb_transport_header(request);
>
> - /* Do we need option processing ? */
> - ns_olen = request->len - (skb_network_offset(request) +
> - sizeof(struct ipv6hdr)) - sizeof(*ns);
> - for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
> - if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
> - kfree_skb(reply);
> - return;
> - }
> - if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
> - if ((ns->opt[i + 1] << 3) >=
> - sizeof(struct nd_opt_hdr) + ETH_ALEN)
> - daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
> - break;
> - }
> + /* Derive the option length from the IPv6 payload length so that any
> + * trailing L2 padding in the skb is not parsed as ND options.
> + */
> + ns_olen = ntohs(ipv6_hdr(request)->payload_len) - sizeof(*ns);
> + if (!ndisc_parse_options(dev, ns->opt, ns_olen, &ndopts)) {
Will this cause a link-time build failure when CONFIG_BRIDGE=y and
CONFIG_IPV6=m?
The commit message mentions exporting ndisc_parse_options() allows it to be
resolved when the bridge is built as a module (CONFIG_BRIDGE=m).
However, since ndisc_parse_options() is defined in net/ipv6/ndisc.c, it is
compiled into the ipv6.ko module when CONFIG_IPV6=m. If the bridge subsystem
is built in (CONFIG_BRIDGE=y), vmlinux won't be able to resolve the symbol
at link time.
"
CONFIG_IPV6 is built-in only. It's a bool, not a tristate. So it is not a possible scenario.
^ permalink raw reply
* Re: [PATCH net-next 7/9] netfilter: flowtable: release tunnel route on error when building forward path
From: Pablo Neira Ayuso @ 2026-08-05 8:06 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netfilter-devel, davem, netdev, pabeni, edumazet, fw, horms
In-Reply-To: <20260805001906.3985830-1-kuba@kernel.org>
Hi,
On Tue, Aug 04, 2026 at 05:19:06PM -0700, Jakub Kicinski wrote:
[...]
> [Severity: High]
> Can the dst installed here end up belonging to a different address family
> than the flow it is attached to?
Yes, the extra check to ensure IPv4 over IPv6 does not end up with the
wrong route is really required, LLM is right here.
But such check will last not long because Lorenzo's Bianconi has been
working on ip over ipv6 series for the tunneling.
I think this is not a reason to stall this net-next series, and
I think this can be done it a follow up?
Reading the two comments on patches in this net-next PR, this is the
only one that is really an issue and I think it can be addressed in a
follow up.
> The removed nft_flow_tunnel_update_route() derived the replacement dst from
> nf_route(nft_net(pkt), &tun_dst, &fl, false, nft_pf(pkt)), so the dst family
> always matched the flow's l3proto. The dst that now arrives in
> info.tun_dst follows the tunnel underlay instead:
>
> net/ipv4/ipip.c:ipip_fill_forward_path() always stores an IPv4 rtable:
>
> path->tun.l3_proto = IPPROTO_IPIP;
> path->tun.dst = &rt->dst;
>
> net/ipv6/ip6_tunnel.c:ip6_tnl_fill_forward_path() always stores an IPv6
> rt6_info:
>
> path->tun.l3_proto = IPPROTO_IPV6;
> path->tun.dst = dst;
>
> Neither handler consults the tunnel payload proto, and neither
> nft_dev_path_info() nor nft_dev_forward_path() checks the family before
> storing it in route->tuple[dir].dst.
>
> The consumers cast that dst according to the inner flow family. In
> net/netfilter/nf_flow_table_ip.c:nf_flow_offload_ipv6_hook():
>
> rt = dst_rt6_info(tuplehash->tuple.dst_cache);
> ...
> neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, ip6_daddr));
>
> and in nf_flow_offload_ip_hook():
>
> rt = dst_rtable(tuplehash->tuple.dst_cache);
> ... rt_nexthop(rt, ip_daddr) ...
>
> net/netfilter/nf_flow_table_core.c:flow_offload_dst_cookie() does the same:
>
> if (flow_tuple->l3proto == NFPROTO_IPV6)
> return rt6_get_cookie(dst_rt6_info(flow_tuple->dst_cache));
>
> For an ip6_tunnel in ip4ip6 mode (t->parms.proto == IPPROTO_IPIP) carrying
> IPv4 flows, ip6_tnl_fill_forward_path() still reports l3_proto =
> IPPROTO_IPV6 and hands out an rt6_info, which nf_flow_offload_ip_hook()
> then reads through dst_rtable() and rt_nexthop()/ip_mtu_locked(). That
> configuration needs no misconfiguration to reach.
>
> The mirror case is an IPv6 flow routed out an ipip device: an ipip device
> with mtu 1480 gets an inet6_dev at NETDEV_REGISTER, so fib6_nh_init()
> accepts "ip -6 route add ... dev tunlX", and nft_dev_fill_forward_path()
> deliberately skips the ether-device requirement for tunnels, so
> ipip_fill_forward_path() hands back an rtable that becomes the dst_cache of
> an IPv6 flow. rt6_nexthop() then reads rt6i_flags and rt6i_gateway past
> the end of a struct rtable allocated from the ip_dst_cache slab, and the
> result is used as the IPv6 next hop passed to ip_neigh_gw6().
>
> nf_flow_dst_check() does not catch this either: dst->ops->check() for the
> stored rtable is ipv4_dst_check(), which only tests rt_is_expired().
>
> Would it make sense to validate info.tun.l3_proto (or the dst's
> ops->family) against the flow's l3proto before installing info.tun_dst?
>
> [Severity: Low]
> Should this carry a Fixes: tag?
No, and it is intentional.
I already noted this rare bug while working on this series which
results in an incorrect fastpath in case route goes away while flow
offload entry is being set up, but these tunneling support is recent,
and I really preferred to go for net-next.
That was a deliberate decision. Patches with Fixes: are picked more
quickly into -stable and I did not deem this to be urgent.
> Before this change the tunnel information was only installed when the
> second lookup succeeded:
>
> if (info.num_tuns &&
> !nft_flow_tunnel_update_route(pkt, &info.tun, route, dir)) {
>
> while route->tuple[!dir].in.ifindex, route->tuple[dir].out.ifindex, the
> encaps, h_source/h_dest and xmit_type had already been written and
> nft_dev_forward_path() still returned 0. On nf_route() failure the flow was
> offloaded with the underlay device as output but num_tuns == 0, so inner
> packets left the underlay without IPIP/IP6IP6 encapsulation, which matches
> the "incomplete forward path" described in the commit message.
>
> git blame points that construct at d30301ba4b07ac ("netfilter: flowtable:
> tunnel route update"), with the tunnel forward path support itself from
> ab427db1788581, so the bug is in released kernels rather than in-flight
> code from this series. A Fixes: d30301ba4b07ac tag would help stable
> tracking.
>
> It may also be worth noting in the changelog that this is not standalone
> backportable: neither dev_fill_forward_path_release() nor the
> net_device_path tun.dst field exist before the earlier "net: pass dst via
> net_device_path in dev_fill_forward_path()" commit in this series.
^ permalink raw reply
* RE: [PATCH net-next v2 2/5] ipv6: ndisc: Add ndisc_check_ns_na() validation helper
From: Danielle Ratson @ 2026-08-05 8:06 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: dsahern@kernel.org, Ido Schimmel, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, razor@blackwall.org, ja@ssi.bg, Petr Machata,
fw@strlen.de, kuniyu@google.com, bridge@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260803112505.613873-3-danieller@nvidia.com>
> -----Original Message-----
> From: Danielle Ratson <danieller@nvidia.com>
> Sent: Monday, 3 August 2026 14:25
> To: netdev@vger.kernel.org
> Cc: dsahern@kernel.org; Ido Schimmel <idosch@nvidia.com>;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; horms@kernel.org; razor@blackwall.org; ja@ssi.bg; Petr
> Machata <petrm@nvidia.com>; fw@strlen.de; kuniyu@google.com;
> bridge@lists.linux.dev; linux-kernel@vger.kernel.org; Danielle Ratson
> <danieller@nvidia.com>
> Subject: [PATCH net-next v2 2/5] ipv6: ndisc: Add ndisc_check_ns_na()
> validation helper
>
> Add ndisc_check_ns_na(), a standalone NS/NA packet validator modeled after
> ipv6_mc_check_mld(). It performs the RFC 4861 section 7.1.1 (Neighbor
> Solicitation) and 7.1.2 (Neighbor Advertisement) mandatory checks that are
> relevant for software operating at the bridge level, where packets bypass the
> normal IPv6 stack path:
>
> - Hop Limit must be 255 (packet was not forwarded by a router)
> - ICMPv6 checksum is valid
> - ICMP Code is 0
> - ICMP length is at least 24 octets (sizeof(struct nd_msg))
> - Target Address must not be a multicast address
> - All included options have a length that is greater than zero
> - NS/DAD: destination must be a solicited-node multicast address
> - NS/DAD: no Source Link-Layer Address option when source is unspecified
> - NA: Solicited flag must be 0 when IP Destination is multicast
>
> On success the function sets the skb transport header and returns 0, matching
> the convention of ipv6_mc_check_mld().
>
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
Sashiko wrote:
"
--- Patch [2]: [PATCH net-next v2 2/5] ipv6: ndisc: Add ndisc_check_ns_na() validation helper ---
commit 4031e051060238d9c59321eb3171c00634174182
Author: Danielle Ratson <danieller@nvidia.com>
ipv6: ndisc: Add ndisc_check_ns_na() validation helper
This commit adds ndisc_check_ns_na(), a standalone validator for IPv6
Neighbor Solicitation and Neighbor Advertisement packets. It performs
mandatory RFC 4861 checks for software operating at the bridge level.
> diff --git a/net/ipv6/ndisc_snoop.c b/net/ipv6/ndisc_snoop.c
> new file mode 100644
> index 0000000000000..fa86528d5cfee
> --- /dev/null
> +++ b/net/ipv6/ndisc_snoop.c
> @@ -0,0 +1,190 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/skbuff.h>
> +#include <net/addrconf.h>
> +#include <net/ip6_checksum.h>
> +#include <net/ipv6.h>
> +#include <net/ndisc.h>
> +
> +static int ndisc_check_ip6hdr(struct sk_buff *skb)
> +{
> + const struct ipv6hdr *ip6h;
> + unsigned int offset, len;
> +
> + offset = skb_network_offset(skb) + sizeof(*ip6h);
> + if (!pskb_may_pull(skb, offset))
> + return -EINVAL;
> +
> + ip6h = ipv6_hdr(skb);
> +
> + if (ip6h->version != 6)
> + return -EINVAL;
> +
> + if (ip6h->nexthdr != IPPROTO_ICMPV6)
> + return -ENOMSG;
Does this strict check allow attackers to bypass bridge-level ND security
mechanisms using IPv6 extension headers?
If a spoofed Neighbor Solicitation or Advertisement packet is sent with a
valid IPv6 extension header (like Destination Options), this check will
return -ENOMSG. A bridge would then likely treat the packet as a non-ND
message and forward it normally, bypassing security mechanisms like
ND snooping or anti-spoofing.
The target host's IPv6 stack will traverse the extension headers and
successfully process the malicious ND payload. Should this use
ipv6_skip_exthdr() to properly traverse to the ICMPv6 header instead?
"
The callers only reach this after checking ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6, so a packet with an extension header (nexthdr != ICMPV6) never gets here in the first place.
And if such a packet were ever skipped, it's just not suppressed and floods normally (the default without this feature), so there's no bypass.
^ permalink raw reply
* RE: [PATCH net-next v2 1/5] bridge: Use direct pointer in br_is_nd_neigh_msg()
From: Danielle Ratson @ 2026-08-05 8:06 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: dsahern@kernel.org, Ido Schimmel, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, razor@blackwall.org, ja@ssi.bg, Petr Machata,
fw@strlen.de, kuniyu@google.com, bridge@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260803112505.613873-2-danieller@nvidia.com>
> -----Original Message-----
> From: Danielle Ratson <danieller@nvidia.com>
> Sent: Monday, 3 August 2026 14:25
> To: netdev@vger.kernel.org
> Cc: dsahern@kernel.org; Ido Schimmel <idosch@nvidia.com>;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; horms@kernel.org; razor@blackwall.org; ja@ssi.bg; Petr
> Machata <petrm@nvidia.com>; fw@strlen.de; kuniyu@google.com;
> bridge@lists.linux.dev; linux-kernel@vger.kernel.org; Danielle Ratson
> <danieller@nvidia.com>
> Subject: [PATCH net-next v2 1/5] bridge: Use direct pointer in
> br_is_nd_neigh_msg()
>
> Both callers of br_is_nd_neigh_msg() already call pskb_may_pull() to ensure
> sizeof(struct ipv6hdr) + sizeof(struct nd_msg) bytes are in the linear area
> before invoking this function. The skb_header_pointer() call and its fallback
> buffer are therefore unnecessary.
>
> Replace skb_header_pointer() with a direct cast to ipv6_hdr(skb) + 1 and drop
> the now-unused 'msg' parameter and its corresponding stack buffer from all
> callers.
>
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
Sashiko wrote:
"
> diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
> index 23eb6931a2b4a..db08c3272001f 100644
> --- a/net/bridge/br_arp_nd_proxy.c
> +++ b/net/bridge/br_arp_nd_proxy.c
This is a pre-existing issue, but can br_do_proxy_suppress_arp() also suffer
from an out-of-bounds read when skb_network_offset(skb) > 0?
In br_do_proxy_suppress_arp(), pskb_may_pull() is used to validate
arp_hdr_len(dev) relative to skb->data:
if ((dev->flags & IFF_NOARP) ||
!pskb_may_pull(skb, arp_hdr_len(dev)))
return;
parp = arp_hdr(skb);
Since arp_hdr(skb) resolves to skb_network_header(skb), accessing the arp
payload reads beyond the validated linear area if the network offset is
greater than 0. This could happen if a bpf program at traffic control egress
uses bpf_skb_adjust_room to shift the network header relative to skb->data
before bridge processing.
"
Pre-existing issue, not in the scope.
"
> @@ -234,14 +234,9 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
> #endif
>
> #if IS_ENABLED(CONFIG_IPV6)
> -struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg)
> +struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb)
> {
> - struct nd_msg *m;
> -
> - m = skb_header_pointer(skb, skb_network_offset(skb) +
> - sizeof(struct ipv6hdr), sizeof(*msg), msg);
> - if (!m)
> - return NULL;
> + struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1);
Does this cast introduce an out-of-bounds read if skb_network_offset(skb) > 0?
The callers use pskb_may_pull() to validate the length relative to skb->data.
However, ipv6_hdr(skb) resolves to skb_network_header(skb), which is
skb->data + skb_network_offset(skb).
If a packet is sent through the bridge device where skb_network_offset(skb)
is greater than 0 (such as when a bpf program at traffic control egress
expands the mac header), the bridge's unconditional pull of the ethernet
header leaves skb->data before the network header.
In this situation, the direct pointer cast evaluates to a memory address that
requires:
skb_network_offset(skb) + sizeof(struct ipv6hdr) + sizeof(struct nd_msg)
linear bytes, but pskb_may_pull() only validated sizeof(struct ipv6hdr) +
sizeof(struct nd_msg) relative to skb->data.
”
Patch #3 supersedes the cast, no bug in the assembled series.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox