* [DPDK/ethdev Bug 2082] net/pcap: Rx timestamp overwrites mbuf buf_addr in secondary process
@ 2026-10-27 16:14 bugzilla
0 siblings, 0 replies; only message in thread
From: bugzilla @ 2026-10-27 16:14 UTC (permalink / raw)
To: dev
https://bugs.dpdk.org/show_bug.cgi?id=2082
Bug ID: 2082
Summary: net/pcap: Rx timestamp overwrites mbuf buf_addr in
secondary process
Product: DPDK
Version: 26.11
Hardware: All
OS: All
Status: UNCONFIRMED
Severity: normal
Priority: Normal
Component: ethdev
Assignee: dev@dpdk.org
Reporter: stephen@networkplumber.org
Target Milestone: ---
Created attachment 430
--> https://bugs.dpdk.org/attachment.cgi?id=430&action=edit
AI proposed patch
============================================================================
ANT-2026-80N5ICNL [NOT SECURITY: ordinary bug]
net/pcap: Rx timestamp overwrites mbuf buf_addr in secondary process
Location: drivers/net/pcap/pcap_ethdev.c:423 in eth_pcap_rx()
Commit: 9a45341f18a1ab47af7123e63d942b90c1b79f38
============================================================================
Since v26.07, a DPDK secondary process that receives on a net_pcap port with
the Rx timestamp offload enabled crashes on the first packets it receives. In
the secondary, `eth_pcap_rx()` writes the 64-bit timestamp one byte before each
mbuf instead of into the timestamp dynamic field. This overwrites the low seven
bytes of `buf_addr`, so the next read or write of that mbuf's packet data goes
to `timestamp >> 8`.
The cause is that `timestamp_dynfield_offset` and `timestamp_rx_dynflag` in
`drivers/net/pcap/pcap_ethdev.c` are per-process statics, starting at -1 and 0.
They are only filled in by `eth_dev_configure()`, which runs in the primary.
The `timestamp_offloading` switch, by contrast, is copied into `struct
pcap_rx_queue` in shared `dev_private`. A secondary therefore sees the
primary's `true` and its own offset of -1.
This is a regression from 32fd14bf07f8 ("net/pcap: support nanosecond timestamp
precision"). Before that commit the field was registered in `pmd_pcap_probe()`
in every process. Only v26.07 and 26.11-rc1 are affected; 25.11 and earlier are
not.
The attached reproducer runs a primary `dpdk-testpmd` with
`--enable-rx-timestamp` on an `rx_pcap=` port and an rxonly secondary. The
secondary segfaults on its second Rx burst, writing at the capture's timestamp
`>> 8` plus the 128-byte headroom, in the `rte_memcpy()` at line 392. Without
the offload, the same pair runs cleanly. A live `iface=` port fails the same
way on ordinary traffic. If a capture timestamp happens to decode to a mapped
address, the next frame is silently written there instead of crashing.
Single-process applications and Tx-only secondaries are not affected.
A patch is attached. It moves the offset and flag into `pmd_internals` and the
Rx queue, alongside `timestamp_offloading`.
This is an ordinary regression, not a security issue: it fires on every packet
a secondary receives with the offload on,
whatever the packet contains, so the affected configuration has not worked at
all since v26.07, and nobody who can send
traffic or supply a capture gets anything beyond what the first ordinary packet
already does.
## Details
In a secondary process, `eth_pcap_rx()` stores the Rx timestamp at `(char
*)mbuf - 1` instead of in the timestamp dynamic
field. The 8-byte store replaces the low seven bytes of `mbuf->buf_addr` with
the timestamp.
Shown with two `dpdk-testpmd` processes from the ASan build and a capture file
whose 64 records all carry the timestamp
`0x414141414100 << 8` ns:
```
primary: dpdk-testpmd -l 0-1 --no-pci --proc-type=primary --vdev
net_pcap0,rx_pcap=in.pcap \
-- -i --total-num-mbufs=4096 --enable-rx-timestamp
secondary: dpdk-testpmd -l 0-1 --no-pci --proc-type=secondary -- -i
--forward-mode=rxonly --no-flush-rx
testpmd> start
```
(The reproducer adds `--file-prefix`, `--huge-dir` and, for ASan,
`--base-virtaddr` to both.)
The secondary dies in its second Rx burst with an ASan `SEGV on unknown address
0x414141414180`, a WRITE, in the
`rte_memcpy()` at `drivers/net/pcap/pcap_ethdev.c:392`. That address is the
timestamp shifted right by 8, plus the 128 bytes
of headroom. Without `--enable-rx-timestamp` the same pair receives the 64
frames and exits normally. The default (gcc,
non-ASan) build behaves the same way: the kernel logs `segfault at
414141414180`.
The offset and the flag of the dynamic field are static variables, so every
process has its own copy, and the copy starts
at -1:
```c
/* drivers/net/pcap/pcap_ethdev.c:63 */
static uint64_t timestamp_rx_dynflag;
static int timestamp_dynfield_offset = -1;
```
The only place that fills them in is `eth_dev_configure()`. The port is
configured by the primary; a secondary attaches
to it and never runs this code:
```c
/* drivers/net/pcap/pcap_ethdev.c:1028 */
internals->timestamp_offloading = !!(rxmode->offloads &
RTE_ETH_RX_OFFLOAD_TIMESTAMP);
if (internals->timestamp_offloading && timestamp_rx_dynflag == 0) {
int ret =
rte_mbuf_dyn_rx_timestamp_register(×tamp_dynfield_offset,
×tamp_rx_dynflag);
```
The switch that enables the store is not per process. `eth_rx_queue_setup()`
copies it into the Rx queue
(`pcap_q->timestamp_offloading = internals->timestamp_offloading`, line 1253),
and `struct pcap_rx_queue` is part of
`dev_private`, which is shared memory. The secondary branch of
`pmd_pcap_probe()` (lines 2061-2096) attaches to the port
and installs `eth_pcap_rx` as `rx_pkt_burst` (line 2088) without touching the
two statics. So the Rx function in a
secondary sees the primary's `true` and its own -1:
```c
/* drivers/net/pcap/pcap_ethdev.c:414 */
if (pcap_q->timestamp_offloading) { /*
shared: set by the primary */
…
uint64_t ns = (uint64_t)header->ts.tv_sec *
NSEC_PER_SEC
+ header->ts.tv_usec;
*RTE_MBUF_DYNFIELD(mbuf, timestamp_dynfield_offset, /*
per process: still -1 */
rte_mbuf_timestamp_t *) = ns;
mbuf->ol_flags |= timestamp_rx_dynflag; /*
per process: still 0 */
```
`RTE_MBUF_DYNFIELD(m, offset, type)` is `(type)((uintptr_t)(m) + (offset))`
(`lib/mbuf/rte_mbuf_dyn.h:228`) and `buf_addr`
is the first member of `struct rte_mbuf`. The low byte of `ns` lands in the
last byte of the
`struct rte_mempool_objhdr` in front of the mbuf (the top byte of its `iova`).
The other seven bytes become bytes 0-6 of
`buf_addr`; its top byte is zero already, so `buf_addr == ns >> 8`.
Nothing resets `buf_addr` when a direct mbuf is freed and allocated again (it
is set once, in `rte_pktmbuf_init()`), so the
damage stays with the mbuf in the shared pool. In the example, burst 1 receives
32 frames: each is copied into a good
mbuf at line 392 and then the store at line 423 sets that mbuf's `buf_addr` to
`0x414141414100`. `rxonly` frees the
32 mbufs, burst 2 gets them back from the lcore cache, and the copy at line 392
now writes the next frame to
`0x414141414100 + RTE_PKTMBUF_HEADROOM`. An application that looks at the
packet does not get that far: with
`set verbose 1` in the secondary, testpmd faults on the first burst, reading
the Ethernet header at `0x41414141418c`.
This is a regression from 32fd14bf07f8 ("net/pcap: support nanosecond timestamp
precision"), first released in
v26.07. Before it, `pmd_pcap_probe()` registered the field ahead of the
primary/secondary branch, in every process, so
only v26.07 and 26.11-rc1 are affected; 25.11 and earlier are not.
## Impact
Anyone who receives on a net_pcap port from a secondary process with the Rx
timestamp offload on hits this, on the first
packets, every time: since v26.07 that combination does not work. The
configuration is a supported one
(`Multiprocess aware = Y` in `doc/guides/nics/features/pcap.ini`) but needs two
non-default choices: the primary enables
`RTE_ETH_RX_OFFLOAD_TIMESTAMP` on a net_pcap port, and a secondary process
calls `rte_eth_rx_burst()` on it. Every packet
received that way leaves one mbuf of the shared pool with a wrong `buf_addr`,
whatever the packet contains.
* Live interface (`iface=`, `rx_iface=`): the timestamp is the kernel's
wall-clock time, which today gives a non-canonical
address. The secondary crashes when the application first reads packet data,
or when the driver first reuses an mbuf.
With `iface=lo` and the testpmd pair above, three UDP datagrams sent to
127.0.0.1 end the secondary with a SEGV in the
same `rte_memcpy()`.
* Capture file (`rx_pcap=`): the record timestamp selects the address and the
following frame supplies the bytes. A
capture with ordinary timestamps crashes the secondary the same way (a 2026
timestamp faults at `0x18e2040019a2cd`).
A timestamp that happens to decode to a mapped address gives no crash: the
next frame is silently copied there,
corrupting whatever that memory holds.
A single-process application is not affected (the same capture with the same
offload in one testpmd process is received
with the correct timestamp on all 64 packets), nor is a secondary that only
transmits.
--
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-27 16:14 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 16:14 [DPDK/ethdev Bug 2082] net/pcap: Rx timestamp overwrites mbuf buf_addr in secondary process bugzilla
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.