* Re: [PATCH 0/3] SM8450 IPA support
From: Esteban Urrutia @ 2026-07-20 17:41 UTC (permalink / raw)
To: Konrad Dybcio, Alex Elder, Bjorn Andersson, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Alex Elder
Cc: linux-arm-msm, devicetree, linux-kernel, netdev
In-Reply-To: <9fc68256-6790-4889-b1d3-1802de852d96@oss.qualcomm.com>
On 7/20/26 10:21 AM, Konrad Dybcio wrote:
> OK you meant the qcom,ipa-q6-smem-size property. I can't find what
> it corresponds to, the name of the associated IOMMU stream
> unfortunately doesn't hint at anything useful either..
Perhaps this can help:
https://github.com/LineageOS/android_kernel_qcom_sm8450-modules/blob/lineage-20/qcom/opensource/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa.c#L10950-L11010
Regards,
Esteban
^ permalink raw reply
* Re: [PATCH 1/3] mm: move internal mempolicy APIs to new internal header
From: Gregory Price @ 2026-07-20 17:40 UTC (permalink / raw)
To: Brendan Jackman
Cc: Matthew Wilcox, Brendan Jackman, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Johannes Weiner, Zi Yan,
Jan Kara, Joshua Hahn, Byungchul Park, Ying Huang,
Alistair Popple, Hugh Dickins, Baolin Wang, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Youngjun Park,
Joerg Roedel (AMD), Will Deacon, Robin Murphy, Huacai Chen,
WANG Xuerui, Thomas Gleixner, Chuck Lever, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Trond Myklebust,
Anna Schumaker, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, linux-kernel, linux-mm, linux-fsdevel,
iommu, loongarch, linux-nfs, netdev
In-Reply-To: <DK05KUIDJVLZ.61UIWOL0K3OT@linux.dev>
On Thu, Jul 16, 2026 at 04:57:37PM +0000, Brendan Jackman wrote:
> On Thu Jul 16, 2026 at 4:48 PM UTC, Matthew Wilcox wrote:
> > On Thu, Jul 16, 2026 at 02:30:10PM +0000, Brendan Jackman wrote:
> >> There are no external users for this surface, reduce the scope.
> >> -struct folio *folio_alloc_mpol_noprof(gfp_t gfp, unsigned int order,
> >> - struct mempolicy *mpol, pgoff_t ilx, int nid);
> >
> > Hm. So what we're saying is that allocations which respect mempolicy are
> > only for core mm and not for, eg, device drivers to do. Is that really
> > what we want to say? I don't think so, because that's inconsistent
> > with having just widened __filemap_get_folio_mpol to allow guest_memfd
> > to specify a mempolicy.
>
> Yeah I agree, mempolicy definitely seems like a "public concept". All
> I'm saying here is this specific function doesn't have any external
> users so it doesn't need to be an external header.
>
> ... With the ulterior motive that I want to add a new parameter to it
> that actually _is_ mm-internal. Namely, alloc_flags, so I can add
> ALLOC_UNMAPPED to implement AS_NO_DIRECT_MAP, i.e. the next iteration of
> [0]. So basically this is
> about trying to extend the allocator without creating a GFP flag.
>
> So I'm envisaging if an external user arises for it later, we'd slap two
> underscores on the beginning of the internal one, (with the alloc_flags
> arg), and then bring back the public one as a wrapper.
>
> Does that make sense?
>
> [0]: https://lore.kernel.org/all/20260410151746.61150-1-kalyazin@amazon.com/
>
fwiw i've been fiddling with a module side mempolicy interface for a bit
to allow a clean guest_memfd() + mempolicy() interaction [0][1].
I saw this and thought "maybe, maybe not entirely".
there is definitely some oddities in the mempolicy uapi (internal-only
flags that are exposed to the uapi is odd) that some of the interface is
worth internalizing.
(fyi i've converted my private node work on top of the alloc_flags
addition + new zonelists, new rfc is being posted today, your recent
merges were timely).
~Gregory
[0] https://github.com/gourryinverse/linux/commit/b25bb27602fe512733450a97ac31ef4387b7e727
[1] https://github.com/gourryinverse/linux/commit/670f7dd81ef33a9385985a54b8ad6bc070b2d581
^ permalink raw reply
* Re: [PATCH RESEND v4 net-next 13/14] net: enetc: use alloc_etherdev_mqs() to create netdev for VF driver
From: Joe Damato @ 2026-07-20 17:36 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, linux, wei.fang, chleroy,
maxime.chevallier, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260720014317.1059359-14-wei.fang@oss.nxp.com>
On Mon, Jul 20, 2026 at 09:43:15AM +0800, wei.fang@oss.nxp.com wrote:
> From: Wei Fang <wei.fang@nxp.com>
>
> The VF driver uses alloc_etherdev_mq() with ENETC_MAX_NUM_TXQS as the
> queue count, which forces the TX and RX queue counts to be equal and
> uses a compile-time constant rather than the actual hardware capability.
>
> After enetc_get_si_caps() is called, si->num_tx_rings and
> si->num_rx_rings reflect the actual number of rings assigned to the VF
> by the PF. For the ENETC VF on LS1028A and the upcoming i.MX95/94, their
> SoCs have no more than 6 CPUs, and the number of TX/RX rings allocated
> to the VF is less than 8.
>
> Therefore, switch to alloc_etherdev_mqs() so that the TX and RX queue
> counts are set independently, each capped at ENETC_MAX_NUM_TXQS, based
> on the actual number of rings assigned to the VF by the PF.
>
> Note that if future SoCs have more than 6 CPUs and more than 6 RX rings
> allocated to VFs, the size of the int_vector array in struct
> enetc_ndev_priv will need to be modified. Similarly, if more than 8 TX
> rings are allocated to each int_vector, ENETC_MAX_NUM_TXQS will also
> need to be modified.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> ---
> drivers/net/ethernet/freescale/enetc/enetc_vf.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> index 9cdb0a4d6baf..7dcb4a0246f5 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> @@ -317,7 +317,14 @@ static int enetc_vf_probe(struct pci_dev *pdev,
>
> enetc_get_si_caps(si);
>
> - ndev = alloc_etherdev_mq(sizeof(*priv), ENETC_MAX_NUM_TXQS);
> + /* Currently, the supported SoCs have a max of 6 CPUs and the VFs
> + * have less than 6 RX/TX rings. So no issues for these supported
> + * SoCs, but for future SoCs which have more CPUs or more TX/RX
> + * rings, all the related logic needs to be improved.
> + */
> + ndev = alloc_etherdev_mqs(sizeof(*priv),
> + min(si->num_tx_rings, ENETC_MAX_NUM_TXQS),
> + min(si->num_rx_rings, ENETC_MAX_NUM_TXQS));
Code looks right, but looks almost like a typo. I guess it would read nicer if
ENETC_MAX_NUM_RXQS existed?
That said:
Reviewed-by: Joe Damato <joe@dama.to>
^ permalink raw reply
* Re: [PATCH] vsock: use sock_error() to consume sk_err after connect timeout
From: Phi Nguyen @ 2026-07-20 17:34 UTC (permalink / raw)
To: Stefano Garzarella
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, syzbot+1b2c9c4a0f8708082678, virtualization, netdev,
linux-kernel
In-Reply-To: <al3UuvBhfxp82krV@sgarzare-redhat>
On 7/20/2026 4:17 PM, Stefano Garzarella wrote:
> On Mon, Jul 20, 2026 at 05:57:47AM +0800, Nguyen Dinh Phi wrote:
>> After vsock_connect() exits the wait loop due to sk->sk_err being
>> set, the error was read but not cleared. This left sk->sk_err set
>> for subsequent operations.
>
> So, is this a fix? If yes, we should put a Fixes tag.
>
> Also, can you describe how to trigger the issue?
>
> Because I see this in vsock_connect(), so I thought it was in some way
> already handled:
>
> /* sk_err might have been set as a result of an earlier
> * (failed) connect attempt.
> */
> sk->sk_err = 0;
>
This only handles the case where the function following the failed
connect is another connect() call.
>> Switch to sock_error() which atomically reads and clears sk->sk_err,
>> so the error is consumed when returned.
>>
>> Signed-off-by: Nguyen Dinh Phi <phind.uet@gmail.com>
>> Reported-by: syzbot+1b2c9c4a0f8708082678@syzkaller.appspotmail.com
>
> Can you explain how this patch fixes that issue?
> (this should be the first information to be put in the commit message IMHO)
>
> I'd like to understand better if this is a fix of real bug or just an
> improvement to the code (which is fine by me).
>
> Thanks,
> Stefano
>
Here are the steps of the syzkaller reproducer:
r0 = socket(AF_VSOCK, SOCK_STREAM, 0)
bind(r0, {VMADDR_CID_ANY, PORT})
connect(r0, {VMADDR_CID_LOCAL, PORT})
listen(r0, backlog)
r1 = socket(AF_VSOCK, SOCK_STREAM, 0)
connect(r1, {VMADDR_CID_LOCAL, PORT})
connect(r0 -> self) -> -1, EPROTO
listen(r0) -> 0
connect(r1 -> r0) -> 0
accept(r0) -> -1, EPROTO
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
This patch will prevent the rejection branch to occur in this scenario.
I think we might schedule the cleanup worker to run in the rejection
path for these transports as well.
Thanks,
Phi
^ permalink raw reply
* Re: [PATCH net-next] octeontx2-af: npc: Warn on NPC_IPSEC_SPI key overlap
From: Simon Horman @ 2026-07-20 17:18 UTC (permalink / raw)
To: rkannoth
Cc: Simon Horman, linux-kernel, netdev, andrew+netdev, davem,
edumazet, kuba, pabeni, sgoutham, hkelam
In-Reply-To: <20260713144247.545592-1-rkannoth@marvell.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
https://sashiko.dev/#/patchset/20260713144247.545592-1-rkannoth@marvell.com?part=1
---
--- Patch [1]: [PATCH net-next] octeontx2-af: npc: Warn on NPC_IPSEC_SPI key overlap ---
commit f018da26dc974202fbe56b064f0bc0375c74613a
Author: Hariprasad Kelam <hkelam@marvell.com>
octeontx2-af: npc: Warn on NPC_IPSEC_SPI key overlap
When scanning the MKEX profile to determine supported NPC features, warn
if the SPI extraction field overlaps with other key fields. AH and ESP
may legitimately use the same key offset for SPI, so continue to
advertise NPC_IPSEC_SPI via npc_is_field_present() instead of treating
the overlap as a hard failure.
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
> index 91b5947dae060..b217c1c19810a 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
> @@ -730,6 +730,9 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf)
> *features &= ~BIT_ULL(NPC_OUTER_VID);
>
> /* Allow extracting SPI field from AH and ESP headers at same offset */
> + if (npc_check_overlap(rvu, blkaddr, NPC_IPSEC_SPI, 0, intf))
> + dev_warn(rvu->dev, "Overlap detected the field NPC_IPSEC_SPI\n");
[Severity: Medium]
The commit message notes that AH and ESP may legitimately use the same key
offset for SPI. However, will this new dev_warn() trigger anyway for those
valid configurations?
If an MKEX profile configures AH and ESP SPI extraction at the same
offset, npc_check_overlap() will still see them as mutually exclusive fields
with different lids (LID_LD and LID_LE). Does this result in spurious
warnings polluting the kernel log on valid hardware configurations?
[Severity: High]
This is a pre-existing issue, but does npc_check_overlap() have a data
race when called concurrently?
Looking at npc_check_overlap(), it assigns a local pointer to the shared
scratchpad array and then mutates it:
rvu_npc_fs.c:npc_check_overlap() {
...
dummy = &mcam->rx_key_fields[NPC_UNKNOWN];
...
memset(dummy, 0, sizeof(struct npc_key_field));
npc_set_layer_mdata(rvu, mcam, NPC_UNKNOWN, cfg, lid, lt, intf);
...
}
If multiple VFs simultaneously send mailbox commands that trigger overlap
checks across different CPUs, won't they overwrite this global dummy struct
and corrupt each other's flow validation?
> + /* Set SPI flag only if AH/ESP and IPSEC_SPI are in the key */
> if (npc_is_field_present(rvu, NPC_IPSEC_SPI, intf) &&
> (*features & (BIT_ULL(NPC_IPPROTO_ESP) | BIT_ULL(NPC_IPPROTO_AH))))
> *features |= BIT_ULL(NPC_IPSEC_SPI);
^ permalink raw reply
* [PATCH v6 1/2] bpf, sockmap: handle spurious tcp_msg_wait_data() wakeup
From: Nnamdi Onyeyiri @ 2026-07-20 17:15 UTC (permalink / raw)
To: nnamdio
Cc: bpf, davem, edumazet, horms, jakub, jiayuan.chen, john.fastabend,
kuba, kuniyu, ncardwell, netdev, pabeni, sashiko-reviews,
linux-kernel
In-Reply-To: <20260720171535.67867-1-nnamdio@gmail.com>
recvfrom()/recv() are documented as only returning EAGAIN for blocking sockets
when they have a receive timeout configured. However, adding a blocking
ipv4 tcp socket without a receive timeout to a sockmap will cause EAGAIN errors
sporadically. A socket with a receive timeout may return EAGAIN before the
timeout expires.
There are 2 code paths affected by this:
1. tcp_bpf_recvmsg() - Used when the socket has been added to a sockmap
that has no verdict program attached.
2. tcp_bpf_recvmsg_parser() - Used when the socket has been added to a
sockmap that has a verdict program. To reproduce this issue, it is
enough for the verdict program to do nothing but return SK_PASS.
In both cases this happens when tcp_msg_wait_data() wakes spuriously
(returning 0). To fix it, we now loop back to msg_bytes_ready instead
of returning -EAGAIN on spurious wakeup.
To ensure the looping does not cause sockets with a SO_RCVTIMEO set to
wait excessively long, tcp_msg_wait_data() now takes a pointer to timeo,
allowing sk_wait_event() to update it as appropriate.
The logic in tcp_bpf_recvmsg_parser() that allow it to handle signals,
socket errors and closuers in its loop was also added to tcp_bpf_recvmsg().
Signed-off-by: Nnamdi Onyeyiri <nnamdio@gmail.com>
---
net/ipv4/tcp_bpf.c | 69 ++++++++++++++++++++++++++++++++++++++++------
1 file changed, 60 insertions(+), 9 deletions(-)
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index cc0bd73f36b6..aa5c5d741599 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -179,7 +179,7 @@ EXPORT_SYMBOL_GPL(tcp_bpf_sendmsg_redir);
#ifdef CONFIG_BPF_SYSCALL
static int tcp_msg_wait_data(struct sock *sk, struct sk_psock *psock,
- long timeo)
+ long *timeo)
{
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int ret = 0;
@@ -187,12 +187,12 @@ static int tcp_msg_wait_data(struct sock *sk, struct sk_psock *psock,
if (sk->sk_shutdown & RCV_SHUTDOWN)
return 1;
- if (!timeo)
+ if (!*timeo)
return ret;
add_wait_queue(sk_sleep(sk), &wait);
sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
- ret = sk_wait_event(sk, &timeo,
+ ret = sk_wait_event(sk, timeo,
!list_empty(&psock->ingress_msg) ||
!skb_queue_empty_lockless(&sk->sk_receive_queue), &wait);
sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
@@ -229,6 +229,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
int copied_from_self = 0;
int copied = 0;
u32 seq;
+ long timeo;
if (unlikely(flags & MSG_ERRQUEUE))
return inet_recv_error(sk, msg, len);
@@ -262,6 +263,8 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
}
}
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
msg_bytes_ready:
copied = __sk_msg_recvmsg(sk, psock, msg, len, flags, &copied_from_self);
/* The typical case for EFAULT is the socket was gracefully
@@ -280,7 +283,6 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
}
seq += copied_from_self;
if (!copied) {
- long timeo;
int data;
if (sock_flag(sk, SOCK_DONE))
@@ -299,7 +301,6 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
goto out;
}
- timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
if (!timeo) {
copied = -EAGAIN;
goto out;
@@ -310,13 +311,15 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
goto out;
}
- data = tcp_msg_wait_data(sk, psock, timeo);
+ data = tcp_msg_wait_data(sk, psock, &timeo);
if (data < 0) {
copied = data;
goto unlock;
}
if (data && !sk_psock_queue_empty(psock))
goto msg_bytes_ready;
+ if (!data && timeo > 0)
+ goto msg_bytes_ready;
copied = -EAGAIN;
}
out:
@@ -355,6 +358,7 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
{
struct sk_psock *psock;
int copied, ret;
+ long timeo;
if (unlikely(flags & MSG_ERRQUEUE))
return inet_recv_error(sk, msg, len);
@@ -371,14 +375,59 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
return tcp_recvmsg(sk, msg, len, flags);
}
lock_sock(sk);
+
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
msg_bytes_ready:
copied = sk_msg_recvmsg(sk, psock, msg, len, flags);
if (!copied) {
- long timeo;
int data;
- timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
- data = tcp_msg_wait_data(sk, psock, timeo);
+ if (sock_flag(sk, SOCK_DONE)) {
+ ret = 0;
+ goto unlock;
+ }
+
+ if (sk->sk_err) {
+ if (!sk_psock_queue_empty(psock))
+ goto msg_bytes_ready;
+ if (!skb_queue_empty(&sk->sk_receive_queue)) {
+ release_sock(sk);
+ sk_psock_put(sk, psock);
+ return tcp_recvmsg(sk, msg, len, flags);
+ }
+ ret = sock_error(sk);
+ goto unlock;
+ }
+
+ if (sk->sk_shutdown & RCV_SHUTDOWN) {
+ if (!sk_psock_queue_empty(psock))
+ goto msg_bytes_ready;
+ if (!skb_queue_empty(&sk->sk_receive_queue)) {
+ release_sock(sk);
+ sk_psock_put(sk, psock);
+ return tcp_recvmsg(sk, msg, len, flags);
+ }
+ ret = 0;
+ goto unlock;
+ }
+
+ if (sk->sk_state == TCP_CLOSE) {
+ ret = -ENOTCONN;
+ goto unlock;
+ }
+
+ if (!timeo) {
+ ret = -EAGAIN;
+ goto unlock;
+ }
+
+ if (signal_pending(current)) {
+ ret = sock_intr_errno(timeo);
+ goto unlock;
+ }
+
+ data = tcp_msg_wait_data(sk, psock, &timeo);
if (data < 0) {
ret = data;
goto unlock;
@@ -390,6 +439,8 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
sk_psock_put(sk, psock);
return tcp_recvmsg(sk, msg, len, flags);
}
+ if (!data && timeo > 0)
+ goto msg_bytes_ready;
copied = -EAGAIN;
}
ret = copied;
--
2.52.0
^ permalink raw reply related
* [PATCH v6 2/2] selftests/bpf: add sockmap recvfrom EAGAIN selftest
From: Nnamdi Onyeyiri @ 2026-07-20 17:15 UTC (permalink / raw)
To: nnamdio
Cc: bpf, davem, edumazet, horms, jakub, jiayuan.chen, john.fastabend,
kuba, kuniyu, ncardwell, netdev, pabeni, sashiko-reviews,
linux-kernel
In-Reply-To: <20260720171535.67867-1-nnamdio@gmail.com>
These selftests exercise the tcp_bpf_recvmsg() and tcp_bpf_recvmsg_parser()
functions, to ensure that they are properly handling spurious wakeups in
tcp_msg_wait_data().
The expected behaviour is that recvfrom() does not return an EAGAIN
error. If the spurious wakeups are incorrectly handled, this assertion
will fail.
Signed-off-by: Nnamdi Onyeyiri <nnamdio@gmail.com>
---
.../selftests/bpf/prog_tests/sockmap_basic.c | 124 ++++++++++++++++++
1 file changed, 124 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
index cb3229711f93..d18faf46fac0 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -1373,6 +1373,126 @@ static void test_sockmap_multi_channels(int sotype)
test_sockmap_pass_prog__destroy(skel);
}
+static void *test_sockmap_recvfrom_eagain_thread(void *arg)
+{
+ int fd = *(int *)arg;
+ char buf[1024];
+ void *result = NULL;
+
+ while (true) {
+ ssize_t len = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
+
+ if (len == -1) {
+ if (errno == EINTR)
+ continue;
+ result = (void *)1;
+ break;
+ }
+
+ if (!len || buf[len - 1] == 'e')
+ break;
+ }
+
+ send(fd, "test", 4, MSG_NOSIGNAL);
+
+ close(fd);
+
+ return result;
+}
+
+static void test_sockmap_recvfrom_eagain(bool with_verdict)
+{
+ struct test_sockmap_pass_prog *skel = NULL;
+ struct bpf_program *prog = NULL;
+ size_t buflen = 1024 * 1024 * 25;
+ char *buf = NULL;
+ int map, err;
+
+ skel = test_sockmap_pass_prog__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ map = bpf_map__fd(skel->maps.sock_map_msg);
+
+ if (with_verdict) {
+ prog = skel->progs.prog_skb_verdict;
+ err = bpf_prog_attach(bpf_program__fd(prog), map, BPF_SK_SKB_STREAM_VERDICT, 0);
+ if (!ASSERT_OK(err, "bpf_prog_attach verdict"))
+ goto cleanup;
+ }
+
+ buf = malloc(buflen);
+ if (!ASSERT_OK_PTR(buf, "malloc buf"))
+ goto cleanup;
+ memset(buf, 0, buflen);
+ buf[buflen - 1] = 'e';
+
+ for (int i = 0; i < 200; ++i) {
+ ssize_t sent;
+ char ignored[128];
+ pthread_t thread;
+ bool thread_created = false;
+ size_t rem = buflen;
+ int c = -1, p = -1, zero = 0;
+ bool success = false;
+
+ err = create_pair(AF_INET, SOCK_STREAM, &c, &p);
+ if (!ASSERT_OK(err, "create_pair"))
+ goto end_attempt;
+
+ err = pthread_create(&thread, NULL, &test_sockmap_recvfrom_eagain_thread, &p);
+ if (!ASSERT_OK(err, "pthread_create"))
+ goto end_attempt;
+ thread_created = true;
+
+ err = bpf_map_update_elem(map, &zero, &c, BPF_ANY);
+ if (!ASSERT_OK(err, "bpf_map_update_elem"))
+ goto end_attempt;
+
+ while (rem) {
+ sent = xsend(c, buf + (buflen - rem), rem, 0);
+ if (sent == -1)
+ goto end_attempt;
+ rem -= sent;
+ }
+
+ /* we cannot use recv_timeout(), otherwise EAGAIN would be an expected errno. */
+ err = recvfrom(c, ignored, sizeof(ignored), 0, NULL, NULL);
+
+ /*
+ * we are checking for the invalid return of EAGAIN, any other return is considered
+ * successful for the purposes of this test.
+ */
+ if (err < 0 && !ASSERT_NEQ(errno, EAGAIN, "recvfrom eagain"))
+ goto end_attempt;
+
+ success = true;
+
+end_attempt:
+ if (c >= 0)
+ close(c);
+
+ if (thread_created) {
+ void *retval = NULL;
+
+ pthread_join(thread, &retval);
+ if (!ASSERT_NULL(retval, "retval"))
+ success = false;
+ }
+
+ if (!thread_created && p >= 0)
+ close(p);
+ if (!success)
+ break;
+ }
+
+cleanup:
+ if (buf)
+ free(buf);
+
+ test_sockmap_pass_prog__destroy(skel);
+}
+
void test_sockmap_basic(void)
{
if (test__start_subtest("sockmap create_update_free"))
@@ -1451,4 +1571,8 @@ void test_sockmap_basic(void)
test_sockmap_multi_channels(SOCK_STREAM);
if (test__start_subtest("sockmap udp multi channels"))
test_sockmap_multi_channels(SOCK_DGRAM);
+ if (test__start_subtest("sockmap recvfrom eagain"))
+ test_sockmap_recvfrom_eagain(false);
+ if (test__start_subtest("sockmap recvfrom eagain with verdict"))
+ test_sockmap_recvfrom_eagain(true);
}
--
2.52.0
^ permalink raw reply related
* [PATCH v6 0/2] bpf, sockmap: handle spurious tcp_msg_wait_data() wakeup
From: Nnamdi Onyeyiri @ 2026-07-20 17:15 UTC (permalink / raw)
To: nnamdio
Cc: bpf, davem, edumazet, horms, jakub, jiayuan.chen, john.fastabend,
kuba, kuniyu, ncardwell, netdev, pabeni, sashiko-reviews,
linux-kernel
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 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 | 69 ++++++++--
.../selftests/bpf/prog_tests/sockmap_basic.c | 124 ++++++++++++++++++
2 files changed, 184 insertions(+), 9 deletions(-)
--
2.52.0
^ permalink raw reply
* [PATCH net] net/smc: validate peer CDC cursor against RMBE size before accepting it
From: Ibrahim Hashimov @ 2026-07-20 17:07 UTC (permalink / raw)
To: alibuda, dust.li, wenjia, davem, edumazet, kuba, pabeni
Cc: tonylu, guwen, horms, linux-rdma, linux-s390, netdev,
linux-kernel, stable
smc_cdc_cursor_to_host() converts the wire-format producer/consumer
cursor of an incoming CDC message into a host smc_host_cursor. It rejects
a cursor that goes backwards, but never checks that the cursor's count is
inside the buffer it indexes. Per smc_host_cursor ("an offset in an
RMBE") and the invariant smc_curs_add() enforces for every local advance
(0 <= count < size), a valid cursor count must be < size; the wire cursor
is peer-controlled and was never checked against that.
smcr_cdc_msg_to_host() accepts the producer and consumer cursors, and the
unbounded value feeds smc_curs_diff() in smc_cdc_msg_recv_action(), which
computes new->count - old->count without clamping against size. A peer
that puts an out-of-range prod.count on the wire (e.g. 0x7fffffff) drives
bytes_to_rcv far past rmb_desc->len -- the very invariant the comment
above the atomic_add() claims but does not enforce.
smc_rx_recvmsg() then trusts bytes_to_rcv as the amount of valid RMB
data. Its first copy chunk is safely bounded by rmb_desc->len -
cons.count, but the second chunk copies (copylen - chunk_len) bytes from
offset 0, and copylen came from the inflated readable -- an out-of-bounds
read of the RMB's backing (v)malloc allocation, copied straight to the
receiving process via _copy_to_iter(). This is a remote kernel-memory
disclosure driven by a malicious SMC-R peer, needing no local privilege
on the victim. The same unbounded count also reaches
smc_cdc_handle_urg_data_arrival() (base + urg_curs.count - 1) -- a
second, narrower OOB read.
Fix it where the wire cursor is accepted, mirroring the cursor-sanity
idiom two lines above: reject any incoming cursor whose count is >= the
size of the buffer it will index. smc_cdc_cursor_to_host() gains a size
parameter; smcr_cdc_msg_to_host() passes conn->rmb_desc->len for the
producer cursor and conn->peer_rmbe_size for the consumer cursor. This
bounds it at its single origin instead of patching every downstream
consumer. SMC-D (smcd_cdc_msg_to_host()) does not use this helper and is
a separate, out-of-scope gap.
Reproduced on a v6.19 KASAN kernel (SMC-Rv2 over soft-RoCE): a peer
emitting prod.count=0x7fffffff triggers a vmalloc-out-of-bounds read in
_copy_to_iter() via smc_rx_recvmsg(), returning ~960 KB of RMB-adjacent
kernel heap to userspace; an in-range cursor transfers cleanly. With the
fix the out-of-range cursor is dropped before it reaches
conn->local_rx_ctrl.prod, and the reproducer returns rc=0 with no report.
Fixes: 5f08318f617b ("smc: connection data control (CDC)")
Cc: stable@vger.kernel.org
Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
Assisted-by: AuditCode-AI:2026.07
---
net/smc/smc_cdc.h | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/net/smc/smc_cdc.h b/net/smc/smc_cdc.h
index 696cc11f2303..f07bbef47073 100644
--- a/net/smc/smc_cdc.h
+++ b/net/smc/smc_cdc.h
@@ -221,6 +221,7 @@ static inline void smc_host_msg_to_cdc(struct smc_cdc_msg *peer,
static inline void smc_cdc_cursor_to_host(union smc_host_cursor *local,
union smc_cdc_cursor *peer,
+ unsigned int size,
struct smc_connection *conn)
{
union smc_host_cursor temp, old;
@@ -235,6 +236,14 @@ static inline void smc_cdc_cursor_to_host(union smc_host_cursor *local,
if ((old.wrap == temp.wrap) &&
(old.count > temp.count))
return;
+ /* count is an offset into the RMBE and must always stay inside
+ * it (see smc_curs_add()); the peer is untrusted, so reject an
+ * out-of-range wire cursor the same way an out-of-order one is
+ * already rejected above, instead of letting it drive
+ * bytes_to_rcv / the urgent-byte offset past the buffer end
+ */
+ if (temp.count >= size)
+ return;
smc_curs_copy(local, &temp, conn);
}
@@ -246,8 +255,10 @@ static inline void smcr_cdc_msg_to_host(struct smc_host_cdc_msg *local,
local->len = peer->len;
local->seqno = ntohs(peer->seqno);
local->token = ntohl(peer->token);
- smc_cdc_cursor_to_host(&local->prod, &peer->prod, conn);
- smc_cdc_cursor_to_host(&local->cons, &peer->cons, conn);
+ smc_cdc_cursor_to_host(&local->prod, &peer->prod,
+ conn->rmb_desc->len, conn);
+ smc_cdc_cursor_to_host(&local->cons, &peer->cons,
+ conn->peer_rmbe_size, conn);
local->prod_flags = peer->prod_flags;
local->conn_state_flags = peer->conn_state_flags;
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* Re: [PATCH 4/8] net: bcmgenet: use platform_device_set_fwnode()
From: Florian Fainelli @ 2026-07-20 16:57 UTC (permalink / raw)
To: Andrew Lunn, Bartosz Golaszewski
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Broadcom internal kernel review list, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Lee Jones, Sebastian Hesselbarth,
Srinivas Kandagatla, brgl, driver-core, linuxppc-dev,
linux-kernel, linux-i2c, iommu, netdev, linux-pm, imx,
linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <ae9da5dd-3528-4270-ada0-d17ed14478e0@lunn.ch>
[-- Attachment #1: Type: text/plain, Size: 668 bytes --]
On 7/20/26 07:38, Andrew Lunn wrote:
> On Mon, Jul 20, 2026 at 11:24:51AM +0200, Bartosz Golaszewski wrote:
>> Prefer the higher-level platform_device_set_fwnode() over the
>> OF-specific platform_device_set_of_node() for dynamically allocated
>> platform devices.
>
> Why?
>
> This driver is OF only. It does not support ACPI, and probably never
> will. In general, networking and ACPI don't go together, ACPI is not
> sufficiently advanced.
>
> What is you use case here?
The driver has been, or was used on ACPI-based platforms:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=99c6b06a37d4cab118c45448fef9d28df62d35d8
--
Florian
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5485 bytes --]
^ permalink raw reply
* Re: [PATCH net 2/2] selftests/net: packetdrill: cover RST validation in SYN-RECEIVED
From: Eric Dumazet @ 2026-07-20 16:48 UTC (permalink / raw)
To: Yuxiang Yang
Cc: netdev, linux-kernel, linux-kselftest, Neal Cardwell,
Kuniyuki Iwashima, David S . Miller, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan, Yizhou Zhao, Ao Wang, Xuewei Feng,
Qi Li, Ke Xu, yyxroy22
In-Reply-To: <20260717081443.809393-3-yangyx22@mails.tsinghua.edu.cn>
On Fri, Jul 17, 2026 at 10:15 AM Yuxiang Yang
<yangyx22@mails.tsinghua.edu.cn> wrote:
>
> Add packetdrill coverage for the RFC 9293 reset checks on request
> sockets in SYN-RECEIVED. Verify that an exact RST removes the request,
> a non-exact in-window RST sends a challenge ACK without removing it,
> and an out-of-window RST is silently discarded.
>
> Also cover an RST|ACK with an unacceptable ACK number to ensure RST
> sequence validation runs before ACK-field validation.
>
> Signed-off-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Thanks!
^ permalink raw reply
* Re: [PATCH net 1/2] tcp: challenge ACK for non-exact RST in SYN-RECEIVED
From: Eric Dumazet @ 2026-07-20 16:45 UTC (permalink / raw)
To: Yuxiang Yang
Cc: netdev, linux-kernel, linux-kselftest, Neal Cardwell,
Kuniyuki Iwashima, David S . Miller, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan, Yizhou Zhao, Ao Wang, Xuewei Feng,
Qi Li, Ke Xu, stable, yyxroy22
In-Reply-To: <20260717081443.809393-2-yangyx22@mails.tsinghua.edu.cn>
On Fri, Jul 17, 2026 at 10:15 AM Yuxiang Yang
<yangyx22@mails.tsinghua.edu.cn> wrote:
>
> The SYN-RECEIVED request-socket path in tcp_check_req() accepts an
> in-window RST without requiring SEG.SEQ to exactly match RCV.NXT. A
> non-exact RST therefore removes the request instead of eliciting a
> challenge ACK.
>
> RFC 9293 section 3.10.7.4 applies the RFC 5961 reset check in
> SYN-RECEIVED: an exact RST resets the connection, while a non-exact
> in-window RST must trigger a challenge ACK and be dropped.
>
> Apply that check before the ACK-field validation, following the RFC
> sequence-number, RST, then ACK processing order. Factor the per-netns
> challenge ACK quota out of tcp_send_challenge_ack() so request sockets
> can share it. Use the request socket's send_ack() callback and its own
> out-of-window ACK timestamp to send and rate-limit the response.
>
> Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
> Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
> Reported-by: Ao Wang <wangao@seu.edu.cn>
> Reported-by: Xuewei Feng <fengxw06@126.com>
> Reported-by: Qi Li <qli01@tsinghua.edu.cn>
> Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
> Fixes: 282f23c6ee34 ("tcp: implement RFC 5961 3.2")
> Cc: stable@vger.kernel.org
> Signed-off-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Note : Please do not add a Reported-by: Yuxiang Yang
<yangyx22@mails.tsinghua.edu.cn> if you are the patch author.
The amount of noise in this patch footer is quite annoying.
Reviewed-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [PATCH net v3] vmxnet3: fix BUG_ON in vmxnet3_get_hdr_len() for Geneve packets
From: Simon Horman @ 2026-07-20 16:41 UTC (permalink / raw)
To: Harshaka Narayana
Cc: davem, kuba, pabeni, netdev, ronak.doshi,
bcm-kernel-feedback-list, andrew+netdev, edumazet, linux-kernel,
guolin.yang, sankararaman.jayaraman
In-Reply-To: <20260713140915.3381715-1-harshaka.narayana@broadcom.com>
On Mon, Jul 13, 2026 at 07:09:15AM -0700, Harshaka Narayana wrote:
> vmxnet3_get_hdr_len() assumes gdesc->rcd.v4/v6/tcp always describe the
> outer header, but for a Geneve-encapsulated packet the device can set
> them based on the inner header instead, signalled by the
> VMXNET3_RCD_HDR_INNER_SHIFT bit in the completion descriptor. Since the
> function never skips the outer encapsulation, this mismatch triggers:
>
> - BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP), because the outer
> protocol is UDP (Geneve), not TCP.
> - BUG_ON(hdr.eth->h_proto != ...), when the tunnel's outer and inner
> IP versions differ (e.g. outer IPv6/inner IPv4 or vice versa).
>
> Check VMXNET3_RCD_HDR_INNER_SHIFT up front and bail out, since the
> function cannot locate the inner header it would need to parse. Also
> convert the remaining BUG_ON()s in this function to return 0
> defensively.
>
> Fixes: 45dac1d6ea04 ("vmxnet3: Changes for vmxnet3 adapter version 2 (fwd)")
> Signed-off-by: Harshaka Narayana <harshaka.narayana@broadcom.com>
> Reviewed-by: Ronak Doshi <ronak.doshi@broadcom.com>
> Reviewed-by: Sankararaman Jayaraman <sankararaman.jayaraman@broadcom.com>
> ---
> v3:
> - Combined the two early return-0 checks into one condition
> - Replaced stacked Signed-off-by tags with Reviewed-by for Ronak
> Doshi and Sankararaman Jayaraman
> v2: https://lore.kernel.org/netdev/20260709201654.4108084-1-harshaka.narayana@broadcom.com/
> - Check VMXNET3_RCD_HDR_INNER_SHIFT up front to catch the Geneve
> inner-header case directly, and convert the remaining
> BUG_ON(hdr.eth->h_proto != ...) checks to return 0
> - Reworded commit message to describe the root cause via
> VMXNET3_RCD_HDR_INNER_SHIFT
> v1: https://lore.kernel.org/netdev/20260707165248.1859188-1-harshaka.narayana@broadcom.com/
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [PATCH net-next v5 00/15][pull request] Introduce iXD driver
From: Larysa Zaremba @ 2026-07-20 16:24 UTC (permalink / raw)
To: Tony Nguyen
Cc: davem, kuba, pabeni, edumazet, andrew+netdev, netdev,
przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
madhu.chittim, joshua.a.hay, jacob.e.keller,
jayaprakash.shanmugam, jiri, horms, corbet, richardcochran, skhan,
linux-doc
In-Reply-To: <20260715180042.1972010-1-anthony.l.nguyen@intel.com>
I have looked though Sashiko feedback for this version. A lot medium+ comments
were things that I have addressed as false-positives in v4, so those are left
out, same as many of low-severity subjective issues. Unfortunately, patch 4 does
need a fixup (see Re: for the patch itself), but hopefully this does not warrant
another PR.
^ permalink raw reply
* [PATCH net-next v2 7/7] selftests: net: getsockopt_iter: cover rawv6, ieee802154, phonet and tls
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team, Rémi Denis-Courmont
In-Reply-To: <20260720-getsockopt_phase4-v2-0-8a08fcfa0d72@debian.org>
Add fixtures for the newly converted getsockopt leaves:
- rawv6: IPV6_HDRINCL / IPV6_CHECKSUM int paths + a SOL_RAW
unknown-optname case that reaches do_rawv6_getsockopt().
- ieee802154: WPAN_WANTACK dgram int path + non-SOL_IEEE802154 level
rejection.
- phonet: PNPIPE_ENCAP pep int path + non-SOL_PNPIPE level
rejection.
- tls: TLS_TX_ZEROCOPY_RO, the TLS_TX crypto_info round-trip at
the base and full cipher sizes, the NULL-optval and short
buffer EINVAL paths, and an unknown optname. It skips when
the kernel lacks TLS or AES-GCM.
Each fixture pins the returned-length / errno semantics across exact,
oversized and short buffers, an unknown optname and a bogus level. The
semantics are unchanged by the sockopt_t conversion, so the tests pass
both before and after the leaf conversions.
Signed-off-by: Breno Leitao <leitao@debian.org>
Acked-by: Rémi Denis-Courmont <remi@remlab.net>
---
tools/testing/selftests/net/getsockopt_iter.c | 424 ++++++++++++++++++++++++++
1 file changed, 424 insertions(+)
diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c
index fe5a5268bc34e..974065a23fa82 100644
--- a/tools/testing/selftests/net/getsockopt_iter.c
+++ b/tools/testing/selftests/net/getsockopt_iter.c
@@ -28,7 +28,10 @@
#include <linux/vm_sockets.h>
#include <linux/icmp.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
#include <sys/socket.h>
+#include <linux/tls.h>
#include "kselftest_harness.h"
#ifndef AF_VSOCK
@@ -40,6 +43,45 @@
#ifndef ICMP_FILTER
#define ICMP_FILTER 1
#endif
+#ifndef IPV6_HDRINCL
+#define IPV6_HDRINCL 36
+#endif
+#ifndef IPV6_CHECKSUM
+#define IPV6_CHECKSUM 7
+#endif
+#ifndef AF_IEEE802154
+#define AF_IEEE802154 36
+#endif
+#ifndef SOL_IEEE802154
+#define SOL_IEEE802154 0
+#endif
+#ifndef WPAN_WANTACK
+#define WPAN_WANTACK 0
+#endif
+#ifndef AF_PHONET
+#define AF_PHONET 35
+#endif
+#ifndef SOL_PNPIPE
+#define SOL_PNPIPE 275
+#endif
+#ifndef PN_PROTO_PIPE
+#define PN_PROTO_PIPE 2
+#endif
+#ifndef PNPIPE_ENCAP
+#define PNPIPE_ENCAP 1
+#endif
+#ifndef PNPIPE_ENCAP_NONE
+#define PNPIPE_ENCAP_NONE 0
+#endif
+#ifndef PNPIPE_ENCAP_IP
+#define PNPIPE_ENCAP_IP 1
+#endif
+#ifndef SOL_TLS
+#define SOL_TLS 282
+#endif
+#ifndef TCP_ULP
+#define TCP_ULP 31
+#endif
/* ---------- netlink ---------- */
@@ -394,4 +436,386 @@ TEST_F(raw, bad_optname)
ASSERT_EQ(sizeof(val), optlen);
}
+/* ---------- raw (ipv6) ---------- */
+
+FIXTURE(rawv6)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(rawv6)
+{
+ self->fd = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
+ if (self->fd < 0)
+ SKIP(return, "SOCK_RAW/IPv6 socket: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(rawv6)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(rawv6, hdrincl_exact)
+{
+ socklen_t optlen;
+ int val = -1;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == 0 || val == 1);
+}
+
+TEST_F(rawv6, hdrincl_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+/* Raw int options clamp the reported length down to the user buffer
+ * instead of returning EINVAL on a short buffer.
+ */
+TEST_F(rawv6, hdrincl_undersize_clamped)
+{
+ socklen_t optlen = 2;
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
+ &val, &optlen));
+ ASSERT_EQ(2, optlen);
+}
+
+TEST_F(rawv6, checksum_default)
+{
+ socklen_t optlen;
+ int val = 0;
+
+ optlen = sizeof(val);
+
+ /* A non-ICMPv6 raw socket has the checksum disabled, reported as -1. */
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_CHECKSUM,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_EQ(-1, val);
+}
+
+TEST_F(rawv6, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ /* SOL_RAW reaches do_rawv6_getsockopt() directly. */
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* ---------- ieee802154 (dgram) ---------- */
+
+FIXTURE(ieee802154)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(ieee802154)
+{
+ self->fd = socket(AF_IEEE802154, SOCK_DGRAM, 0);
+ if (self->fd < 0)
+ SKIP(return, "AF_IEEE802154 dgram socket: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(ieee802154)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(ieee802154, wantack_exact)
+{
+ socklen_t optlen;
+ int val = -1;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IEEE802154, WPAN_WANTACK,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == 0 || val == 1);
+}
+
+TEST_F(ieee802154, wantack_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IEEE802154, WPAN_WANTACK,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+TEST_F(ieee802154, wantack_undersize_clamped)
+{
+ socklen_t optlen = 2;
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IEEE802154, WPAN_WANTACK,
+ &val, &optlen));
+ ASSERT_EQ(2, optlen);
+}
+
+TEST_F(ieee802154, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IEEE802154, 0x7fff,
+ &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* dgram_getsockopt() rejects any level other than SOL_IEEE802154. */
+TEST_F(ieee802154, bad_level)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, WPAN_WANTACK,
+ &val, &optlen));
+ ASSERT_EQ(EOPNOTSUPP, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* ---------- phonet (pep) ---------- */
+
+FIXTURE(phonet)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(phonet)
+{
+ self->fd = socket(AF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
+ if (self->fd < 0)
+ SKIP(return, "AF_PHONET pipe socket: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(phonet)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(phonet, encap_exact)
+{
+ socklen_t optlen;
+ int val = -1;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_PNPIPE, PNPIPE_ENCAP,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == PNPIPE_ENCAP_NONE || val == PNPIPE_ENCAP_IP);
+}
+
+TEST_F(phonet, encap_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_PNPIPE, PNPIPE_ENCAP,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+/* pep clamps the reported length down to the user buffer. Use an
+ * int-sized backing buffer with a short optlen so the baseline kernel,
+ * which writes a full int via put_user(), does not scribble past it.
+ */
+TEST_F(phonet, encap_undersize_clamped)
+{
+ socklen_t optlen = 2;
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_PNPIPE, PNPIPE_ENCAP,
+ &val, &optlen));
+ ASSERT_EQ(2, optlen);
+}
+
+TEST_F(phonet, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_PNPIPE, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* pep_getsockopt() rejects any level other than SOL_PNPIPE. */
+TEST_F(phonet, bad_level)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, PNPIPE_ENCAP, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* ---------- tls ---------- */
+
+FIXTURE(tls)
+{
+ int fd;
+ int sfd;
+};
+
+FIXTURE_SETUP(tls)
+{
+ struct sockaddr_in a = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ socklen_t alen = sizeof(a);
+ int lfd;
+
+ self->fd = -1;
+ self->sfd = -1;
+
+ lfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (lfd < 0)
+ SKIP(return, "TCP socket: %s", strerror(errno));
+ if (bind(lfd, (struct sockaddr *)&a, sizeof(a)) || listen(lfd, 1) ||
+ getsockname(lfd, (struct sockaddr *)&a, &alen)) {
+ close(lfd);
+ SKIP(return, "listener setup: %s", strerror(errno));
+ }
+ self->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (connect(self->fd, (struct sockaddr *)&a, sizeof(a))) {
+ close(lfd);
+ SKIP(return, "connect: %s", strerror(errno));
+ }
+ self->sfd = accept(lfd, NULL, NULL);
+ close(lfd);
+ if (setsockopt(self->fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")))
+ SKIP(return, "TCP_ULP=tls: %s (built without TLS?)",
+ strerror(errno));
+}
+
+FIXTURE_TEARDOWN(tls)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+ if (self->sfd >= 0)
+ close(self->sfd);
+}
+
+/* do_tls_getsockopt_tx_zc(): fixed-size int, exact length required. */
+TEST_F(tls, tx_zerocopy_exact)
+{
+ socklen_t optlen = sizeof(int);
+ int val = -1;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == 0 || val == 1);
+}
+
+TEST_F(tls, tx_zerocopy_wrong_len)
+{
+ socklen_t optlen = 2;
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO,
+ &val, &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+/* do_tls_getsockopt_conf(): NULL optval still yields EINVAL -- the
+ * converted code tests opt->iter_out.ubuf in place of optval.
+ */
+TEST_F(tls, conf_null_optval)
+{
+ socklen_t optlen = 64;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, NULL, &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST_F(tls, conf_short)
+{
+ socklen_t optlen = 2;
+ char buf[2];
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, buf, &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+/* TLS_TX before crypto is set reports not-ready. */
+TEST_F(tls, conf_not_ready)
+{
+ struct tls_crypto_info info;
+ socklen_t optlen = sizeof(info);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, &info, &optlen));
+ ASSERT_EQ(EBUSY, errno);
+}
+
+/* Set TX crypto, then read it back at the base and full sizes, exercising
+ * both copy_to_iter() branches. SKIP if AES-GCM is unavailable.
+ */
+TEST_F(tls, conf_crypto_roundtrip)
+{
+ struct tls12_crypto_info_aes_gcm_128 tx = {
+ .info.version = TLS_1_2_VERSION,
+ .info.cipher_type = TLS_CIPHER_AES_GCM_128,
+ };
+ struct tls12_crypto_info_aes_gcm_128 full;
+ struct tls_crypto_info base;
+ socklen_t optlen;
+
+ if (setsockopt(self->fd, SOL_TLS, TLS_TX, &tx, sizeof(tx)))
+ SKIP(return, "set TLS_TX aes_gcm_128: %s", strerror(errno));
+
+ optlen = sizeof(base);
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &base, &optlen));
+ ASSERT_EQ(sizeof(base), optlen);
+ ASSERT_EQ(TLS_1_2_VERSION, base.version);
+ ASSERT_EQ(TLS_CIPHER_AES_GCM_128, base.cipher_type);
+
+ optlen = sizeof(full);
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &full, &optlen));
+ ASSERT_EQ(sizeof(full), optlen);
+ ASSERT_EQ(TLS_CIPHER_AES_GCM_128, full.info.cipher_type);
+}
+
+TEST_F(tls, bad_optname)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+}
+
TEST_HARNESS_MAIN
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 6/7] tls: convert getsockopt to sockopt_t
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
In-Reply-To: <20260720-getsockopt_phase4-v2-0-8a08fcfa0d72@debian.org>
Continue converting the proto-layer getsockopt callbacks to the sockopt_t
interface, converting do_tls_getsockopt() and its per-option helpers to
take a sockopt_t.
The thin tls_getsockopt() wrapper keeps its __user signature for now: it
builds a user-backed sockopt_t with sockopt_init_user(), calls the helper,
and writes the returned length back to optlen. The helpers use
copy_to_iter() instead of copy_to_user(); the NULL optval check in the
TLS_TX/TLS_RX path is preserved by testing the iterator user buffer.
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/tls/tls_main.c | 80 ++++++++++++++++++++++++++----------------------------
1 file changed, 38 insertions(+), 42 deletions(-)
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 8c588cdab733d..fbb274287aa5f 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -424,20 +424,16 @@ static __poll_t tls_sk_poll(struct file *file, struct socket *sock,
return mask;
}
-static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
- int __user *optlen, int tx)
+static int do_tls_getsockopt_conf(struct sock *sk, sockopt_t *opt, int tx)
{
int rc = 0;
const struct tls_cipher_desc *cipher_desc;
struct tls_context *ctx = tls_get_ctx(sk);
struct tls_crypto_info *crypto_info;
struct cipher_context *cctx;
- int len;
+ int len = opt->optlen;
- if (get_user(len, optlen))
- return -EFAULT;
-
- if (!optval || (len < sizeof(*crypto_info))) {
+ if (!opt->iter_out.ubuf || len < sizeof(*crypto_info)) {
rc = -EINVAL;
goto out;
}
@@ -462,7 +458,8 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
}
if (len == sizeof(*crypto_info)) {
- if (copy_to_user(optval, crypto_info, sizeof(*crypto_info)))
+ if (copy_to_iter(crypto_info, sizeof(*crypto_info),
+ &opt->iter_out) != sizeof(*crypto_info))
rc = -EFAULT;
goto out;
}
@@ -478,44 +475,38 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
memcpy(crypto_info_rec_seq(crypto_info, cipher_desc),
cctx->rec_seq, cipher_desc->rec_seq);
- if (copy_to_user(optval, crypto_info, cipher_desc->crypto_info))
+ if (copy_to_iter(crypto_info, cipher_desc->crypto_info,
+ &opt->iter_out) != cipher_desc->crypto_info)
rc = -EFAULT;
out:
return rc;
}
-static int do_tls_getsockopt_tx_zc(struct sock *sk, char __user *optval,
- int __user *optlen)
+static int do_tls_getsockopt_tx_zc(struct sock *sk, sockopt_t *opt)
{
struct tls_context *ctx = tls_get_ctx(sk);
unsigned int value;
- int len;
-
- if (get_user(len, optlen))
- return -EFAULT;
+ int len = opt->optlen;
if (len != sizeof(value))
return -EINVAL;
value = ctx->zerocopy_sendfile;
- if (copy_to_user(optval, &value, sizeof(value)))
+ if (copy_to_iter(&value, sizeof(value), &opt->iter_out) != sizeof(value))
return -EFAULT;
return 0;
}
-static int do_tls_getsockopt_no_pad(struct sock *sk, char __user *optval,
- int __user *optlen)
+static int do_tls_getsockopt_no_pad(struct sock *sk, sockopt_t *opt)
{
struct tls_context *ctx = tls_get_ctx(sk);
- int value, len;
+ int value, len = opt->optlen;
if (ctx->prot_info.version != TLS_1_3_VERSION)
return -EINVAL;
- if (get_user(len, optlen))
- return -EFAULT;
if (len < sizeof(value))
return -EINVAL;
@@ -525,38 +516,31 @@ static int do_tls_getsockopt_no_pad(struct sock *sk, char __user *optval,
if (value < 0)
return value;
- if (put_user(sizeof(value), optlen))
- return -EFAULT;
- if (copy_to_user(optval, &value, sizeof(value)))
+ opt->optlen = sizeof(value);
+ if (copy_to_iter(&value, sizeof(value), &opt->iter_out) != sizeof(value))
return -EFAULT;
return 0;
}
-static int do_tls_getsockopt_tx_payload_len(struct sock *sk, char __user *optval,
- int __user *optlen)
+static int do_tls_getsockopt_tx_payload_len(struct sock *sk, sockopt_t *opt)
{
struct tls_context *ctx = tls_get_ctx(sk);
u16 payload_len = ctx->tx_max_payload_len;
- int len;
-
- if (get_user(len, optlen))
- return -EFAULT;
+ int len = opt->optlen;
if (len < sizeof(payload_len))
return -EINVAL;
- if (put_user(sizeof(payload_len), optlen))
- return -EFAULT;
-
- if (copy_to_user(optval, &payload_len, sizeof(payload_len)))
+ opt->optlen = sizeof(payload_len);
+ if (copy_to_iter(&payload_len, sizeof(payload_len),
+ &opt->iter_out) != sizeof(payload_len))
return -EFAULT;
return 0;
}
-static int do_tls_getsockopt(struct sock *sk, int optname,
- char __user *optval, int __user *optlen)
+static int do_tls_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
int rc = 0;
@@ -565,17 +549,16 @@ static int do_tls_getsockopt(struct sock *sk, int optname,
switch (optname) {
case TLS_TX:
case TLS_RX:
- rc = do_tls_getsockopt_conf(sk, optval, optlen,
- optname == TLS_TX);
+ rc = do_tls_getsockopt_conf(sk, opt, optname == TLS_TX);
break;
case TLS_TX_ZEROCOPY_RO:
- rc = do_tls_getsockopt_tx_zc(sk, optval, optlen);
+ rc = do_tls_getsockopt_tx_zc(sk, opt);
break;
case TLS_RX_EXPECT_NO_PAD:
- rc = do_tls_getsockopt_no_pad(sk, optval, optlen);
+ rc = do_tls_getsockopt_no_pad(sk, opt);
break;
case TLS_TX_MAX_PAYLOAD_LEN:
- rc = do_tls_getsockopt_tx_payload_len(sk, optval, optlen);
+ rc = do_tls_getsockopt_tx_payload_len(sk, opt);
break;
default:
rc = -ENOPROTOOPT;
@@ -591,12 +574,25 @@ static int tls_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
struct tls_context *ctx = tls_get_ctx(sk);
+ sockopt_t opt;
+ int err;
if (level != SOL_TLS)
return ctx->sk_proto->getsockopt(sk, level,
optname, optval, optlen);
- return do_tls_getsockopt(sk, optname, optval, optlen);
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_tls_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
static int validate_crypto_info(const struct tls_crypto_info *crypto_info,
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 5/7] phonet: pep: convert getsockopt to sockopt_t
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team, Rémi Denis-Courmont
In-Reply-To: <20260720-getsockopt_phase4-v2-0-8a08fcfa0d72@debian.org>
Continue converting the proto-layer getsockopt callbacks to the
sockopt_t interface, splitting pep_getsockopt() into a
do_pep_getsockopt() helper that takes a sockopt_t.
The thin pep_getsockopt() wrapper keeps its __user signature for now:
it builds a user-backed sockopt_t with sockopt_init_user(), calls the
helper, and writes the returned length back to optlen. The helper uses
copy_to_iter() instead of copy_to_user(). No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
Acked-by: Rémi Denis-Courmont <remi@remlab.net>
---
net/phonet/pep.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 60d1a5375725b..7ef4a9a240ef8 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -1078,17 +1078,11 @@ static int pep_setsockopt(struct sock *sk, int level, int optname,
return err;
}
-static int pep_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+static int do_pep_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
struct pep_sock *pn = pep_sk(sk);
int len, val;
- if (level != SOL_PNPIPE)
- return -ENOPROTOOPT;
- if (get_user(len, optlen))
- return -EFAULT;
-
switch (optname) {
case PNPIPE_ENCAP:
val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
@@ -1112,11 +1106,33 @@ static int pep_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- len = min_t(unsigned int, sizeof(int), len);
- if (put_user(len, optlen))
+ len = umin(sizeof(int), opt->optlen);
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ return 0;
+}
+
+static int pep_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_PNPIPE)
+ return -ENOPROTOOPT;
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_pep_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
return -EFAULT;
+
return 0;
}
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 4/7] phonet: pep: do not write beyond optlen in getsockopt
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team, Rémi Denis-Courmont
In-Reply-To: <20260720-getsockopt_phase4-v2-0-8a08fcfa0d72@debian.org>
pep_getsockopt() clamps the reported length to the caller's buffer with
min_t(), but then stores the value with put_user(val, (int __user *)
optval), which always writes sizeof(int) bytes. A getsockopt() call with
an optlen smaller than sizeof(int) thus reports the clamped length yet
writes a full int, one to three bytes past the user buffer.
Write the value with copy_to_user() bounded by len, so at most optlen
bytes are copied, matching the length reported back to userspace.
Fixes: 02a47617cdce ("Phonet: implement GPRS virtual interface over PEP socket")
Signed-off-by: Breno Leitao <leitao@debian.org>
Acked-by: Rémi Denis-Courmont <remi@remlab.net>
---
net/phonet/pep.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 7069271393933..60d1a5375725b 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -1115,7 +1115,7 @@ static int pep_getsockopt(struct sock *sk, int level, int optname,
len = min_t(unsigned int, sizeof(int), len);
if (put_user(len, optlen))
return -EFAULT;
- if (put_user(val, (int __user *) optval))
+ if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 3/7] ieee802154: convert dgram getsockopt to sockopt_t
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
In-Reply-To: <20260720-getsockopt_phase4-v2-0-8a08fcfa0d72@debian.org>
Continue converting the proto-layer getsockopt callbacks to the sockopt_t
interface, splitting dgram_getsockopt() into a do_dgram_getsockopt() helper
that takes a sockopt_t.
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/ieee802154/socket.c | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c
index 85dce296d7513..763f63e48afe4 100644
--- a/net/ieee802154/socket.c
+++ b/net/ieee802154/socket.c
@@ -831,20 +831,12 @@ static int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
return ret;
}
-static int dgram_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+static int do_dgram_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
struct dgram_sock *ro = dgram_sk(sk);
-
int val, len;
- if (level != SOL_IEEE802154)
- return -EOPNOTSUPP;
-
- if (get_user(len, optlen))
- return -EFAULT;
-
- len = min_t(unsigned int, len, sizeof(int));
+ len = min_t(unsigned int, opt->optlen, sizeof(int));
switch (optname) {
case WPAN_WANTACK:
@@ -871,10 +863,32 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- if (put_user(len, optlen))
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ return 0;
+}
+
+static int dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_IEEE802154)
+ return -EOPNOTSUPP;
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_dgram_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
return -EFAULT;
+
return 0;
}
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 2/7] ipv6: raw: convert do_rawv6_getsockopt to sockopt_t
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
In-Reply-To: <20260720-getsockopt_phase4-v2-0-8a08fcfa0d72@debian.org>
Convert do_rawv6_getsockopt to the new sockopt_t model, mirroring what
we have in ipv4. The overall goal is to move these callbacks gradually
from __user points to use sockopt_t, and this part touches
do_rawv6_getsockopt.
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/ipv6/raw.c | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 99eec36796fb9..1f15942d14163 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -1051,14 +1051,12 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
}
-static int do_rawv6_getsockopt(struct sock *sk, int optname,
- char __user *optval, int __user *optlen)
+static int do_rawv6_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
struct raw6_sock *rp = raw6_sk(sk);
int val, len;
- if (get_user(len, optlen))
- return -EFAULT;
+ len = opt->optlen;
switch (optname) {
case IPV6_HDRINCL:
@@ -1082,9 +1080,8 @@ static int do_rawv6_getsockopt(struct sock *sk, int optname,
len = min_t(unsigned int, sizeof(int), len);
- if (put_user(len, optlen))
- return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
}
@@ -1092,6 +1089,9 @@ static int do_rawv6_getsockopt(struct sock *sk, int optname,
static int rawv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
+ sockopt_t opt;
+ int err;
+
switch (level) {
case SOL_RAW:
break;
@@ -1109,7 +1109,18 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
return ipv6_getsockopt(sk, level, optname, optval, optlen);
}
- return do_rawv6_getsockopt(sk, optname, optval, optlen);
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_rawv6_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
static int rawv6_ioctl(struct sock *sk, int cmd, int *karg)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 1/7] ipv6: raw: drop unused level argument from do_rawv6_getsockopt
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
In-Reply-To: <20260720-getsockopt_phase4-v2-0-8a08fcfa0d72@debian.org>
do_rawv6_getsockopt() takes a level argument but never uses it; the
level dispatch is handled by the caller, rawv6_getsockopt(). Drop it,
matching ipv4's do_raw_getsockopt().
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/ipv6/raw.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 3cc58698cbbd3..99eec36796fb9 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -1051,8 +1051,8 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
}
-static int do_rawv6_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+static int do_rawv6_getsockopt(struct sock *sk, int optname,
+ char __user *optval, int __user *optlen)
{
struct raw6_sock *rp = raw6_sk(sk);
int val, len;
@@ -1109,7 +1109,7 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
return ipv6_getsockopt(sk, level, optname, optval, optlen);
}
- return do_rawv6_getsockopt(sk, level, optname, optval, optlen);
+ return do_rawv6_getsockopt(sk, optname, optval, optlen);
}
static int rawv6_ioctl(struct sock *sk, int cmd, int *karg)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t
From: Breno Leitao @ 2026-07-20 16:17 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team, Rémi Denis-Courmont
Now that sockopt_init_user() was already merged, builds a user-backed
sockopt_t from the __user pair. A getsockopt leaf can then take
a sockopt_t behind a thin __user wrapper: the wrapper builds it, calls
the leaf, and writes the length back to optlen. The leaf copies with
copy_to_iter() instead of copy_to_user().
Convert four more leaves the way udp and raw already were: ipv6 raw
(do_rawv6_getsockopt), ieee802154 dgram, phonet pep, and tls
(do_tls_getsockopt and its per-option helpers).
Converting phonet surfaced a pre-existing bug: pep_getsockopt() clamps the
length it reports but writes a full int with put_user(), overrunning an
optval buffer shorter than sizeof(int). It is fixed in its own patch, with
a Fixes: tag, before the phonet conversion, so it can be backported alone.
The last patch adds getsockopt_iter selftest fixtures for rawv6,
ieee802154, phonet and tls, checking the returned length and errno across
exact, oversized and short buffers, an unknown optname and a bad level.
For full motivation about these changes, please check the initial thread
at link
https://lore.kernel.org/all/20260401-getsockopt-v2-0-611df6771aff@debian.org/#t
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Changes in v2:
- Replace min_t() by umin() (David)
- Link to v1: https://patch.msgid.link/20260716-getsockopt_phase4-v1-0-4f45cb12dce7@debian.org
---
Breno Leitao (7):
ipv6: raw: drop unused level argument from do_rawv6_getsockopt
ipv6: raw: convert do_rawv6_getsockopt to sockopt_t
ieee802154: convert dgram getsockopt to sockopt_t
phonet: pep: do not write beyond optlen in getsockopt
phonet: pep: convert getsockopt to sockopt_t
tls: convert getsockopt to sockopt_t
selftests: net: getsockopt_iter: cover rawv6, ieee802154, phonet and tls
net/ieee802154/socket.c | 38 ++-
net/ipv6/raw.c | 27 +-
net/phonet/pep.c | 36 ++-
net/tls/tls_main.c | 80 +++--
tools/testing/selftests/net/getsockopt_iter.c | 424 ++++++++++++++++++++++++++
5 files changed, 533 insertions(+), 72 deletions(-)
---
base-commit: f6f3b36c15ed44de1fbb44e645e4fae8c4a4453e
change-id: 20260715-getsockopt_phase4-180209cfc60a
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply
* Re: [PATCH net-next v5 04/15] libie: add control queue support
From: Larysa Zaremba @ 2026-07-20 16:17 UTC (permalink / raw)
To: Tony Nguyen
Cc: davem, kuba, pabeni, edumazet, andrew+netdev, netdev,
Phani R Burra, przemyslaw.kitszel, aleksander.lobakin,
sridhar.samudrala, michal.swiatkowski, maciej.fijalkowski,
emil.s.tantilov, madhu.chittim, joshua.a.hay, jacob.e.keller,
jayaprakash.shanmugam, jiri, horms, corbet, richardcochran, skhan,
linux-doc, Samuel Salin, Bharath R
In-Reply-To: <20260715180042.1972010-5-anthony.l.nguyen@intel.com>
Sashiko has found one pretty valid issue and one low-hanging-fruit in this
patch. Just in case, here is diff that addresses those:
Author: Larysa Zaremba <larysa.zaremba@intel.com>
Date: Mon Jul 20 18:00:25 2026 +0200
fixup! libie: add control queue support
diff --git a/drivers/net/ethernet/intel/libie/controlq.c b/drivers/net/ethernet/intel/libie/controlq.c
index 0a179957bfef..1eee507dd484 100644
--- a/drivers/net/ethernet/intel/libie/controlq.c
+++ b/drivers/net/ethernet/intel/libie/controlq.c
@@ -127,6 +127,7 @@ int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq)
if (likely(ctlq->next_to_post != ntp)) {
ctlq->next_to_post = ntp;
+ dma_wmb();
writel(ntp, ctlq->reg.tail);
}
@@ -331,11 +332,6 @@ libie_ctlq_add(struct libie_ctlq_ctx *ctx,
qinfo->len > FIELD_MAX(LIBIE_CTLQ_MBX_ATQ_LEN))
return ERR_PTR(-EOPNOTSUPP);
- /* libie_ctlq_init was not called */
- scoped_guard(spinlock, &ctx->ctlqs_lock)
- if (!ctx->ctlqs.next)
- return ERR_PTR(-EINVAL);
-
ctlq = kvzalloc_obj(*ctlq);
if (!ctlq)
return ERR_PTR(-ENOMEM);
^ permalink raw reply related
* Re: [PATCH net-next v5 14/15] ixd: add the core initialization
From: Larysa Zaremba @ 2026-07-20 16:14 UTC (permalink / raw)
To: Tony Nguyen
Cc: davem, kuba, pabeni, edumazet, andrew+netdev, netdev,
przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
madhu.chittim, joshua.a.hay, jacob.e.keller,
jayaprakash.shanmugam, jiri, horms, corbet, richardcochran, skhan,
linux-doc, Bharath R
In-Reply-To: <20260715180042.1972010-15-anthony.l.nguyen@intel.com>
Sashiko says:
> --- Patch [14]: [PATCH net-next v5 14/15] ixd: add the core initialization ---
> commit 43763d63c75ee5bcdd9189c20cf8b272997bdfec
> Author: Larysa Zaremba <larysa.zaremba@intel.com>
>
> ixd: add the core initialization
>
> This patch initializes the core when the mailbox is set up. The driver
> confirms the virtchnl version, requests necessary capabilities, and sets up
> the message handling framework.
>
> > diff --git a/drivers/net/ethernet/intel/ixd/ixd_ctlq.c b/drivers/net/ethernet/intel/ixd/ixd_ctlq.c
> > index 0000000000000..6d658be0695d4
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/ixd/ixd_ctlq.c
>
> [ ... ]
>
> > @@ -0,0 +1,146 @@
>
> [ ... ]
>
> > +void ixd_ctlq_rx_task(struct work_struct *work)
> > +{
> > + struct ixd_adapter *adapter;
> > +
> > + adapter = container_of(work, struct ixd_adapter, mbx_task.work);
> > +
> > + queue_delayed_work(system_unbound_wq, &adapter->mbx_task,
> > + msecs_to_jiffies(300));
> > +
> > + ixd_ctlq_recv_mb_msg(adapter);
> > +}
>
> Does queueing the delayed work before executing the main logic create a race
> condition? If cancel_delayed_work_sync() is called (for example, during
> teardown in ixd_deinit_dflt_mbx()), try_to_grab_pending() could successfully
> delete the newly queued timer and return immediately, assuming the work is
> fully cancelled.
>
> This would bypass flush_work() and allow the teardown path to free
> adapter->xnm and set adapter->arq to NULL while ixd_ctlq_recv_mb_msg() is
> still running, potentially causing a use-after-free or NULL pointer
> dereference. Should the re-arming be moved to the end of the task?
>
I assume the process of cancelling delayed work is more robust.
> > diff --git a/drivers/net/ethernet/intel/ixd/ixd_lib.c b/drivers/net/ethernet/intel/ixd/ixd_lib.c
> > index afc413d3650fb..ce870aba143d4 100644
> > --- a/drivers/net/ethernet/intel/ixd/ixd_lib.c
> > +++ b/drivers/net/ethernet/intel/ixd/ixd_lib.c
>
> [ ... ]
>
> > @@ -136,8 +145,27 @@ void ixd_init_task(struct work_struct *work)
>
> [ ... ]
>
> > + libie_ctlq_xn_shutdown(adapter->xnm);/
> > + ixd_trigger_reset(adapter);
> > + ixd_deinit_dflt_mbx(adapter);
>
> Is it safe to trigger a hardware reset before cancelling the mailbox polling
> task? ixd_deinit_dflt_mbx() is responsible for cancelling the mbx_task, but
> calling ixd_trigger_reset() first means the polling task could run while the
> hardware is resetting.
>
> During reset, MMIO reads or DMA ring memory might temporarily contain invalid
> states, and the still-running mbx_task might process corrupted descriptors.
> Should the polling task be cancelled before asserting the hardware reset here,
> as well as in ixd_remove() in ixd_main.c?
>
Yes, this is safe, we just won't receive anything at this point.
^ permalink raw reply
* Re: [PATCH net-next v5 09/15] idpf: refactor idpf to use libie control queues
From: Larysa Zaremba @ 2026-07-20 16:11 UTC (permalink / raw)
To: Tony Nguyen
Cc: davem, kuba, pabeni, edumazet, andrew+netdev, netdev,
Pavan Kumar Linga, przemyslaw.kitszel, aleksander.lobakin,
sridhar.samudrala, michal.swiatkowski, maciej.fijalkowski,
emil.s.tantilov, joshua.a.hay, jacob.e.keller,
jayaprakash.shanmugam, jiri, horms, corbet, richardcochran, skhan,
linux-doc, Aleksandr Loktionov, Samuel Salin
In-Reply-To: <20260715180042.1972010-10-anthony.l.nguyen@intel.com>
Sashiko says:
> > +void idpf_send_vf_reset_msg(struct idpf_adapter *adapter)
> > +{
> > + struct libie_ctlq_info *ctlq = adapter->asq;
> > +
> > + /* Forcefully claim send queue slot */
> > + idpf_mb_clean(adapter, ctlq, true);
> > +
> > + scoped_guard(spinlock, &ctlq->lock) {
> > + *ctlq->tx_msg[ctlq->next_to_use] = (struct libie_ctlq_msg) {
> > + .opcode = LIBIE_CTLQ_SEND_MSG_TO_CP,
> > + .chnl_opcode = VIRTCHNL2_OP_RESET_VF,
> > + };
> > +
> > + libie_ctlq_send(adapter->asq, 1);
> > + }
> > +}
> Compared to the old call, which routed through idpf_send_mb_msg() and
> first ran idpf_is_reset_detected() (via the arq registers) to bail out
> early if the mailbox was gone, this new helper unconditionally
> dereferences adapter->asq and immediately calls idpf_mb_clean() which
> takes &ctlq->lock.
> Can adapter->asq be NULL here?
> idpf_deinit_dflt_mbx() sets asq = NULL after libie_ctlq_xn_deinit(),
> and idpf_init_dflt_mbx() can fail with asq/arq still NULL if
> libie_find_ctlq() returns NULL. If a hard reset flow is entered on
> such an error path, ctlq->lock would fault.
> Also, is bypassing the xn manager and reaching directly into libie
> internals (ctlq->tx_msg[ctlq->next_to_use], ctlq->lock, and
> libie_ctlq_send()) intentional? This is the only send path in the
> refactored driver that pokes libie state directly, and it is fragile
> against future changes to how tx_msg[] slots are allocated or
> lifecycled after a forced idpf_mb_clean().
> Finally, is it safe to writel() to the mailbox tail via
> libie_ctlq_send() when hardware is already in reset? The old
> idpf_is_reset_detected() gate specifically avoided that.
VF reset is done only in one flow, where it is performed only
if no other reset is going on.
[...]
>
> > +int idpf_send_mb_msg_kfree(struct idpf_adapter *adapter,
> > + struct libie_ctlq_xn_send_params *xn_params,
> > + void *send_buf, size_t send_buf_size)
> > +{
> > + int err = idpf_send_mb_msg(adapter, xn_params, send_buf, send_buf_size);
> > +
> > + if (libie_cp_can_send_onstack(send_buf_size))
> > + kfree(send_buf);
> > +
> > + return err;
> > +}
> Is the stack-vs-heap discrimination here safe as a long-term invariant?
> The reset-detected branch decides to kfree(send_buf) based purely on
> libie_cp_can_send_onstack(send_buf_size), and rel_tx_buf is set to
> kfree unconditionally so libie_ctlq_xn_send() will also invoke kfree
> on the send buffer when the send fails and the size crosses
> LIBIE_CP_TX_COPYBREAK (128 bytes).
> Many call sites pass the address of a stack-allocated struct:
> struct virtchnl2_get_capabilities caps = {};
> ... idpf_send_mb_msg(adapter, &xn_params, &caps, sizeof(caps));
> struct virtchnl2_vport_stats stats_msg = {};
> ... idpf_send_mb_msg(np->adapter, &xn_params, &stats_msg,
> sizeof(stats_msg));
> virtchnl2_vport_stats is exactly 128 bytes today, so it sits right at
> the LIBIE_CP_TX_COPYBREAK boundary. Adding one field to it, or any
> similar stack-passed structure growing past 128 bytes, would silently
> cause kfree() to be called on a stack address in the reset-detected
> or send-failure path, corrupting the slab allocator.
> Would a BUILD_BUG_ON at each stack-based call site, or a typed helper
> that only accepts on-stack buffers up to LIBIE_CP_TX_COPYBREAK, be
> worth adding so this constraint is enforced at compile time rather
> than by convention?
I would very much prefer to go the callback route right away, like in ixd,
but the diff would be even more unmanageable then.
[...]
>
> > @@ -1586,33 +1168,29 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
> > }
> >
> > if (!adapter->vport_params_recvd[idx]) {
> > - adapter->vport_params_recvd[idx] = kzalloc(IDPF_CTLQ_MAX_BUF_LEN,
> > - GFP_KERNEL);
> > + adapter->vport_params_recvd[idx] =
> > + kzalloc(LIBIE_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
> > if (!adapter->vport_params_recvd[idx]) {
> > err = -ENOMEM;
> > goto rel_buf;
> > }
> > }
> >
> > - xn_params.vc_op = VIRTCHNL2_OP_CREATE_VPORT;
> > - xn_params.send_buf.iov_base = vport_msg;
> > - xn_params.send_buf.iov_len = buf_size;
> > - xn_params.recv_buf.iov_base = adapter->vport_params_recvd[idx];
> > - xn_params.recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN;
> > - xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
> > - reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
> > - if (reply_sz < 0) {
> > - err = reply_sz;
> > - goto free_vport_params;
> > + err = idpf_send_mb_msg_kfree(adapter, &xn_params, vport_msg,
> > + sizeof(*vport_msg));
> > + if (err) {
> > + kfree(adapter->vport_params_recvd[idx]);
> > + adapter->vport_params_recvd[idx] = NULL;
> > + return err;
> > }
> >
> > - kfree(vport_msg);
> > + memcpy(adapter->vport_params_recvd[idx], xn_params.recv_mem.iov_base,
> > + xn_params.recv_mem.iov_len);
> Is the memcpy() length here guaranteed to be bounded?
> adapter->vport_params_recvd[idx] is a fixed LIBIE_CTLQ_MAX_BUF_LEN
> allocation, but the copy length is taken directly from
> xn_params.recv_mem.iov_len without a defensive cap. This function
libie_cp does cap the iov_len at LIBIE_CTLQ_MAX_BUF_LEN in
libie_ctlq_fill_rx_msg(), so this is fine.
> relies on libie's Rx path always delivering iov_len <=
> LIBIE_CTLQ_MAX_BUF_LEN. If that invariant ever changes (larger RX
> buffers, multi-descriptor delivery, or a missing bounds check against
> the descriptor's data_len in libie), this becomes an unchecked heap
> overflow driven by CP firmware.
> Other call sites in the same patch use bounded copies, for example
> idpf_send_get_caps_msg():
> memcpy(&adapter->caps, xn_params.recv_mem.iov_base,
> sizeof(adapter->caps));
> Would using min_t(size_t, LIBIE_CTLQ_MAX_BUF_LEN,
> xn_params.recv_mem.iov_len) match the pattern used elsewhere in this
> file?
>
^ 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