* Re: [PATCH net] dpll: fix NULL pointer dereference in dpll_msg_add_pin_ref_sync()
From: Jiri Pirko @ 2026-07-13 8:56 UTC (permalink / raw)
To: Ivan Vecera
Cc: netdev, Vadim Fedorenko, Arkadiusz Kubalewski, Przemek Kitszel,
Milena Olech, Jakub Kicinski, open list
In-Reply-To: <20260710193625.1378822-1-ivecera@redhat.com>
Fri, Jul 10, 2026 at 09:36:25PM +0200, ivecera@redhat.com wrote:
>When a dpll_pin is shared across multiple dpll_device instances and
>those devices are being unregistered (e.g. during driver module removal),
>a NULL pointer dereference can occur in dpll_msg_add_pin_ref_sync().
>
>This happens under the following conditions:
> - A pin is registered with two or more dpll devices (dpll_A, dpll_B)
> - The pin has ref_sync pairs with other pins
> - During unregistration of dpll_A's pins, a ref_sync partner pin is
> unregistered first, removing it from dpll_A->pin_refs
> - But since the partner pin is still registered with dpll_B, its
> dpll_refs is not empty, so dpll_pin_ref_sync_pair_del() does NOT
> run and the partner stays in the pin's ref_sync_pins xarray
> - When the pin itself is then unregistered from dpll_A, the delete
> notification calls dpll_msg_add_pin_ref_sync() which finds the
> partner in ref_sync_pins, passes dpll_pin_available() (partner is
> still registered with dpll_B), but dpll_pin_on_dpll_priv(dpll_A,
> partner) returns NULL because partner was already removed from
> dpll_A->pin_refs
> - The NULL priv pointer is passed to the driver's ref_sync_get
> callback, which dereferences it
>
> BUG: kernel NULL pointer dereference, address: 0000000000000034
> Oops: Oops: 0000 [#1] SMP NOPTI
> RIP: 0010:zl3073x_dpll_input_pin_ref_sync_get+0x73/0x80 [zl3073x]
> Call Trace:
> dpll_msg_add_pin_ref_sync+0xb8/0x200
> dpll_cmd_pin_get_one+0x3b6/0x4b0
> dpll_pin_event_send+0x72/0x140
> __dpll_pin_unregister+0x5a/0x2b0
> dpll_pin_unregister+0x49/0x70
>
>Fix this by skipping ref_sync pins whose priv pointer cannot be resolved
>for the current dpll device.
>
>Fixes: 58256a26bfb3 ("dpll: add reference sync get/set")
>Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
^ permalink raw reply
* Re: [PATCH v8 08/10] rust: binder: use `LocalModule` for `THIS_MODULE`
From: Alvin Sun @ 2026-07-13 8:53 UTC (permalink / raw)
To: Alice Ryhl
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Greg Kroah-Hartman, Rafael J. Wysocki, David Airlie,
Simona Vetter, Daniel Almeida, Arnd Bergmann, Brendan Higgins,
David Gow, Rae Moar, Breno Leitao, Jens Axboe, Dave Ertman,
Leon Romanovsky, Igor Korotin, FUJITA Tomonori, Bjorn Helgaas,
Krzysztof Wilczyński, Arve Hjønnevåg, Todd Kjos,
Christian Brauner, Carlos Llamas, rust-for-linux, linux-modules,
driver-core, dri-devel, nova-gpu, linux-kselftest, kunit-dev,
linux-block, linux-kernel, netdev, linux-pci
In-Reply-To: <alSeRIaps6QSqxJE@google.com>
On 7/13/26 16:13, Alice Ryhl wrote:
> On Mon, Jul 13, 2026 at 02:45:29PM +0800, Alvin Sun wrote:
>> Replace the `THIS_MODULE` static reference in the binder fops with
>> `this_module::<LocalModule>()`, consistent with the move of
>> `THIS_MODULE` into the `ModuleMetadata` trait.
>>
>> Assisted-by: opencode:glm-5.2
>> Reviewed-by: Gary Guo <gary@garyguo.net>
>> Acked-by: Danilo Krummrich <dakr@kernel.org>
>> Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
> Most other Rust Binder commits use 'rust_binder:' prefix rather than
> 'rust: binder:'.
I missed this prefix convention — thanks for catching it.
I'll fix it in the next revision, along with any other feedback that
comes in.
Best regards,
Alvin
>
> Alice
^ permalink raw reply
* Re: [PATCH] net: gre: fix lltx regression for GRE tunnels with SEQ/CSUM
From: Ido Schimmel @ 2026-07-13 8:53 UTC (permalink / raw)
To: Yun Zhou
Cc: dsahern, davem, edumazet, kuba, pabeni, horms, netdev,
linux-kernel
In-Reply-To: <20260709105243.864835-1-yun.zhou@windriver.com>
Patch prefix should be [PATCH net]:
https://docs.kernel.org/process/maintainer-netdev.html
On Thu, Jul 09, 2026 at 06:52:43PM +0800, Yun Zhou wrote:
> Before commit 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to
> dev->lltx"), NETIF_F_LLTX was set unconditionally in __gre_tunnel_init()
> alongside GRE_FEATURES:
>
> dev->features |= GRE_FEATURES | NETIF_F_LLTX;
>
> When that commit converted NETIF_F_LLTX to the dev->lltx flag, it
> placed 'dev->lltx = true' after the SEQ/CSUM early returns instead
> of before them. This causes GRE/GRETAP tunnels with SEQ or CSUM+encap
> to lose lockless TX, reintroducing _xmit_lock acquisition around their
> ndo_start_xmit. Since GRE xmit re-enters the stack via
> ip_tunnel_xmit(), holding _xmit_lock risks ABBA deadlock with the
> underlay device.
>
> CPU0 CPU1
> ---- ----
> lock(&qdisc_xmit_lock_key#6);
> lock(&qdisc_xmit_lock_key#3);
> lock(&qdisc_xmit_lock_key#6);
> lock(&qdisc_xmit_lock_key#3);
>
> Fix by moving dev->lltx = true before the early returns, restoring
> the original unconditional behavior.
>
> Fixes: 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to dev->lltx")
Both sashiko instances indicate that this commit introduced the same bug
in ip6gre_tnl_init_features(). Please perform the same fix there and
fold it into v2.
> Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
> ---
> net/ipv4/ip_gre.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
> index 9fbff16cda1d..8c5ad8ec8d09 100644
> --- a/net/ipv4/ip_gre.c
> +++ b/net/ipv4/ip_gre.c
> @@ -1018,6 +1018,8 @@ static void __gre_tunnel_init(struct net_device *dev)
> dev->features |= GRE_FEATURES;
> dev->hw_features |= GRE_FEATURES;
>
> + dev->lltx = true;
> +
> /* TCP offload with GRE SEQ is not supported, nor can we support 2
> * levels of outer headers requiring an update.
> */
> @@ -1029,8 +1031,6 @@ static void __gre_tunnel_init(struct net_device *dev)
>
> dev->features |= NETIF_F_GSO_SOFTWARE;
> dev->hw_features |= NETIF_F_GSO_SOFTWARE;
> -
> - dev->lltx = true;
> }
>
> static int ipgre_tunnel_init(struct net_device *dev)
> --
> 2.43.0
>
^ permalink raw reply
* [Bug]KASAN: slab-use-after-free Read in tcp_sync_mss
From: Yue Sun @ 2026-07-13 8:52 UTC (permalink / raw)
To: Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: netdev, linux-kernel, syzkaller
Hello,
We encountered a slab-use-after-free read in tcp_sync_mss() on
7.2.0-rc1-g7558af35077a (upstream parent 7404ce516372). We checked
the latest mainline commit, a13c140cc289 (Linux 7.2-rc3), and
confirmed that the root cause is still present.
Original KASAN report
=====================
==================================================================
BUG: KASAN: slab-use-after-free in __tcp_mtu_to_mss net/ipv4/tcp_output.c:2005 [inline]
BUG: KASAN: slab-use-after-free in tcp_mtu_to_mss net/ipv4/tcp_output.c:2024 [inline]
BUG: KASAN: slab-use-after-free in tcp_sync_mss+0x560/0x610 net/ipv4/tcp_output.c:2088
Read of size 2 at addr ff11000047600bb8 by task kworker/u8:2/37
CPU: 1 UID: 0 PID: 37 Comm: kworker/u8:2 Tainted: G L 7.2.0-rc1-g7558af35077a #48 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
Workqueue: krdsd rds_tcp_accept_worker
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x116/0x1b0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xf1/0x5c0 mm/kasan/report.c:482
kasan_report+0xca/0x100 mm/kasan/report.c:595
__tcp_mtu_to_mss net/ipv4/tcp_output.c:2005 [inline]
tcp_mtu_to_mss net/ipv4/tcp_output.c:2024 [inline]
tcp_sync_mss+0x560/0x610 net/ipv4/tcp_output.c:2088
tcp_ack_update_window net/ipv4/tcp_input.c:3979 [inline]
tcp_ack+0x4b35/0x6e80 net/ipv4/tcp_input.c:4336
tcp_rcv_state_process+0xf30/0x7270 net/ipv4/tcp_input.c:7201
tcp_v4_do_rcv+0x7ec/0x12b0 net/ipv4/tcp_ipv4.c:1873
tcp_v4_rcv+0x2d30/0x4800 net/ipv4/tcp_ipv4.c:2245
ip_protocol_deliver_rcu+0xba/0x4d0 net/ipv4/ip_input.c:207
ip_local_deliver_finish+0x3ce/0x710 net/ipv4/ip_input.c:241
NF_HOOK include/linux/netfilter.h:318 [inline]
NF_HOOK include/linux/netfilter.h:312 [inline]
ip_local_deliver+0x19a/0x200 net/ipv4/ip_input.c:262
dst_input include/net/dst.h:480 [inline]
ip_rcv_finish net/ipv4/ip_input.c:492 [inline]
NF_HOOK include/linux/netfilter.h:318 [inline]
NF_HOOK include/linux/netfilter.h:312 [inline]
ip_rcv+0x327/0x3d0 net/ipv4/ip_input.c:612
__netif_receive_skb_one_core+0x199/0x1e0 net/core/dev.c:6206
__netif_receive_skb+0x1d/0x160 net/core/dev.c:6319
process_backlog+0x382/0x1540 net/core/dev.c:6670
__napi_poll.constprop.0+0xb3/0x540 net/core/dev.c:7729
napi_poll net/core/dev.c:7792 [inline]
net_rx_action+0x9b1/0xea0 net/core/dev.c:7949
handle_softirqs+0x1d4/0x980 kernel/softirq.c:622
do_softirq kernel/softirq.c:523 [inline]
do_softirq+0xac/0xe0 kernel/softirq.c:510
</IRQ>
<TASK>
__local_bh_enable_ip+0x100/0x120 kernel/softirq.c:450
local_bh_enable include/linux/bottom_half.h:33 [inline]
rcu_read_unlock_bh include/linux/rcupdate.h:914 [inline]
__dev_queue_xmit+0xe25/0x4300 net/core/dev.c:4907
dev_queue_xmit include/linux/netdevice.h:3446 [inline]
neigh_hh_output include/net/neighbour.h:544 [inline]
neigh_output include/net/neighbour.h:558 [inline]
ip_finish_output2+0xc52/0x1ea0 net/ipv4/ip_output.c:236
__ip_finish_output.part.0+0x1bb/0x350 net/ipv4/ip_output.c:314
__ip_finish_output net/ipv4/ip_output.c:302 [inline]
ip_finish_output net/ipv4/ip_output.c:324 [inline]
NF_HOOK_COND include/linux/netfilter.h:307 [inline]
ip_output+0x376/0x860 net/ipv4/ip_output.c:437
dst_output include/net/dst.h:470 [inline]
ip_local_out+0x1b4/0x200 net/ipv4/ip_output.c:131
__ip_queue_xmit+0x773/0x1a90 net/ipv4/ip_output.c:533
__tcp_transmit_skb+0x36b1/0x5080 net/ipv4/tcp_output.c:1716
tcp_transmit_skb net/ipv4/tcp_output.c:1734 [inline]
tcp_write_xmit+0x1876/0x7dd0 net/ipv4/tcp_output.c:3062
__tcp_push_pending_frames+0xb0/0x3c0 net/ipv4/tcp_output.c:3245
tcp_send_fin+0x11f/0x10d0 net/ipv4/tcp_output.c:3844
__tcp_close+0xa27/0x10f0 net/ipv4/tcp.c:3220
tcp_close+0x28/0x110 net/ipv4/tcp.c:3311
inet_release+0xed/0x200 net/ipv4/af_inet.c:442
inet6_release+0x50/0x80 net/ipv6/af_inet6.c:472
__sock_release net/socket.c:710 [inline]
sock_release+0x91/0x1d0 net/socket.c:738
rds_tcp_accept_one+0x246/0xf10 net/rds/tcp_listen.c:341
rds_tcp_accept_worker+0x41/0x60 net/rds/tcp.c:552
process_one_work+0xce3/0x2210 kernel/workqueue.c:3619
process_scheduled_works kernel/workqueue.c:3728 [inline]
worker_thread+0x693/0xeb0 kernel/workqueue.c:3809
kthread+0x38d/0x4a0 kernel/kthread.c:436
ret_from_fork+0xb09/0xdb0 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Allocated by task 46228:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
unpoison_slab_object mm/kasan/common.c:340 [inline]
__kasan_slab_alloc+0x87/0x90 mm/kasan/common.c:366
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4612 [inline]
slab_alloc_node mm/slub.c:4945 [inline]
kmem_cache_alloc_noprof+0x244/0x690 mm/slub.c:4959
sk_prot_alloc+0x5f/0x290 net/core/sock.c:2246
sk_alloc+0x36/0xe10 net/core/sock.c:2308
smc_sock_alloc net/smc/af_smc.c:428 [inline]
smc_create+0x125/0x320 net/smc/af_smc.c:3389
__sock_create+0x382/0x8e0 net/socket.c:1651
sock_create net/socket.c:1709 [inline]
__sys_socket_create net/socket.c:1746 [inline]
__sys_socket_create net/socket.c:1731 [inline]
__sys_socket+0x145/0x260 net/socket.c:1793
__do_sys_socket net/socket.c:1807 [inline]
__se_sys_socket net/socket.c:1805 [inline]
__x64_sys_socket+0x72/0xb0 net/socket.c:1805
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x11f/0x860 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 15:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x61/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2705 [inline]
slab_free_after_rcu_debug+0x105/0x2b0 mm/slub.c:6467
rcu_do_batch kernel/rcu/tree.c:2645 [inline]
rcu_core+0x59e/0x1130 kernel/rcu/tree.c:2897
handle_softirqs+0x1d4/0x980 kernel/softirq.c:622
run_ksoftirqd kernel/softirq.c:1076 [inline]
run_ksoftirqd+0x3a/0x60 kernel/softirq.c:1068
smpboot_thread_fn+0x3d4/0xaa0 kernel/smpboot.c:160
kthread+0x38d/0x4a0 kernel/kthread.c:436
ret_from_fork+0xb09/0xdb0 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
Last potentially related work creation:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
kasan_record_aux_stack+0xa7/0xc0 mm/kasan/generic.c:556
slab_free_hook mm/slub.c:2666 [inline]
slab_free mm/slub.c:6405 [inline]
kmem_cache_free+0x438/0x720 mm/slub.c:6532
sk_prot_free net/core/sock.c:2289 [inline]
__sk_destruct+0x62d/0xab0 net/core/sock.c:2391
sk_destruct+0xc1/0xe0 net/core/sock.c:2419
__sk_free+0xf2/0x3e0 net/core/sock.c:2430
sk_free+0x6a/0x90 net/core/sock.c:2441
sock_put include/net/sock.h:2020 [inline]
smc_release+0x41d/0x620 net/smc/af_smc.c:369
__sock_release+0xb3/0x270 net/socket.c:710
sock_close+0x1c/0x30 net/socket.c:1501
__fput+0x402/0xb60 fs/file_table.c:512
task_work_run+0x16b/0x260 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1de/0x5f0 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x737/0x860 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ff11000047600000
which belongs to the cache SMC of size 3216
The buggy address is located 3000 bytes inside of
freed 3216-byte region [ff11000047600000, ff11000047600c90)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xff11000047604f80 pfn:0x47600
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
memcg:ff11000047607741
flags: 0xfff00000000240(workingset|head|node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000240 ff11000020a6a780 ff11000020a68708 ffd4000001aa4a10
raw: ff11000047604f80 0000000800090006 00000000f5000000 ff11000047607741
head: 00fff00000000240 ff11000020a6a780 ff11000020a68708 ffd4000001aa4a10
head: ff11000047604f80 0000000800090006 00000000f5000000 ff11000047607741
head: 00fff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 13226, tgid 13219 (syz.0.27), ts 78395568066, free_ts 78097920902
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0xff/0x130 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0xe82/0x2bf0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x27b/0x2a00 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3294 [inline]
allocate_slab mm/slub.c:3408 [inline]
new_slab+0xad/0x610 mm/slub.c:3454
refill_objects+0x10e/0x3d0 mm/slub.c:7338
refill_sheaf mm/slub.c:2832 [inline]
__pcs_replace_empty_main+0x352/0x670 mm/slub.c:4703
alloc_from_pcs mm/slub.c:4801 [inline]
slab_alloc_node mm/slub.c:4933 [inline]
kmem_cache_alloc_noprof+0x550/0x690 mm/slub.c:4959
sk_prot_alloc+0x5f/0x290 net/core/sock.c:2246
sk_alloc+0x36/0xe10 net/core/sock.c:2308
smc_sock_alloc net/smc/af_smc.c:428 [inline]
smc_create+0x125/0x320 net/smc/af_smc.c:3389
__sock_create+0x382/0x8e0 net/socket.c:1651
sock_create net/socket.c:1709 [inline]
__sys_socket_create net/socket.c:1746 [inline]
__sys_socket_create net/socket.c:1731 [inline]
__sys_socket+0x145/0x260 net/socket.c:1793
__do_sys_socket net/socket.c:1807 [inline]
__se_sys_socket net/socket.c:1805 [inline]
__x64_sys_socket+0x72/0xb0 net/socket.c:1805
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x11f/0x860 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
page last free pid 790 tgid 790 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
free_pages_prepare+0x54b/0xd20 mm/page_alloc.c:1451
__free_contig_range_common+0x165/0x240 mm/page_alloc.c:6897
__free_contig_range mm/page_alloc.c:6942 [inline]
free_pages_bulk+0x125/0x1a0 mm/page_alloc.c:5257
vm_area_free_pages+0x17c/0x290 mm/vmalloc.c:3439
vfree+0x125/0x780 mm/vmalloc.c:3488
delayed_vfree_work+0x57/0x70 mm/vmalloc.c:3392
process_one_work+0xce3/0x2210 kernel/workqueue.c:3619
process_scheduled_works kernel/workqueue.c:3728 [inline]
worker_thread+0x693/0xeb0 kernel/workqueue.c:3809
kthread+0x38d/0x4a0 kernel/kthread.c:436
ret_from_fork+0xb09/0xdb0 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
Memory state around the buggy address:
ff11000047600a80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ff11000047600b00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ff11000047600b80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ff11000047600c00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ff11000047600c80: fb fb fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
Root cause
==========
The KASAN report places the invalid read 3000 bytes into a freed
3216-byte struct smc_sock. In the crashing build:
offsetof(struct smc_sock, af_ops) = 2992
offsetof(struct inet_connection_sock_af_ops, net_header_len) = 8
Their sum is 3000, so the invalid two-byte read is precisely
smc->af_ops.net_header_len. This matches __tcp_mtu_to_mss():
mss_now = pmtu - icsk->icsk_af_ops->net_header_len -
sizeof(struct tcphdr);
smc_listen() replaces the underlying TCP socket's icsk_af_ops with an
operations structure embedded in the SMC socket before calling
kernel_listen():
smc->ori_af_ops = inet_csk(smc->clcsock->sk)->icsk_af_ops;
smc->af_ops = *smc->ori_af_ops;
smc->af_ops.syn_recv_sock = smc_tcp_syn_recv_sock;
inet_csk(smc->clcsock->sk)->icsk_af_ops = &smc->af_ops;
rc = kernel_listen(smc->clcsock, backlog);
if (rc) {
smc_clcsock_restore_cb(...);
rcu_assign_sk_user_data(smc->clcsock->sk, NULL);
goto out;
}
When kernel_listen() fails, the error path restores sk_data_ready and
sk_user_data but does not restore icsk_af_ops. Closing the SMC socket
can therefore free the smc_sock while its TCP clcsock still contains
an icsk_af_ops pointer into that object. Later TCP receive processing
dereferences the stale pointer in tcp_sync_mss().
The triggering program uses an AF_SMC socket, sends with MSG_FASTOPEN,
then calls listen(). MSG_FASTOPEN switches SMC to TCP fallback and
starts activity on the underlying TCP socket; kernel_listen() then
fails, leaving the stale pointer installed. The same missing restore
is still present in smc_listen() at a13c140cc289.
The error path should restore the original pointer before returning:
inet_csk(smc->clcsock->sk)->icsk_af_ops = smc->ori_af_ops;
This may be related to the issue discussed in:
https://lore.kernel.org/netdev/tencent_BD4B709F8D16281265EDBC0DC9EFC8758808@qq.com/
Thanks,
--
Yue Sun
^ permalink raw reply
* [PATCH net] net: txgbe: fix heap overflow when reading module EEPROM
From: Chenguang Zhao @ 2026-07-13 8:51 UTC (permalink / raw)
To: jiawenwu, mengyuanlou, andrew+netdev, davem, edumazet, kuba,
pabeni
Cc: chenguang.zhao, maxime.chevallier, netdev, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
txgbe_read_eeprom_hostif() always copies round_up(length, 4) bytes
into the caller buffer, which ethtool allocates with exactly 'length'
bytes. A non-4-aligned length therefore causes an out-of-bounds write.
Copy only the remaining bytes on the final dword instead.
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
index affea1a364ef..26d0cfc58ee2 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
@@ -96,11 +96,13 @@ int txgbe_read_eeprom_hostif(struct wx *wx,
dword_len = round_up(length, 4) >> 2;
for (i = 0; i < dword_len; i++) {
+ u32 copy_len = min_t(u32, 4, length - i * 4);
+
value = rd32a(wx, WX_FW2SW_MBOX, i + offset);
le32_to_cpus(&value);
- memcpy(data, &value, 4);
- data += 4;
+ memcpy(data, &value, copy_len);
+ data += copy_len;
}
return 0;
--
2.25.1
^ permalink raw reply related
* Re: [PATCH net] tcp: initialize standalone IPv4 ACK options
From: Eric Dumazet @ 2026-07-13 8:48 UTC (permalink / raw)
To: Yizhou Zhao
Cc: netdev, Neal Cardwell, Kuniyuki Iwashima, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel,
Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li, Ke Xu, stable
In-Reply-To: <20260713081842.3119-1-zhaoyz24@mails.tsinghua.edu.cn>
On Mon, Jul 13, 2026 at 10:18 AM Yizhou Zhao
<zhaoyz24@mails.tsinghua.edu.cn> wrote:
>
> tcp_v4_send_ack() constructs standalone IPv4 TCP ACK replies on the stack
> for SYN-RECV and TIME-WAIT paths. It currently zeroes only the TCP
> header, not the accompanying option buffer.
>
> TCP-AO options may have actual lengths that are not 4-byte aligned, while
> the transmitted TCP header length is correctly rounded up to a 4-byte
> boundary. tcp_ao_hash_hdr() writes only the MAC bytes, leaving the
> TCP-AO option alignment padding in rep.opt uninitialized. With stack
> auto-initialization disabled, those padding bytes can be copied into the
> network packet and sent to the peer.
>
> Zero the whole reply structure before writing options, so the alignment
> padding bytes are initialized.
Please fix TCP-AO instead of slowing down TCP (almost no TCP flow is using AO)
^ permalink raw reply
* Re: [Regression] Broken MPLS routes with multiple nexthops
From: Thorsten Leemhuis @ 2026-07-13 8:48 UTC (permalink / raw)
To: Kuniyuki Iwashima, anthony.doeraene; +Cc: davem, netdev
In-Reply-To: <20260625155313.847098-1-kuniyu@google.com>
On 6/25/26 17:51, Kuniyuki Iwashima wrote:
> From: Anthony Doeraene <anthony.doeraene@uclouvain.be>
> Date: Thu, 25 Jun 2026 17:07:41 +0200
>>
>> According to my experiments, it seems that ECMP with MPLS (i.e. an MPLS
>> route with multiple
>> nexthops) is broken on the master branch of the kernel.
>>
>> Indeed, whenever adding an MPLS route with multiple nexthops, ip route
>> show the route as
>> a dead route/link down, even if nexthops are reachable.
>> [...]
>> From a git blame, it seems that commit
>> f0914b8436c589b7ab32c614d8d7868eb4ebd5bf
>> broke the core logic for building nexthops.
>
> Thanks for the report !
>
> It was to balance refcount with netdev_put() in mpls_rt_alloc().
> I'll post the patch below.
>
> (Updating rt->rt_nhn is not strictlly needed for netdev_put()
> because it has NULL check and rt is allocated with kzalloc(),
> but it's a bit error prone, so I'll keep it)
Kuniyuki Iwashima: was this regression ever resolved? This thread looks
stalled, but I fear I might have missed something.
Ciao, Thorsten
> ---8<---
> diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
> index ca504d9626cf..4a81514e919a 100644
> --- a/net/mpls/af_mpls.c
> +++ b/net/mpls/af_mpls.c
> @@ -922,8 +922,7 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
> struct nlattr *nla_via, *nla_newdst;
> int remaining = cfg->rc_mp_len;
> int err = 0;
> -
> - rt->rt_nhn = 0;
> + u8 nhs = 0;
>
> change_nexthops(rt) {
> int attrlen;
> @@ -959,12 +958,15 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
> rt->rt_nhn_alive--;
>
> rtnh = rtnh_next(rtnh, &remaining);
> - rt->rt_nhn++;
> + nhs++;
> } endfor_nexthops(rt);
>
> + rt->rt_nhn = nhs;
> +
> return 0;
>
> errout:
> + rt->rt_nhn = nhs;
> return err;
> }
>
> ---8<---
>
>
>>
>> This commit modified function `mpls_nx_build_multi` by setting
>> `rt->rt_nhn` to 0 at the
>> start of the function. However, the loop `change_nexthops` just below
>> depends on
>> `rt->rt_nhn` to know the actual number of nexthops that it should build.
>> As `rt->rt_nhn`
>> is set to 0 just before, **no nexthop is ever built**, leading to a dead
>> route. Even if we
>> remove this modification, this commit incorrectly increments
>> `rt->rt_nhn` at the end of
>> the loop (`rt->rt_nhn++`), such that the loop always end with an error
>> as it tries to
>> constructs more nexthops that actually provided.
>>
>> Commenting these two lines fix the issue, and allows to create once
>> again MPLS routes
>> with multiple nexthops:
>>
>> ```
>> modprobe mpls_iptunnel mpls_router
>> sysctl net.mpls.platform_labels=100000
>> ip link set dummy0 up
>> ip addr add fc00:1::1/112 dev dummy0
>> ip addr add fc00:2::1/112 dev dummy0
>> ip -M route add 16000 \
>> nexthop via inet6 fc00:1::2 as 16001 \
>> nexthop via inet6 fc00:2::2 as 16002
>>
>> # Check the route
>> ip -M route
>> # Output:
>> # 16000
>> # nexthop as to 16001 via inet6 fc00:1::2 dev dummy0
>> # nexthop as to 16002 via inet6 fc00:2::2 dev dummy0
>> #
>> # Route is accepted and present !
>> ```
>>
>> Overall, I think it would be interesting to discuss what this patch was
>> trying to achieve,
>> and how we can conciliate both use-cases.
>>
>> Best regards and looking forward to hearing from you,
>> Doeraene Anthony
>
^ permalink raw reply
* Re: [PATCH v8 10/10] rust: module: update MAINTAINERS to cover module.rs
From: Alvin Sun @ 2026-07-13 8:47 UTC (permalink / raw)
To: Alice Ryhl
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Greg Kroah-Hartman, Rafael J. Wysocki, David Airlie,
Simona Vetter, Daniel Almeida, Arnd Bergmann, Brendan Higgins,
David Gow, Rae Moar, Breno Leitao, Jens Axboe, Dave Ertman,
Leon Romanovsky, Igor Korotin, FUJITA Tomonori, Bjorn Helgaas,
Krzysztof Wilczyński, Arve Hjønnevåg, Todd Kjos,
Christian Brauner, Carlos Llamas, rust-for-linux, linux-modules,
driver-core, dri-devel, nova-gpu, linux-kselftest, kunit-dev,
linux-block, linux-kernel, netdev, linux-pci
In-Reply-To: <alSe6PsvdrSYP_Z0@google.com>
On 7/13/26 16:16, Alice Ryhl wrote:
> On Mon, Jul 13, 2026 at 02:45:31PM +0800, Alvin Sun wrote:
>> Module types now live in `rust/kernel/module.rs` alongside
>> `rust/kernel/module_param.rs`. Update the MODULE SUPPORT file pattern
>> from `rust/kernel/module_param.rs` to `rust/kernel/module*.rs` so both
>> files are covered.
>>
>> Cc: Petr Pavlu <petr.pavlu@suse.com>
>> Assisted-by: opencode:glm-5.2
>> Link: https://lore.kernel.org/rust-for-linux/8ea21b29-9baf-4926-a16f-7d21c5a1a1b8@suse.com
>> Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
>> ---
>> MAINTAINERS | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index e035a3be797c4..74733de3e41ee 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -17984,7 +17984,7 @@ F: include/linux/module*.h
>> F: kernel/module/
>> F: lib/test_kmod.c
>> F: lib/tests/module/
>> -F: rust/kernel/module_param.rs
>> +F: rust/kernel/module*.rs
>> F: rust/macros/module.rs
>> F: scripts/module*
>> F: tools/testing/selftests/kmod/
> Is there a reason why this is a separate commit from the commit that
> introduces rust/kernel/module.rs file?
Gary asked the same on v6; `git log` shows MAINTAINERS changes are
conventionally
standalone commits with a `MAINTAINERS: ` prefix, so I kept it separate.
Gary agreed [1].
[1]:
https://lore.kernel.org/rust-for-linux/DJJ26195JBS9.D3DNLZPRJYO4@garyguo.net/
Best regards,
Alvin
>
> Alice
^ permalink raw reply
* [PATCH mlx5-next 2/2] net/mlx5: ifc: Add PSP related fields
From: Tariq Toukan @ 2026-07-13 8:43 UTC (permalink / raw)
To: Leon Romanovsky, linux-rdma, Mark Bloch, netdev, Saeed Mahameed,
Tariq Toukan
Cc: Alexei Lazar, Alex Vesker, Andrew Lunn, Cosmin Ratiu,
David S. Miller, Dragos Tatulea, Eric Dumazet, Feng Liu,
Jakub Kicinski, Kees Cook, linux-kernel, Paolo Abeni,
Parav Pandit, Shay Drory, Simon Horman, Yevgeny Kliteynik
In-Reply-To: <20260713084320.1015240-1-tariqt@nvidia.com>
From: Cosmin Ratiu <cratiu@nvidia.com>
This adds:
- misc_parameters_6, containing a few fields for matching PSP headers.
As this is the last misc_parameters field defined, retire the old
optimization added in commit [1] to not touch the reserved part.
- PSP decap action.
- PSP SPI header field pointer.
[1] commit 667cb65ae5ad ("net/mlx5: Don't store reserved part in FTEs
and FGs")
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../net/ethernet/mellanox/mlx5/core/fs_core.h | 12 +-----------
include/linux/mlx5/device.h | 1 +
include/linux/mlx5/mlx5_ifc.h | 17 +++++++++++++++--
3 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
index dbaf33b537f7..906584345a02 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -214,17 +214,7 @@ struct mlx5_ft_underlay_qp {
u32 qpn;
};
-#define MLX5_FTE_MATCH_PARAM_RESERVED reserved_at_e00
-/* Calculate the fte_match_param length and without the reserved length.
- * Make sure the reserved field is the last.
- */
-#define MLX5_ST_SZ_DW_MATCH_PARAM \
- ((MLX5_BYTE_OFF(fte_match_param, MLX5_FTE_MATCH_PARAM_RESERVED) / sizeof(u32)) + \
- BUILD_BUG_ON_ZERO(MLX5_ST_SZ_BYTES(fte_match_param) != \
- MLX5_FLD_SZ_BYTES(fte_match_param, \
- MLX5_FTE_MATCH_PARAM_RESERVED) +\
- MLX5_BYTE_OFF(fte_match_param, \
- MLX5_FTE_MATCH_PARAM_RESERVED)))
+#define MLX5_ST_SZ_DW_MATCH_PARAM MLX5_ST_SZ_DW(fte_match_param)
struct fs_fte_action {
int modify_mask;
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index 07a25f264292..8cb321a9fb3d 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -1171,6 +1171,7 @@ enum {
MLX5_MATCH_MISC_PARAMETERS_3 = 1 << 4,
MLX5_MATCH_MISC_PARAMETERS_4 = 1 << 5,
MLX5_MATCH_MISC_PARAMETERS_5 = 1 << 6,
+ MLX5_MATCH_MISC_PARAMETERS_6 = 1 << 7,
};
enum {
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 7de01d4f1b5e..cf01922cf69f 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -508,7 +508,8 @@ struct mlx5_ifc_flow_table_prop_layout_bits {
u8 reformat_l2_to_l3_audp_tunnel[0x1];
u8 reformat_l3_audp_tunnel_to_l2[0x1];
u8 ignore_flow_level_rtc_valid[0x1];
- u8 reserved_at_70[0x8];
+ u8 reserved_at_70[0x7];
+ u8 reformat_del_psp_transport[0x1];
u8 log_max_ft_num[0x8];
u8 reserved_at_80[0x10];
@@ -798,6 +799,15 @@ struct mlx5_ifc_fte_match_set_misc5_bits {
u8 reserved_at_100[0x100];
};
+struct mlx5_ifc_fte_match_set_misc6_bits {
+ u8 reserved_at_0[0x1a];
+ u8 psp_version[0x4];
+ u8 reserved_at_1e[0x2];
+
+ u8 reserved_at_20[0x1e0];
+};
+
+
struct mlx5_ifc_cmd_pas_bits {
u8 pa_h[0x20];
@@ -2339,7 +2349,7 @@ struct mlx5_ifc_fte_match_param_bits {
struct mlx5_ifc_fte_match_set_misc5_bits misc_parameters_5;
- u8 reserved_at_e00[0x200];
+ struct mlx5_ifc_fte_match_set_misc6_bits misc_parameters_6;
};
enum {
@@ -6984,6 +6994,7 @@ enum {
MLX5_QUERY_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS_3 = 0x4,
MLX5_QUERY_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS_4 = 0x5,
MLX5_QUERY_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS_5 = 0x6,
+ MLX5_QUERY_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS_6 = 0x7,
};
struct mlx5_ifc_query_flow_group_out_bits {
@@ -7245,6 +7256,7 @@ enum mlx5_reformat_ctx_type {
MLX5_REFORMAT_TYPE_REMOVE_HDR = 0x10,
MLX5_REFORMAT_TYPE_ADD_MACSEC = 0x11,
MLX5_REFORMAT_TYPE_DEL_MACSEC = 0x12,
+ MLX5_REFORMAT_TYPE_REMOVE_PSP_TRANSPORT = 0x16,
};
struct mlx5_ifc_alloc_packet_reformat_context_in_bits {
@@ -7368,6 +7380,7 @@ enum {
MLX5_ACTION_IN_FIELD_OUT_EMD_47_32 = 0x6F,
MLX5_ACTION_IN_FIELD_OUT_EMD_31_0 = 0x70,
MLX5_ACTION_IN_FIELD_PSP_SYNDROME = 0x71,
+ MLX5_ACTION_IN_FIELD_PSP_HEADER_1 = 0x78,
};
struct mlx5_ifc_alloc_modify_header_context_out_bits {
--
2.44.0
^ permalink raw reply related
* [PATCH mlx5-next 0/2] mlx5-next updates 2026-07-13
From: Tariq Toukan @ 2026-07-13 8:43 UTC (permalink / raw)
To: Leon Romanovsky, linux-rdma, Mark Bloch, netdev, Saeed Mahameed,
Tariq Toukan
Cc: Alexei Lazar, Alex Vesker, Andrew Lunn, Cosmin Ratiu,
David S. Miller, Dragos Tatulea, Eric Dumazet, Feng Liu,
Jakub Kicinski, Kees Cook, linux-kernel, Paolo Abeni,
Parav Pandit, Shay Drory, Simon Horman, Yevgeny Kliteynik
Hi,
This series contains mlx5 shared updates.
Regards,
Tariq
Cosmin Ratiu (1):
net/mlx5: ifc: Add PSP related fields
Shay Drory (1):
net/mlx5: Drop redundant esw_cap, reuse e_switch_cap
.../net/ethernet/mellanox/mlx5/core/fs_core.h | 12 +-----
.../mellanox/mlx5/core/steering/hws/cmd.c | 6 +--
include/linux/mlx5/device.h | 1 +
include/linux/mlx5/mlx5_ifc.h | 38 ++++++++++---------
4 files changed, 25 insertions(+), 32 deletions(-)
base-commit: ddbddbf8aee54bee038149187270c93a45478473
--
2.44.0
^ permalink raw reply
* [PATCH mlx5-next 1/2] net/mlx5: Drop redundant esw_cap, reuse e_switch_cap
From: Tariq Toukan @ 2026-07-13 8:43 UTC (permalink / raw)
To: Leon Romanovsky, linux-rdma, Mark Bloch, netdev, Saeed Mahameed,
Tariq Toukan
Cc: Alexei Lazar, Alex Vesker, Andrew Lunn, Cosmin Ratiu,
David S. Miller, Dragos Tatulea, Eric Dumazet, Feng Liu,
Jakub Kicinski, Kees Cook, linux-kernel, Paolo Abeni,
Parav Pandit, Shay Drory, Simon Horman, Yevgeny Kliteynik
In-Reply-To: <20260713084320.1015240-1-tariqt@nvidia.com>
From: Shay Drory <shayd@nvidia.com>
esw_manager_vport_number{,_valid} and merged_eswitch were read through a
separate mlx5_ifc_esw_cap_bits struct, but these bits live in the
e-switch capability that mlx5_ifc_e_switch_cap_bits already describes
(both overlay the same QUERY_HCA_CAP op_mod 0x9 output).
Add esw_manager_vport_number{,_valid} to mlx5_ifc_e_switch_cap_bits at
the same offsets, drop the redundant mlx5_ifc_esw_cap_bits and its
hca_cap_union member, and switch the only user (hws/cmd.c) to
capability.e_switch_cap.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Yevgeny Kliteynik <kliteyn@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../mellanox/mlx5/core/steering/hws/cmd.c | 6 +++---
include/linux/mlx5/mlx5_ifc.h | 21 +++++--------------
2 files changed, 8 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/cmd.c
index e624f5da96c8..8fae90101653 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/cmd.c
@@ -1172,13 +1172,13 @@ int mlx5hws_cmd_query_caps(struct mlx5_core_dev *mdev,
}
if (MLX5_GET(query_hca_cap_out, out,
- capability.esw_cap.esw_manager_vport_number_valid))
+ capability.e_switch_cap.esw_manager_vport_number_valid))
caps->eswitch_manager_vport_number =
MLX5_GET(query_hca_cap_out, out,
- capability.esw_cap.esw_manager_vport_number);
+ capability.e_switch_cap.esw_manager_vport_number);
caps->merged_eswitch = MLX5_GET(query_hca_cap_out, out,
- capability.esw_cap.merged_eswitch);
+ capability.e_switch_cap.merged_eswitch);
}
ret = mlx5_cmd_exec(mdev, in, sizeof(in), out, out_size);
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 4f59b7e8a3d5..7de01d4f1b5e 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -1042,20 +1042,6 @@ struct mlx5_ifc_wqe_based_flow_table_cap_bits {
u8 reserved_at_1c1[0x1f];
};
-struct mlx5_ifc_esw_cap_bits {
- u8 reserved_at_0[0x1d];
- u8 merged_eswitch[0x1];
- u8 reserved_at_1e[0x2];
-
- u8 reserved_at_20[0x40];
-
- u8 esw_manager_vport_number_valid[0x1];
- u8 reserved_at_61[0xf];
- u8 esw_manager_vport_number[0x10];
-
- u8 reserved_at_80[0x780];
-};
-
enum {
MLX5_COUNTER_SOURCE_ESWITCH = 0x0,
MLX5_COUNTER_FLOW_ESWITCH = 0x1,
@@ -1096,7 +1082,11 @@ struct mlx5_ifc_e_switch_cap_bits {
u8 log_max_esw_sf[0x5];
u8 esw_sf_base_id[0x10];
- u8 reserved_at_60[0x7a0];
+ u8 esw_manager_vport_number_valid[0x1];
+ u8 reserved_at_61[0xf];
+ u8 esw_manager_vport_number[0x10];
+
+ u8 reserved_at_80[0x780];
};
@@ -3855,7 +3845,6 @@ union mlx5_ifc_hca_cap_union_bits {
struct mlx5_ifc_flow_table_nic_cap_bits flow_table_nic_cap;
struct mlx5_ifc_flow_table_eswitch_cap_bits flow_table_eswitch_cap;
struct mlx5_ifc_wqe_based_flow_table_cap_bits wqe_based_flow_table_cap;
- struct mlx5_ifc_esw_cap_bits esw_cap;
struct mlx5_ifc_e_switch_cap_bits e_switch_cap;
struct mlx5_ifc_port_selection_cap_bits port_selection_cap;
struct mlx5_ifc_qos_cap_bits qos_cap;
--
2.44.0
^ permalink raw reply related
* [PATCH net v2] tipc: clear sock->sk on the failed-insert path in tipc_sk_create()
From: Daehyeon Ko @ 2026-07-13 8:23 UTC (permalink / raw)
To: netdev
Cc: Jon Maloy, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Tung Quang Nguyen, tipc-discussion,
linux-kernel, Daehyeon Ko, stable
When tipc_sk_create() fails to insert the new socket (tipc_sk_insert()
returns non-zero), its error path frees the sk with sk_free() but leaves
sock->sk pointing at the freed object:
if (tipc_sk_insert(tsk)) {
sk_free(sk);
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}
This is harmless for plain socket(): the syscall layer clears sock->ops
before releasing, so tipc_release() is never called. It is not harmless
on the accept() path. tipc_accept() creates the pre-allocated child
socket with tipc_sk_create(net, new_sock, 0, kern); on failure it leaves
new_sock->sk dangling and new_sock->ops non-NULL, and do_accept() then
fput()s the new file, so __sock_release() -> tipc_release() runs
lock_sock(new_sock->sk) on the freed sk -- a use-after-free write of the
sk_lock spinlock.
tipc_release() already guards this exact "failed accept() releases a
pre-allocated child" case with "if (sk == NULL) return 0;", but the
guard is bypassed because tipc_sk_create() left sock->sk non-NULL
(dangling) rather than NULL.
Clear sock->sk on the failed-insert path so the existing tipc_release()
NULL check fires and the use-after-free is avoided.
The tipc_sk_insert() failure is reached when the per-netns socket
rhashtable hits its max_size (tsk_rht_params.max_size = 1048576, ~2M
elements) -- i.e. once a netns holds ~2M TIPC sockets every insert
returns -E2BIG.
BUG: KASAN: slab-use-after-free in lock_sock_nested (net/core/sock.c:3839)
Write of size 8 at addr ffff8880047cdc38 by task init/1
lock_sock_nested (net/core/sock.c:3839)
tipc_release (net/tipc/socket.c:638)
__sock_release (net/socket.c:710)
sock_close (net/socket.c:1501)
__fput (fs/file_table.c:512)
Allocated by task 1:
sk_alloc (net/core/sock.c:2308)
tipc_sk_create (net/tipc/socket.c:487)
tipc_accept (net/tipc/socket.c:2744)
do_accept (net/socket.c:2034)
Freed by task 1:
__sk_destruct (net/core/sock.c:2391)
tipc_sk_create (net/tipc/socket.c:504)
tipc_accept (net/tipc/socket.c:2744)
do_accept (net/socket.c:2034)
Fixes: 07f6c4bc048a ("tipc: convert tipc reference table to use generic rhashtable")
Cc: stable@vger.kernel.org
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
v2: replace the raw KASAN backtrace in the commit message with the
decoded (scripts/decode_stacktrace.sh) file:line form, as requested
by Tung Quang Nguyen. No code change.
Link to v1: https://lore.kernel.org/netdev/20260710014440.2055584-1-4ncienth@gmail.com/
net/tipc/socket.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index e564341e0216..55e695748332 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -502,6 +502,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
tipc_set_sk_state(sk, TIPC_OPEN);
if (tipc_sk_insert(tsk)) {
sk_free(sk);
+ sock->sk = NULL;
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net] net: rnpgbe: Pass an expression directly in rnpgbe_rm_adapter()
From: Markus Elfring @ 2026-07-13 8:22 UTC (permalink / raw)
To: Dan Carpenter, netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
MD Danish Anwar, Michael Grzeschik, Paolo Abeni,
Uwe Kleine-König, Vadim Fedorenko, Yibo Dong, LKML,
kernel-janitors
In-Reply-To: <alScHkiC3EmGzOlI@stanley.mountain>
>> The address of a data structure member was determined before
>> a corresponding null pointer check in the implementation of
>> the function “rnpgbe_rm_adapter”.
>>
>> Thus avoid the risk for undefined behaviour by omitting the variable “hw”.
>> Pass the required address directly to a function call.
>>
>> This issue was detected by using the Coccinelle software.
>>
>> Fixes: 2ee95ec17e97c58b65e978a08b75fa8cb6424e4e ("net: rnpgbe: Add register_netdev")
>
> There is no NULL dereference here. It's just pointer math.
> No need for a Fixes tag.
How does your view fit to information in an article like “Fun with NULL pointers, part 1”(by Jonathan Corbet from 2009-07-20)?
https://lwn.net/Articles/342330/
Regards,
Markus
^ permalink raw reply
* [PATCH net] tcp: initialize standalone IPv4 ACK options
From: Yizhou Zhao @ 2026-07-13 8:18 UTC (permalink / raw)
To: netdev
Cc: Yizhou Zhao, Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima,
David S. Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel, Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li, Ke Xu,
stable
tcp_v4_send_ack() constructs standalone IPv4 TCP ACK replies on the stack
for SYN-RECV and TIME-WAIT paths. It currently zeroes only the TCP
header, not the accompanying option buffer.
TCP-AO options may have actual lengths that are not 4-byte aligned, while
the transmitted TCP header length is correctly rounded up to a 4-byte
boundary. tcp_ao_hash_hdr() writes only the MAC bytes, leaving the
TCP-AO option alignment padding in rep.opt uninitialized. With stack
auto-initialization disabled, those padding bytes can be copied into the
network packet and sent to the peer.
Zero the whole reply structure before writing options, so the alignment
padding bytes are initialized.
Fixes: decde2586b34 ("net/tcp: Add TCP-AO sign to twsk")
Cc: stable@vger.kernel.org
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Yuxiang Yang <yangyx22@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>
Assisted-by: Claude-Code:GLM-5.2-special
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
---
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index ec09f97..327ee96 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -922,7 +922,7 @@ static void tcp_v4_send_ack(const struct sock *sk,
struct sock *ctl_sk;
u64 transmit_time;
- memset(&rep.th, 0, sizeof(struct tcphdr));
+ memset(&rep, 0, sizeof(rep));
memset(&arg, 0, sizeof(arg));
arg.iov[0].iov_base = (unsigned char *)&rep;
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v8 00/10] Fix missing fops.owner in Rust DRM/misc abstractions
From: Alice Ryhl @ 2026-07-13 8:17 UTC (permalink / raw)
To: Alvin Sun
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Greg Kroah-Hartman, Rafael J. Wysocki, David Airlie,
Simona Vetter, Daniel Almeida, Arnd Bergmann, Brendan Higgins,
David Gow, Rae Moar, Breno Leitao, Jens Axboe, Dave Ertman,
Leon Romanovsky, Igor Korotin, FUJITA Tomonori, Bjorn Helgaas,
Krzysztof Wilczyński, Arve Hjønnevåg, Todd Kjos,
Christian Brauner, Carlos Llamas, rust-for-linux, linux-modules,
driver-core, dri-devel, nova-gpu, linux-kselftest, kunit-dev,
linux-block, linux-kernel, netdev, linux-pci
In-Reply-To: <20260713-fix-fops-owner-v8-0-2495cfa82d47@linux.dev>
On Mon, Jul 13, 2026 at 02:45:21PM +0800, Alvin Sun wrote:
> During tyr debugfs development, a kernel NULL pointer dereference was
> encountered after `rmmod tyr` while gnome-shell still held /dev/card1 open:
I left a few minor comments, but otherwise LGTM.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply
* Re: [PATCH v8 10/10] rust: module: update MAINTAINERS to cover module.rs
From: Alice Ryhl @ 2026-07-13 8:16 UTC (permalink / raw)
To: Alvin Sun
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Greg Kroah-Hartman, Rafael J. Wysocki, David Airlie,
Simona Vetter, Daniel Almeida, Arnd Bergmann, Brendan Higgins,
David Gow, Rae Moar, Breno Leitao, Jens Axboe, Dave Ertman,
Leon Romanovsky, Igor Korotin, FUJITA Tomonori, Bjorn Helgaas,
Krzysztof Wilczyński, Arve Hjønnevåg, Todd Kjos,
Christian Brauner, Carlos Llamas, rust-for-linux, linux-modules,
driver-core, dri-devel, nova-gpu, linux-kselftest, kunit-dev,
linux-block, linux-kernel, netdev, linux-pci
In-Reply-To: <20260713-fix-fops-owner-v8-10-2495cfa82d47@linux.dev>
On Mon, Jul 13, 2026 at 02:45:31PM +0800, Alvin Sun wrote:
> Module types now live in `rust/kernel/module.rs` alongside
> `rust/kernel/module_param.rs`. Update the MODULE SUPPORT file pattern
> from `rust/kernel/module_param.rs` to `rust/kernel/module*.rs` so both
> files are covered.
>
> Cc: Petr Pavlu <petr.pavlu@suse.com>
> Assisted-by: opencode:glm-5.2
> Link: https://lore.kernel.org/rust-for-linux/8ea21b29-9baf-4926-a16f-7d21c5a1a1b8@suse.com
> Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
> ---
> MAINTAINERS | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e035a3be797c4..74733de3e41ee 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17984,7 +17984,7 @@ F: include/linux/module*.h
> F: kernel/module/
> F: lib/test_kmod.c
> F: lib/tests/module/
> -F: rust/kernel/module_param.rs
> +F: rust/kernel/module*.rs
> F: rust/macros/module.rs
> F: scripts/module*
> F: tools/testing/selftests/kmod/
Is there a reason why this is a separate commit from the commit that
introduces rust/kernel/module.rs file?
Alice
^ permalink raw reply
* [PATCH] [RFC] net-ipv6: ipv4 emulation support
From: Maciej Żenczykowski @ 2026-07-13 8:16 UTC (permalink / raw)
To: Maciej Żenczykowski
Cc: Linux Network Development Mailing List,
Fernando Fernandez Mancera, Maciej Żenczykowski
Change-Id: Ibab1a59f4730f3fd0a7d14fffc5100a02a170ac6
---
gconfigs/Gconfig.net-configs.ipv6 | 2 +
include/linux/ipv6.h | 6 +-
include/linux/socket.h | 5 +-
include/net/compat.h | 2 +-
net/compat.c | 6 +-
net/core/iovec.c | 4 +-
net/ipv6/Kconfig | 19 +++
net/ipv6/af_inet6.c | 5 +
net/ipv6/ipv6_sockglue.c | 8 +-
net/ipv6/sysctl_net_ipv6.c | 26 ++++
net/socket.c | 211 +++++++++++++++++++++++++++---
11 files changed, 260 insertions(+), 34 deletions(-)
create mode 100644 gconfigs/Gconfig.net-configs.ipv6
diff --git a/gconfigs/Gconfig.net-configs.ipv6 b/gconfigs/Gconfig.net-configs.ipv6
new file mode 100644
index 000000000000..9eb5694bae8e
--- /dev/null
+++ b/gconfigs/Gconfig.net-configs.ipv6
@@ -0,0 +1,2 @@
+CONFIG_IPV6_IPV4_EMULATE=y
+CONFIG_IPV6_IPV4_EMULATE_SYSCTL_DEFAULT=y
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index ff560537dd61..8bc5306094b5 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -157,11 +157,13 @@ struct ipv6_pinfo {
#if defined(__BIG_ENDIAN_BITFIELD)
/* Packed in 16bits. */
__s16 mcast_hops:9;
- __u16 __unused_2:6,
+ __u16 __unused_2:5,
+ ipv4emu:1,
mc_loop:1;
#else
__u16 mc_loop:1,
- __unused_2:6;
+ ipv4emu:1,
+ __unused_2:5;
__s16 mcast_hops:9;
#endif
int ucast_oif;
diff --git a/include/linux/socket.h b/include/linux/socket.h
index bb9b83640070..4ae959a3443b 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -8,6 +8,7 @@
#include <linux/types.h> /* pid_t */
#include <linux/compiler.h> /* __user */
#include <uapi/linux/socket.h>
+struct sock; /* from net/sock.h */
struct pid;
struct cred;
@@ -312,8 +313,8 @@ extern int csum_partial_copy_fromiovecend(unsigned char *kdata,
extern unsigned long iov_pages(const struct iovec *iov, int offset,
unsigned long nr_segs);
-extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode);
-extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr);
+extern int verify_iovec(struct sock *sk, struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode);
+extern int move_addr_to_kernel(struct sock *sk, void __user *uaddr, int *ulen, struct sockaddr_storage *kaddr);
extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
struct timespec;
diff --git a/include/net/compat.h b/include/net/compat.h
index 3b603b199c01..b9ee7aa5a31f 100644
--- a/include/net/compat.h
+++ b/include/net/compat.h
@@ -41,7 +41,7 @@ int compat_sock_get_timestampns(struct sock *, struct timespec __user *);
#endif /* defined(CONFIG_COMPAT) */
int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *);
-int verify_compat_iovec(struct msghdr *, struct iovec *,
+int verify_compat_iovec(struct sock *, struct msghdr *, struct iovec *,
struct sockaddr_storage *, int);
asmlinkage long compat_sys_sendmsg(int, struct compat_msghdr __user *,
unsigned int);
diff --git a/net/compat.c b/net/compat.c
index bc8aeefddf3f..f66bd6552204 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -80,15 +80,15 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg)
}
/* I've named the args so it is easy to tell whose space the pointers are in. */
-int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov,
+int verify_compat_iovec(struct sock *sk, struct msghdr *kern_msg, struct iovec *kern_iov,
struct sockaddr_storage *kern_address, int mode)
{
int tot_len;
if (kern_msg->msg_name && kern_msg->msg_namelen) {
if (mode == VERIFY_READ) {
- int err = move_addr_to_kernel(kern_msg->msg_name,
- kern_msg->msg_namelen,
+ int err = move_addr_to_kernel(sk, kern_msg->msg_name,
+ &kern_msg->msg_namelen,
kern_address);
if (err < 0)
return err;
diff --git a/net/core/iovec.c b/net/core/iovec.c
index e1ec45ab1e63..a7a393cae2a7 100644
--- a/net/core/iovec.c
+++ b/net/core/iovec.c
@@ -35,7 +35,7 @@
* in any case.
*/
-int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode)
+int verify_iovec(struct sock *sk, struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode)
{
int size, ct, err;
@@ -43,7 +43,7 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a
if (mode == VERIFY_READ) {
void __user *namep;
namep = (void __user __force *) m->msg_name;
- err = move_addr_to_kernel(namep, m->msg_namelen,
+ err = move_addr_to_kernel(sk, namep, &m->msg_namelen,
address);
if (err < 0)
return err;
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 438a73aa777c..69924a6c2257 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -21,6 +21,25 @@ menuconfig IPV6
if IPV6
+config IPV6_IPV4_EMULATE
+ bool "IPv6: IPv4 socket interface emulation support"
+ default n
+ depends on IPV6=y
+ ---help---
+ Whether to include support code which allows using a kernel IPv6
+ socket from userspace as if it was an IPv4 socket.
+
+ ie. this is a userspace-kernel IP address translation shim layer.
+
+config IPV6_IPV4_EMULATE_SYSCTL_DEFAULT
+ bool "IPv6: IPv4 socket emulation - default sysctl setting"
+ default n
+ depends on IPV6_IPV4_EMULATE
+ ---help---
+ Default value for /proc/sys/net/ipv6/ipv4_emulation
+ The content of this sysctl file determines whether userspace asking
+ for IPv4 sockets gets IPv6 sockets with IPv4 emulation turned on.
+
config IPV6_ROUTER_PREF
bool "IPv6: Router Preference (RFC 4191) support"
---help---
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index e8c4400f23e9..4f08abdf06e0 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -476,6 +476,11 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ if (inet6_sk(sk)->ipv4emu)
+ return inet_ioctl(sock, cmd, arg);
+#endif
+
switch (cmd) {
case SIOCGSTAMP:
return sock_get_timestamp(sk, (struct timeval __user *)arg);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index e1a9583bb419..2e97c6d55b62 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -857,7 +857,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
if (level == SOL_IP && sk->sk_type != SOCK_RAW)
return udp_prot.setsockopt(sk, level, optname, optval, optlen);
- if (level != SOL_IPV6)
+ if ((level != SOL_IPV6) || (inet6_sk(sk)->ipv4emu))
return -ENOPROTOOPT;
err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
@@ -888,7 +888,7 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
return udp_prot.setsockopt(sk, level, optname, optval, optlen);
}
- if (level != SOL_IPV6)
+ if ((level != SOL_IPV6) || (inet6_sk(sk)->ipv4emu))
return -ENOPROTOOPT;
if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
@@ -1297,7 +1297,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
if (level == SOL_IP && sk->sk_type != SOCK_RAW)
return udp_prot.getsockopt(sk, level, optname, optval, optlen);
- if (level != SOL_IPV6)
+ if ((level != SOL_IPV6) || (inet6_sk(sk)->ipv4emu))
return -ENOPROTOOPT;
err = do_ipv6_getsockopt(sk, level, optname, optval, optlen, 0);
@@ -1334,7 +1334,7 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
return udp_prot.getsockopt(sk, level, optname, optval, optlen);
}
- if (level != SOL_IPV6)
+ if ((level != SOL_IPV6) || (inet6_sk(sk)->ipv4emu))
return -ENOPROTOOPT;
if (optname == MCAST_MSFILTER)
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index c5c10fafcfe2..0ab6a8d5aa57 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -6,6 +6,7 @@
*/
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/sysctl.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
@@ -18,6 +19,20 @@
static int one = 1;
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+static int zero;
+
+# if defined(CONFIG_IPV6_IPV4_EMULATE_SYSCTL_DEFAULT)
+bool sysctl_ipv4_emulation = true;
+# else
+bool sysctl_ipv4_emulation = false;
+# endif
+
+module_param_named(ipv4_emulation, sysctl_ipv4_emulation, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ipv4_emulation,
+ "Auto promote PF_INET sockets into PF_INET6 sockets, with IPv4 API emulation.");
+#endif
+
static struct ctl_table ipv6_table_template[] = {
{
.procname = "bindv6only",
@@ -73,6 +88,17 @@ static struct ctl_table ipv6_rotable[] = {
.proc_handler = proc_dointvec_minmax,
.extra1 = &one
},
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ {
+ .procname = "ipv4_emulation",
+ .data = &sysctl_ipv4_emulation,
+ .maxlen = sizeof(bool),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+#endif
{ }
};
diff --git a/net/socket.c b/net/socket.c
index fe20c319a0bb..e7d2f5281ff3 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -101,6 +101,7 @@
#include <linux/netfilter.h>
#include <linux/if_tun.h>
+#include <linux/ipv6.h>
#include <linux/ipv6_route.h>
#include <linux/route.h>
#include <linux/sockios.h>
@@ -178,30 +179,171 @@ static DEFINE_PER_CPU(int, sockets_in_use);
* divide and look after the messy bits.
*/
+static int ipv6_promote_ipv4_address(struct sock *sk,
+ struct sockaddr_storage * kaddr, int * addr_len)
+{
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ if (!sk) {
+ pr_warning("ipv6_promote_ipv4_address: no sk\n");
+ return 0;
+ }
+
+ if (sk->sk_family != PF_INET6) {
+ //pr_warning("ipv6_promote_ipv4_address: sk_family == %d\n", sk->sk_family);
+ return 0;
+ }
+
+ if (!inet6_sk(sk)->ipv4emu) {
+ //pr_warning("ipv6_promote_ipv4_address: not ipv4emu\n");
+ return 0;
+ }
+
+ if (kaddr->ss_family == AF_UNSPEC) {
+ //pr_warning("ipv6_promote_ipv4_address: unspec\n");
+ return 0;
+ }
+
+ if (kaddr->ss_family != AF_INET) {
+ pr_warning("ipv6_promote_ipv4_address: addr is %d\n", kaddr->ss_family);
+ return -EAFNOSUPPORT;
+ }
+
+ if (*addr_len < sizeof(struct sockaddr_in)) {
+ pr_warning("ipv6_promote_ipv4_address: addr_len == %d\n", *addr_len);
+ return -EINVAL;
+ }
+
+ {
+ const struct sockaddr_in * kaddr4 = (const struct sockaddr_in *)kaddr;
+ struct sockaddr_in6 * kaddr6 = (struct sockaddr_in6 *)kaddr;
+ __be16 port = kaddr4->sin_port;
+ __be32 ipv4 = kaddr4->sin_addr.s_addr;
+
+ //*addr_len = sizeof(struct sockaddr_in6);
+ *addr_len = 24; //SIN6_LEN_RFC2133; /* 24 = 2+2+4+16, ie. without scope id */
+// Why not fill in scope_id with 0 ?
+// One way to avoid the magic 24 constant is to use offsetof(struct sockaddr_in6, scope_id)
+ kaddr6->sin6_family = AF_INET6;
+ kaddr6->sin6_port = port;
+ kaddr6->sin6_flowinfo = htonl(0);
+ kaddr6->sin6_addr.s6_addr32[0] = htonl(0);
+ kaddr6->sin6_addr.s6_addr32[1] = htonl(0);
+ kaddr6->sin6_addr.s6_addr32[2] = ntohl(ipv4) ? htonl(0xFFFF) : htonl(0);
+ kaddr6->sin6_addr.s6_addr32[3] = ipv4;
+ kaddr6->sin6_scope_id = 0;
+
+ pr_debug("ipv6_promote_ipv4_address: ok [%08X:%d]\n", ntohl(ipv4), ntohs(port));
+ }
+#endif
+ return 0;
+}
+
+static int ipv6_demote_ipv6_address(struct sock *sk, struct sockaddr_storage * kaddr, int * addr_len) {
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ if (!sk) {
+ pr_warning("ipv6_demote_ipv6_address: no sk\n");
+ return 0;
+ }
+
+ if (sk->sk_family != PF_INET6) {
+ //pr_warning("ipv6_demote_ipv6_address: sk_family == %d\n", sk->sk_family);
+ return 0;
+ }
+
+ if (!inet6_sk(sk)->ipv4emu) {
+ //pr_warning("ipv6_demote_ipv6_address: not ipv4emu\n");
+ return 0;
+ }
+
+ if (kaddr->ss_family != AF_INET6) {
+ pr_warning("ipv6_demote_ipv6_address: addr is %d\n", kaddr->ss_family);
+ return -EAFNOSUPPORT;
+ }
+
+ if (*addr_len < sizeof(struct sockaddr_in6)) {
+ pr_warning("ipv6_demote_ipv6_address: addr_len == %d\n", *addr_len);
+ return -EINVAL;
+ }
+
+ {
+ struct sockaddr_in6 const * kaddr6 = (struct sockaddr_in6 const *)kaddr;
+ struct sockaddr_in * kaddr4 = (struct sockaddr_in *)kaddr;
+ __be16 port = kaddr6->sin6_port;
+ __be32 ipv6a = kaddr6->sin6_addr.s6_addr32[0];
+ __be32 ipv6b = kaddr6->sin6_addr.s6_addr32[1];
+ __be32 ipv6c = kaddr6->sin6_addr.s6_addr32[2];
+ __be32 ipv4 = kaddr6->sin6_addr.s6_addr32[3];
+
+// please use ipv6_addr_v4mapped()
+
+ if (ipv6a) {
+ pr_warning("ipv6_demote_ipv6_address: not mapped [1]\n");
+ return -EINVAL;
+ }
+
+ if (ipv6b) {
+ pr_warning("ipv6_demote_ipv6_address: not mapped [2]\n");
+ return -EINVAL;
+ }
+
+ if ((ipv6c != htonl(0)) && (ipv6c != htonl(0xFFFF))) {
+ pr_warning("ipv6_demote_ipv6_address: not mapped [3]\n");
+ return -EINVAL;
+ }
+
+ if ((ipv6c == htonl(0)) && (ipv4 != htonl(0))) {
+ pr_warning("ipv6_demote_ipv6_address: not mapped [4]\n");
+ return -EINVAL;
+ }
+
+// Really avoid those magic values...
+// Since I doubt this is performance critical, just do the whole struct clear at the beginning:
+// memset(uaddr4, 0, sizeof(*uaddr4));
+
+ *addr_len = sizeof(struct sockaddr_in); // 2 + 2 + 4 + 8 padding
+ kaddr4->sin_family = AF_INET;
+ kaddr4->sin_port = port;
+ kaddr4->sin_addr.s_addr = ipv4;
+ memset(kaddr4->sin_zero, 0, 8);
+
+ pr_debug("ipv6_demote_ipv6_address: ok [%08X:%d]\n", ntohl(ipv4), ntohs(port));
+ }
+#endif
+ return 0;
+}
+
/**
* move_addr_to_kernel - copy a socket address into kernel space
+ * @sk: socket which is receiving the address (or NULL)
* @uaddr: Address in user space
* @kaddr: Address in kernel space
- * @ulen: Length in user space
+ * @ulen: pointer to user space length
*
* The address is copied into kernel space. If the provided address is
* too long an error code of -EINVAL is returned. If the copy gives
* invalid addresses -EFAULT is returned. On a success 0 is returned.
*/
-int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr)
+int move_addr_to_kernel(struct sock *sk, void __user *uaddr, int *ulen,
+ struct sockaddr_storage *kaddr)
{
- if (ulen < 0 || ulen > sizeof(struct sockaddr_storage))
+ int err;
+
+ if (*ulen < 0 || *ulen > sizeof(struct sockaddr_storage))
return -EINVAL;
- if (ulen == 0)
+ if (*ulen == 0)
return 0;
- if (copy_from_user(kaddr, uaddr, ulen))
+ if (copy_from_user(kaddr, uaddr, *ulen))
return -EFAULT;
- return audit_sockaddr(ulen, kaddr);
+ err = audit_sockaddr(*ulen, kaddr);
+ if (err)
+ return err;
+ return ipv6_promote_ipv4_address(sk, kaddr, ulen);
}
/**
* move_addr_to_user - copy an address to user space
+ * @sk: socket which is generating the address (or NULL)
* @kaddr: kernel space address
* @klen: length of address in kernel
* @uaddr: user space address
@@ -217,7 +359,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *k
* specified. Zero is returned for a success.
*/
-static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
+static int move_addr_to_user(struct sock *sk, struct sockaddr_storage *kaddr, int klen,
void __user *uaddr, int __user *ulen)
{
int err;
@@ -225,6 +367,9 @@ static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
BUG_ON(klen > sizeof(struct sockaddr_storage));
err = get_user(len, ulen);
+ if (err)
+ return err;
+ err = ipv6_demote_ipv6_address(sk, kaddr, &klen);
if (err)
return err;
if (len > klen)
@@ -1249,12 +1394,19 @@ call_kill:
}
EXPORT_SYMBOL(sock_wake_async);
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+extern bool sysctl_ipv4_emulation;
+#endif
+
int __sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
{
int err;
struct socket *sock;
const struct net_proto_family *pf;
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ bool ipv4emu = false;
+#endif
/*
* Check protocol is in range
@@ -1283,6 +1435,13 @@ int __sock_create(struct net *net, int family, int type, int protocol,
if (err)
return err;
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ if (sysctl_ipv4_emulation && !kern && family == PF_INET) {
+ family = PF_INET6;
+ ipv4emu = true;
+ }
+#endif
+
/*
* Allocate the socket and allow the family to set things up. if
* the protocol is 0, the family is instructed to select an appropriate
@@ -1328,6 +1487,14 @@ int __sock_create(struct net *net, int family, int type, int protocol,
if (err < 0)
goto out_module_put;
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ if (ipv4emu) {
+ inet6_sk(sock->sk)->ipv4emu = 1;
+ /* we need to clear ipv6only, since it could have defaulted to true via sysctl */
+ sock->sk->sk_ipv6only = 0;
+ }
+#endif
+
/*
* Now to bump the refcnt of the [loadable] module that owns this
* socket at sock_release time we decrement its refcnt.
@@ -1532,7 +1699,7 @@ SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
- err = move_addr_to_kernel(umyaddr, addrlen, &address);
+ err = move_addr_to_kernel(sock->sk, umyaddr, &addrlen, &address);
if (err >= 0) {
err = security_socket_bind(sock,
(struct sockaddr *)&address,
@@ -1646,7 +1813,7 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
err = -ECONNABORTED;
goto out_fd;
}
- err = move_addr_to_user(&address,
+ err = move_addr_to_user(newsock->sk, &address,
len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
goto out_fd;
@@ -1695,7 +1862,7 @@ SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
- err = move_addr_to_kernel(uservaddr, addrlen, &address);
+ err = move_addr_to_kernel(sock->sk, uservaddr, &addrlen, &address);
if (err < 0)
goto out_put;
@@ -1735,7 +1902,7 @@ SYSCALL_DEFINE3(getsockname, int, fd, struct sockaddr __user *, usockaddr,
err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0);
if (err)
goto out_put;
- err = move_addr_to_user(&address, len, usockaddr, usockaddr_len);
+ err = move_addr_to_user(sock->sk, &address, len, usockaddr, usockaddr_len);
out_put:
fput_light(sock->file, fput_needed);
@@ -1767,7 +1934,7 @@ SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr,
sock->ops->getname(sock, (struct sockaddr *)&address, &len,
1);
if (!err)
- err = move_addr_to_user(&address, len, usockaddr,
+ err = move_addr_to_user(sock->sk, &address, len, usockaddr,
usockaddr_len);
fput_light(sock->file, fput_needed);
}
@@ -1806,7 +1973,7 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
msg.msg_controllen = 0;
msg.msg_namelen = 0;
if (addr) {
- err = move_addr_to_kernel(addr, addr_len, &address);
+ err = move_addr_to_kernel(sock->sk, addr, &addr_len, &address);
if (err < 0)
goto out_put;
msg.msg_name = (struct sockaddr *)&address;
@@ -1871,7 +2038,7 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
err = sock_recvmsg(sock, &msg, size, flags);
if (err >= 0 && addr != NULL) {
- err2 = move_addr_to_user(&address,
+ err2 = move_addr_to_user(sock->sk, &address,
msg.msg_namelen, addr, addr_len);
if (err2 < 0)
err = err2;
@@ -2042,9 +2209,9 @@ static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
/* This will also move the address data into kernel space */
if (MSG_CMSG_COMPAT & flags) {
- err = verify_compat_iovec(msg_sys, iov, &address, VERIFY_READ);
+ err = verify_compat_iovec(sock->sk, msg_sys, iov, &address, VERIFY_READ);
} else
- err = verify_iovec(msg_sys, iov, &address, VERIFY_READ);
+ err = verify_iovec(sock->sk, msg_sys, iov, &address, VERIFY_READ);
if (err < 0)
goto out_freeiov;
total_len = err;
@@ -2258,9 +2425,9 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
uaddr = (__force void __user *)msg_sys->msg_name;
uaddr_len = COMPAT_NAMELEN(msg);
if (MSG_CMSG_COMPAT & flags)
- err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
+ err = verify_compat_iovec(sock->sk, msg_sys, iov, &addr, VERIFY_WRITE);
else
- err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
+ err = verify_iovec(sock->sk, msg_sys, iov, &addr, VERIFY_WRITE);
if (err < 0)
goto out_freeiov;
total_len = err;
@@ -2280,7 +2447,7 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
len = err;
if (uaddr != NULL) {
- err = move_addr_to_user(&addr,
+ err = move_addr_to_user(sock->sk, &addr,
msg_sys->msg_namelen, uaddr,
uaddr_len);
if (err < 0)
@@ -3159,7 +3326,11 @@ static int routing_ioctl(struct net *net, struct socket *sock,
u32 rtdev;
mm_segment_t old_fs = get_fs();
- if (sock && sock->sk && sock->sk->sk_family == AF_INET6) { /* ipv6 */
+ if (sock && sock->sk && sock->sk->sk_family == AF_INET6
+#if defined(CONFIG_IPV6_IPV4_EMULATE)
+ && !inet6_sk(sock->sk)->ipv4emu
+#endif
+ ) { /* ipv6 */
struct in6_rtmsg32 __user *ur6 = argp;
ret = copy_from_user(&r6.rtmsg_dst, &(ur6->rtmsg_dst),
3 * sizeof(struct in6_addr));
--
2.55.0.795.g602f6c329a-goog
^ permalink raw reply related
* Re: [PATCH net-next v6 2/2] net: dsa: realtek: rtl8365mb: add HSGMII support for RTL8367S
From: Stacho_P @ 2026-07-13 8:14 UTC (permalink / raw)
To: Johan Alvarado
Cc: Andrew Lunn, Vladimir Oltean, Alvin Sipraga, Linus Walleij,
Russell King, Maxime Chevallier, Luiz Angelo Daros de Luca,
Mieczyslaw Nalewaj, netdev, Stanisław Pal
In-Reply-To: <20260711-rtl8367s-sgmii-v6-2-88f7944ddca7@c127.dev>
Tested this series on a TP-Link Archer AX55 v1: RTL8367S with the
SerDes on external interface 1 running HSGMII (2500base-x fixed-link)
to the SoC (Qualcomm IPQ5018 stmmac gmac1). Base was OpenWrt's 6.12
kernel; the only adjustment needed was dropping the neg_mode parameter
from pcs_get_state() for the older phylink API there.
The trunk comes up at 2.5Gbps and passes traffic (all five front ports
are behind it, NAT routed through the CPU port plus wire-speed L2 on
the switch). Warm reboots and short power cycles bring it up clean
every time, no SerDes firmware involved.
One observation, quite possibly marginal silicon on my unit: after the
device has been powered off for several hours, the first boot brings
the link up (2.5Gbps/Full reported, phylink happy) but the data path
is heavily degraded - 60-70% packet loss, the surviving packets at
normal sub-ms RTT. Re-running the PCS sequence via admin down/up of
the CPU port re-rolls the dice (15% and 40% loss on two consecutive
attempts) but did not fully recover it; a soft reboot (full re-probe
including the chip reset) always restores a clean link. Short
power-offs (~a minute) do not reproduce this. For what it's worth, the
vendor firmware for this board keeps the DW8051 link-management loop
running permanently, which would effectively keep retrying out of
exactly such a state.
Happy to test follow-up patches if you want to poke at that; I don't
think it should block the series - the feature itself works as
advertised.
Tested-by: Stanisław Pal <kuncy7@gmail.com>
^ permalink raw reply
* Re: [PATCH v8 08/10] rust: binder: use `LocalModule` for `THIS_MODULE`
From: Alice Ryhl @ 2026-07-13 8:13 UTC (permalink / raw)
To: Alvin Sun
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Greg Kroah-Hartman, Rafael J. Wysocki, David Airlie,
Simona Vetter, Daniel Almeida, Arnd Bergmann, Brendan Higgins,
David Gow, Rae Moar, Breno Leitao, Jens Axboe, Dave Ertman,
Leon Romanovsky, Igor Korotin, FUJITA Tomonori, Bjorn Helgaas,
Krzysztof Wilczyński, Arve Hjønnevåg, Todd Kjos,
Christian Brauner, Carlos Llamas, rust-for-linux, linux-modules,
driver-core, dri-devel, nova-gpu, linux-kselftest, kunit-dev,
linux-block, linux-kernel, netdev, linux-pci
In-Reply-To: <20260713-fix-fops-owner-v8-8-2495cfa82d47@linux.dev>
On Mon, Jul 13, 2026 at 02:45:29PM +0800, Alvin Sun wrote:
> Replace the `THIS_MODULE` static reference in the binder fops with
> `this_module::<LocalModule>()`, consistent with the move of
> `THIS_MODULE` into the `ModuleMetadata` trait.
>
> Assisted-by: opencode:glm-5.2
> Reviewed-by: Gary Guo <gary@garyguo.net>
> Acked-by: Danilo Krummrich <dakr@kernel.org>
> Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
Most other Rust Binder commits use 'rust_binder:' prefix rather than
'rust: binder:'.
Alice
^ permalink raw reply
* [PATCH net v3 06/14] rxrpc: Fix generation of notifications after call completion
From: David Howells @ 2026-07-13 8:10 UTC (permalink / raw)
To: netdev
Cc: David Howells, Marc Dionne, Jakub Kicinski, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-afs, linux-kernel,
stable
In-Reply-To: <20260713081022.2186481-1-dhowells@redhat.com>
AF_RXRPC may generate a notification to the application after a call has
completed because it generates one notification when
rxrpc_input_split_jumbo() queues the final packet and completes the call
and then generates another when rxrpc_input_split_jumbo() does the
aggregated data receive notification at the end of the function.
This might cause the AFS filesystem to malfunction because it tries to
queue the afs_call for processing an extra time. Most of the time this
happens quickly enough that the second queue_work skips, but sometimes this
means that the call work may happen a second time with implications for
afs_call lifetime management.
Fix this by:
(1) Create a lighter version of rxrpc_notify_socket() that's just used to
requeue a call for rxrpc_recvmsg() without creating another
notification.
(2) Move rxrpc_notify_socket() to call_state.c and rename it to
__rxrpc_notify_socket().
(3) Create a wrapper called rxrpc_notify_socket() that skips the
notification if a call is completed.
(4) Make rxrpc_set_call_completion() call __rxrpc_notify_socket() to avoid
the skip-if-completed check.
Fixes: 2d1faf7a0ca3 ("rxrpc: Simplify skbuff accounting in receive path")
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Eric Dumazet <edumazet@google.com>
cc: "David S. Miller" <davem@davemloft.net>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: linux-afs@lists.infradead.org
cc: stable@kernel.org
---
include/trace/events/rxrpc.h | 1 +
net/rxrpc/call_state.c | 57 +++++++++++++++++++++++++++++++++++-
net/rxrpc/recvmsg.c | 42 +++++++++-----------------
3 files changed, 71 insertions(+), 29 deletions(-)
diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index 8f3e3967885a..d7c7b04d69fc 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -343,6 +343,7 @@
EM(rxrpc_call_see_distribute_error, "SEE dist-err") \
EM(rxrpc_call_see_input, "SEE input ") \
EM(rxrpc_call_see_notify_released, "SEE nfy-rlsd") \
+ EM(rxrpc_call_see_notify_skipped, "SEE nfy-skip") \
EM(rxrpc_call_see_recvmsg, "SEE recvmsg ") \
EM(rxrpc_call_see_recvmsg_requeue, "SEE recv-rqu") \
EM(rxrpc_call_see_recvmsg_requeue_first, "SEE recv-rqF") \
diff --git a/net/rxrpc/call_state.c b/net/rxrpc/call_state.c
index 6afb54373ebb..6e402312e145 100644
--- a/net/rxrpc/call_state.c
+++ b/net/rxrpc/call_state.c
@@ -7,6 +7,61 @@
#include "ar-internal.h"
+/*
+ * Post a call for attention by the socket or kernel service. Further
+ * notifications are suppressed by putting recvmsg_link on a dummy queue.
+ */
+static void __rxrpc_notify_socket(struct rxrpc_call *call)
+{
+ struct rxrpc_sock *rx;
+ struct sock *sk;
+
+ if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) {
+ rxrpc_see_call(call, rxrpc_call_see_notify_released);
+ return;
+ }
+
+ rcu_read_lock();
+
+ rx = rcu_dereference(call->socket);
+ sk = &rx->sk;
+ if (rx && sk->sk_state < RXRPC_CLOSE) {
+ if (call->notify_rx) {
+ spin_lock_irq(&call->notify_lock);
+ call->notify_rx(sk, call, call->user_call_ID);
+ spin_unlock_irq(&call->notify_lock);
+ } else {
+ spin_lock_irq(&rx->recvmsg_lock);
+ if (list_empty(&call->recvmsg_link)) {
+ rxrpc_get_call(call, rxrpc_call_get_notify_socket);
+ list_add_tail(&call->recvmsg_link, &rx->recvmsg_q);
+ }
+ spin_unlock_irq(&rx->recvmsg_lock);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ _debug("call %ps", sk->sk_data_ready);
+ sk->sk_data_ready(sk);
+ }
+ }
+ }
+
+ rcu_read_unlock();
+}
+
+/*
+ * Post a call for attention by the socket or kernel service. Further
+ * notifications are suppressed by putting recvmsg_link on a dummy queue.
+ */
+void rxrpc_notify_socket(struct rxrpc_call *call)
+{
+ if (rxrpc_call_is_complete(call)) {
+ rxrpc_see_call(call, rxrpc_call_see_notify_skipped);
+ return;
+ }
+
+ __rxrpc_notify_socket(call);
+}
+
/*
* Transition a call to the complete state.
*/
@@ -25,7 +80,7 @@ bool rxrpc_set_call_completion(struct rxrpc_call *call,
rxrpc_set_call_state(call, RXRPC_CALL_COMPLETE);
trace_rxrpc_call_complete(call);
wake_up(&call->waitq);
- rxrpc_notify_socket(call);
+ __rxrpc_notify_socket(call);
return true;
}
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index efcba4b2e74f..28b2148b5693 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -17,13 +17,12 @@
#include "ar-internal.h"
/*
- * Post a call for attention by the socket or kernel service. Further
- * notifications are suppressed by putting recvmsg_link on a dummy queue.
+ * Requeue a call for recvmsg() to pick up.
*/
-void rxrpc_notify_socket(struct rxrpc_call *call)
+static void rxrpc_requeue_call(struct socket *sock, struct rxrpc_call *call)
{
- struct rxrpc_sock *rx;
- struct sock *sk;
+ struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+ struct sock *sk = &rx->sk;
_enter("%d", call->debug_id);
@@ -32,31 +31,18 @@ void rxrpc_notify_socket(struct rxrpc_call *call)
return;
}
- rcu_read_lock();
-
- rx = rcu_dereference(call->socket);
- sk = &rx->sk;
- if (rx && sk->sk_state < RXRPC_CLOSE) {
- if (call->notify_rx) {
- spin_lock_irq(&call->notify_lock);
- call->notify_rx(sk, call, call->user_call_ID);
- spin_unlock_irq(&call->notify_lock);
- } else {
- spin_lock_irq(&rx->recvmsg_lock);
- if (list_empty(&call->recvmsg_link)) {
- rxrpc_get_call(call, rxrpc_call_get_notify_socket);
- list_add_tail(&call->recvmsg_link, &rx->recvmsg_q);
- }
- spin_unlock_irq(&rx->recvmsg_lock);
+ spin_lock_irq(&rx->recvmsg_lock);
+ if (list_empty(&call->recvmsg_link)) {
+ rxrpc_get_call(call, rxrpc_call_get_notify_socket);
+ list_add_tail(&call->recvmsg_link, &rx->recvmsg_q);
+ }
+ spin_unlock_irq(&rx->recvmsg_lock);
- if (!sock_flag(sk, SOCK_DEAD)) {
- _debug("call %ps", sk->sk_data_ready);
- sk->sk_data_ready(sk);
- }
- }
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ _debug("call %ps", sk->sk_data_ready);
+ sk->sk_data_ready(sk);
}
- rcu_read_unlock();
_leave("");
}
@@ -561,7 +547,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
if (!(flags & MSG_PEEK) &&
!skb_queue_empty(&call->recvmsg_queue))
- rxrpc_notify_socket(call);
+ rxrpc_requeue_call(sock, call);
goto not_yet_complete;
call_failed:
^ permalink raw reply related
* [PATCH net-next] mptcp: sockopt: implement IPV6_TCLASS
From: David 'equinox' Lamparter @ 2026-07-13 8:04 UTC (permalink / raw)
To: Matthieu Baerts, Mat Martineau, Geliang Tang
Cc: netdev, mptcp, David 'equinox' Lamparter
The IPV6_TCLASS setsockopt just needs to be forwarded to the individual
TCP sockets, like IP_TOS is already handled for IPv4. The code here is
pretty much identical, except there's no helper for IPv6 like IPv4's
__ip_sock_set_tos().
Coincidentally, ssh uses this sockopt and prints an error in the middle
of your ongoing SSH session when it doesn't work (very annoying when
doing SCP/SFTP on a multiplexed session.)
Signed-off-by: David 'equinox' Lamparter <equinox@diac24.net>
Cc: Matthieu Baerts <matttbe@kernel.org>
Cc: Mat Martineau <martineau@kernel.org>
Cc: Geliang Tang <geliang@kernel.org>
---
note: I wasn't entirely sure what tree this should be on top of, with the
mptcp trees on github & kernel.org; this is on top of net-next. It
shouldn't really matter though.
---
net/mptcp/sockopt.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index fcf6feb2a9eb..edddafebb8de 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -394,6 +394,35 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
return -EOPNOTSUPP;
}
+static int mptcp_setsockopt_v6_set_tclass(struct mptcp_sock *msk, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ struct mptcp_subflow_context *subflow;
+ struct sock *sk = (struct sock *)msk;
+ int err, val;
+
+ err = ipv6_setsockopt(sk, SOL_IPV6, optname, optval, optlen);
+
+ if (err != 0)
+ return err;
+
+ lock_sock(sk);
+ sockopt_seq_inc(msk);
+ val = READ_ONCE(inet6_sk(sk)->tclass);
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ bool slow;
+
+ slow = lock_sock_fast(ssk);
+ inet6_sk(ssk)->tclass = val;
+ sk_dst_reset(ssk);
+ unlock_sock_fast(ssk, slow);
+ }
+ release_sock(sk);
+
+ return 0;
+}
+
static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsigned int optlen)
{
@@ -436,6 +465,8 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
release_sock(sk);
break;
+ case IPV6_TCLASS:
+ return mptcp_setsockopt_v6_set_tclass(msk, optname, optval, optlen);
}
return ret;
@@ -1485,6 +1516,9 @@ static int mptcp_getsockopt_v6(struct mptcp_sock *msk, int optname,
struct sock *sk = (void *)msk;
switch (optname) {
+ case IPV6_TCLASS:
+ return mptcp_put_int_option(msk, optval, optlen,
+ READ_ONCE(inet6_sk(sk)->tclass));
case IPV6_V6ONLY:
return mptcp_put_int_option(msk, optval, optlen,
sk->sk_ipv6only);
--
2.53.0
^ permalink raw reply related
* [PATCH net v3 14/14] rxrpc: Remove OOB challenge/response code
From: David Howells @ 2026-07-13 8:10 UTC (permalink / raw)
To: netdev
Cc: David Howells, Marc Dionne, Jakub Kicinski, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-afs, linux-kernel,
Jeffrey Altman, Jarkko Sakkinen, keyrings, stable
In-Reply-To: <20260713081022.2186481-1-dhowells@redhat.com>
Currently, when a CHALLENGE packet comes in, it's queued in an OOB queue on
the AF_RXRPC socket that generated one of the calls on that connection for
the application (which might be in userspace) to service. The application
then picks up the CHALLENGE and requests a RESPONSE packet be generated,
allowing the app to include app-specific data in it if appropriate. There
is, however, no actual limit on the capacity of the CHALLENGE queue, and
this could be abused remotely - and also getting the OOB mechanism right
has proven tricky.
Further, by analogy with other AFS codebases, it's not actually necessary
to generate the application data in response to the CHALLENGE. The reason
I did this was to set the encryption on the app-data to be the same as that
specified in the CHALLENGE as the server must be able to handle that.
However, it's sufficient to use the encoding type set in the token that is
going to be sent to the server; presumably the kerberos server knows that
the fileserver can handle that type - otherwise why tell the client to use
it?
This is the final part of the fix. It removes all the OOB communication
stuff along with the UAPI elements related to them.
Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE")
Link: https://sashiko.dev/#/patchset/20260624163819.3017002-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeffrey Altman <jaltman@auristor.com>
cc: Eric Dumazet <edumazet@google.com>
cc: "David S. Miller" <davem@davemloft.net>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: Jarkko Sakkinen <jarkko@kernel.org>
cc: linux-afs@lists.infradead.org
cc: keyrings@vger.kernel.org
cc: stable@kernel.org
---
fs/afs/cm_security.c | 259 -----------------------
fs/afs/internal.h | 3 -
fs/afs/main.c | 1 -
fs/afs/rxrpc.c | 19 --
fs/afs/server.c | 1 -
include/net/af_rxrpc.h | 18 --
include/trace/events/rxrpc.h | 2 -
include/uapi/linux/rxrpc.h | 5 -
net/rxrpc/Makefile | 1 -
net/rxrpc/af_rxrpc.c | 41 +---
net/rxrpc/ar-internal.h | 19 --
net/rxrpc/oob.c | 387 -----------------------------------
net/rxrpc/recvmsg.c | 82 +-------
net/rxrpc/rxgk.c | 73 -------
net/rxrpc/rxkad.c | 27 ---
net/rxrpc/server_key.c | 40 ----
16 files changed, 5 insertions(+), 973 deletions(-)
delete mode 100644 net/rxrpc/oob.c
diff --git a/fs/afs/cm_security.c b/fs/afs/cm_security.c
index 36907a04efd0..f0fec3a76522 100644
--- a/fs/afs/cm_security.c
+++ b/fs/afs/cm_security.c
@@ -20,11 +20,6 @@
#define xdr_round_up(x) (round_up((x), sizeof(__be32)))
#define xdr_len_object(x) (4 + round_up((x), sizeof(__be32)))
-#ifdef CONFIG_RXGK
-static int afs_create_yfs_cm_token(struct sk_buff *challenge,
- struct afs_server *server);
-#endif
-
#ifdef CONFIG_RXGK
/*
* As the YFS RxGK appdata to be passed in the YFS.FS-service RESPONSE packet,
@@ -255,95 +250,6 @@ int afs_create_server_appdata(struct afs_server *server, struct key *key)
return ret;
}
-/*
- * Respond to an RxGK challenge, adding appdata.
- */
-static int afs_respond_to_challenge(struct sk_buff *challenge)
-{
-#ifdef CONFIG_RXGK
- struct krb5_buffer appdata = {};
- struct afs_server *server;
-#endif
- struct rxrpc_peer *peer;
- unsigned long peer_data;
- u16 service_id;
- u8 security_index;
-
- rxrpc_kernel_query_challenge(challenge, &peer, &peer_data,
- &service_id, &security_index);
-
- _enter("%u,%u", service_id, security_index);
-
- switch (service_id) {
- /* We don't send CM_SERVICE RPCs, so don't expect a challenge
- * therefrom.
- */
- case FS_SERVICE:
- case VL_SERVICE:
- case YFS_FS_SERVICE:
- case YFS_VL_SERVICE:
- break;
- default:
- pr_warn("Can't respond to unknown challenge %u:%u",
- service_id, security_index);
- return rxrpc_kernel_reject_challenge(challenge, RX_USER_ABORT, -EPROTO,
- afs_abort_unsupported_sec_class);
- }
-
- switch (security_index) {
-#ifdef CONFIG_RXKAD
- case RXRPC_SECURITY_RXKAD:
- return rxkad_kernel_respond_to_challenge(challenge);
-#endif
-
-#ifdef CONFIG_RXGK
- case RXRPC_SECURITY_RXGK:
- return rxgk_kernel_respond_to_challenge(challenge, &appdata);
-
- case RXRPC_SECURITY_YFS_RXGK:
- switch (service_id) {
- case FS_SERVICE:
- case YFS_FS_SERVICE:
- server = (struct afs_server *)peer_data;
- if (!server->cm_rxgk_appdata.data) {
- mutex_lock(&server->cm_token_lock);
- if (!server->cm_rxgk_appdata.data)
- afs_create_yfs_cm_token(challenge, server);
- mutex_unlock(&server->cm_token_lock);
- }
- if (server->cm_rxgk_appdata.data)
- appdata = server->cm_rxgk_appdata;
- break;
- }
- return rxgk_kernel_respond_to_challenge(challenge, &appdata);
-#endif
-
- default:
- return rxrpc_kernel_reject_challenge(challenge, RX_USER_ABORT, -EPROTO,
- afs_abort_unsupported_sec_class);
- }
-}
-
-/*
- * Process the OOB message queue, processing challenge packets.
- */
-void afs_process_oob_queue(struct work_struct *work)
-{
- struct afs_net *net = container_of(work, struct afs_net, rx_oob_work);
- struct sk_buff *oob;
- enum rxrpc_oob_type type;
-
- while (READ_ONCE(net->live) &&
- (oob = rxrpc_kernel_dequeue_oob(net->socket, &type))) {
- switch (type) {
- case RXRPC_OOB_CHALLENGE:
- afs_respond_to_challenge(oob);
- break;
- }
- rxrpc_kernel_free_oob(oob);
- }
-}
-
#ifdef CONFIG_RXGK
/*
* Create a securities keyring for the cache manager and attach a key to it for
@@ -405,169 +311,4 @@ int afs_create_token_key(struct afs_net *net, struct socket *socket)
key_put(ring);
return ret;
}
-
-/*
- * Create an YFS RxGK GSS token to use as a ticket to the specified fileserver.
- */
-static int afs_create_yfs_cm_token(struct sk_buff *challenge,
- struct afs_server *server)
-{
- const struct krb5_enctype *conn_krb5, *token_krb5;
- const struct krb5_buffer *token_key;
- struct crypto_aead *aead;
- struct scatterlist sg;
- struct afs_net *net = server->cell->net;
- const struct key *key = net->fs_cm_token_key;
- size_t keysize, uuidsize, authsize, toksize, encsize, contsize, adatasize, offset;
- __be32 caps[1] = {
- [0] = htonl(AFS_CAP_ERROR_TRANSLATION),
- };
- __be32 *xdr;
- void *appdata, *K0, *encbase;
- u32 enctype;
- int ret;
-
- if (!key)
- return -ENOKEY;
-
- /* Assume that the fileserver is happy to use the same encoding type as
- * we were told to use by the token obtained by the user.
- */
- enctype = rxgk_kernel_query_challenge(challenge);
-
- conn_krb5 = crypto_krb5_find_enctype(enctype);
- if (!conn_krb5)
- return -ENOPKG;
- token_krb5 = key->payload.data[0];
- token_key = (const struct krb5_buffer *)&key->payload.data[2];
-
- /* struct rxgk_key {
- * afs_uint32 enctype;
- * opaque key<>;
- * };
- */
- keysize = 4 + xdr_len_object(conn_krb5->key_len);
-
- /* struct RXGK_AuthName {
- * afs_int32 kind;
- * opaque data<AUTHDATAMAX>;
- * opaque display<AUTHPRINTABLEMAX>;
- * };
- */
- uuidsize = sizeof(server->uuid);
- authsize = 4 + xdr_len_object(uuidsize) + xdr_len_object(0);
-
- /* struct RXGK_Token {
- * rxgk_key K0;
- * RXGK_Level level;
- * rxgkTime starttime;
- * afs_int32 lifetime;
- * afs_int32 bytelife;
- * rxgkTime expirationtime;
- * struct RXGK_AuthName identities<>;
- * };
- */
- toksize = keysize + 8 + 4 + 4 + 8 + xdr_len_object(authsize);
-
- offset = 0;
- encsize = crypto_krb5_how_much_buffer(token_krb5, KRB5_ENCRYPT_MODE, toksize, &offset);
-
- /* struct RXGK_TokenContainer {
- * afs_int32 kvno;
- * afs_int32 enctype;
- * opaque encrypted_token<>;
- * };
- */
- contsize = 4 + 4 + xdr_len_object(encsize);
-
- /* struct YFSAppData {
- * opr_uuid initiatorUuid;
- * opr_uuid acceptorUuid;
- * Capabilities caps;
- * afs_int32 enctype;
- * opaque callbackKey<>;
- * opaque callbackToken<>;
- * };
- */
- adatasize = 16 + 16 +
- xdr_len_object(sizeof(caps)) +
- 4 +
- xdr_len_object(conn_krb5->key_len) +
- xdr_len_object(contsize);
-
- ret = -ENOMEM;
- appdata = kzalloc(adatasize, GFP_KERNEL);
- if (!appdata)
- goto out;
- xdr = appdata;
-
- memcpy(xdr, &net->uuid, 16); /* appdata.initiatorUuid */
- xdr += 16 / 4;
- memcpy(xdr, &server->uuid, 16); /* appdata.acceptorUuid */
- xdr += 16 / 4;
- *xdr++ = htonl(ARRAY_SIZE(caps)); /* appdata.caps.len */
- memcpy(xdr, &caps, sizeof(caps)); /* appdata.caps */
- xdr += ARRAY_SIZE(caps);
- *xdr++ = htonl(conn_krb5->etype); /* appdata.enctype */
-
- *xdr++ = htonl(conn_krb5->key_len); /* appdata.callbackKey.len */
- K0 = xdr;
- get_random_bytes(K0, conn_krb5->key_len); /* appdata.callbackKey.data */
- xdr += xdr_round_up(conn_krb5->key_len) / 4;
-
- *xdr++ = htonl(contsize); /* appdata.callbackToken.len */
- *xdr++ = htonl(1); /* cont.kvno */
- *xdr++ = htonl(token_krb5->etype); /* cont.enctype */
- *xdr++ = htonl(encsize); /* cont.encrypted_token.len */
-
- encbase = xdr;
- xdr += offset / 4;
- *xdr++ = htonl(conn_krb5->etype); /* token.K0.enctype */
- *xdr++ = htonl(conn_krb5->key_len); /* token.K0.key.len */
- memcpy(xdr, K0, conn_krb5->key_len); /* token.K0.key.data */
- xdr += xdr_round_up(conn_krb5->key_len) / 4;
-
- *xdr++ = htonl(RXRPC_SECURITY_ENCRYPT); /* token.level */
- *xdr++ = htonl(0); /* token.starttime */
- *xdr++ = htonl(0); /* " */
- *xdr++ = htonl(0); /* token.lifetime */
- *xdr++ = htonl(0); /* token.bytelife */
- *xdr++ = htonl(0); /* token.expirationtime */
- *xdr++ = htonl(0); /* " */
- *xdr++ = htonl(1); /* token.identities.count */
- *xdr++ = htonl(0); /* token.identities[0].kind */
- *xdr++ = htonl(uuidsize); /* token.identities[0].data.len */
- memcpy(xdr, &server->uuid, uuidsize);
- xdr += xdr_round_up(uuidsize) / 4;
- *xdr++ = htonl(0); /* token.identities[0].display.len */
-
- xdr = encbase + xdr_round_up(encsize);
-
- if ((unsigned long)xdr - (unsigned long)appdata != adatasize)
- pr_err("Appdata size incorrect %lx != %zx\n",
- (unsigned long)xdr - (unsigned long)appdata, adatasize);
-
- aead = crypto_krb5_prepare_encryption(token_krb5, token_key, RXGK_SERVER_ENC_TOKEN,
- GFP_KERNEL);
- if (IS_ERR(aead)) {
- ret = PTR_ERR(aead);
- goto out_token;
- }
-
- sg_init_one(&sg, encbase, encsize);
- ret = crypto_krb5_encrypt(token_krb5, aead, &sg, 1, encsize, offset, toksize, false);
- if (ret < 0)
- goto out_aead;
-
- server->cm_rxgk_appdata.len = adatasize;
- server->cm_rxgk_appdata.data = appdata;
- appdata = NULL;
-
-out_aead:
- crypto_free_aead(aead);
-out_token:
- kfree(appdata);
-out:
- return ret;
-}
#endif /* CONFIG_RXGK */
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 57519888b978..9322234ce7f7 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -282,7 +282,6 @@ struct afs_net {
struct socket *socket;
struct afs_call *spare_incoming_call;
struct work_struct charge_preallocation_work;
- struct work_struct rx_oob_work;
struct mutex socket_mutex;
atomic_t nr_outstanding_calls;
atomic_t nr_superblocks;
@@ -547,7 +546,6 @@ struct afs_server {
struct work_struct destroyer; /* Work item to try and destroy a server */
struct timer_list timer; /* Management timer */
struct mutex cm_token_lock; /* Lock governing creation of appdata */
- struct krb5_buffer cm_rxgk_appdata; /* Appdata to be included in RESPONSE packet */
struct key *yfs_rxgk_appdata; /* Appdata to be included in RESPONSE packet */
time64_t unuse_time; /* Time at which last unused */
unsigned long flags;
@@ -1090,7 +1088,6 @@ extern bool afs_cm_incoming_call(struct afs_call *);
* cm_security.c
*/
int afs_create_server_appdata(struct afs_server *server, struct key *key);
-void afs_process_oob_queue(struct work_struct *work);
#ifdef CONFIG_RXGK
int afs_create_token_key(struct afs_net *net, struct socket *socket);
#else
diff --git a/fs/afs/main.c b/fs/afs/main.c
index 7a883c59976f..6353feaa5fb4 100644
--- a/fs/afs/main.c
+++ b/fs/afs/main.c
@@ -73,7 +73,6 @@ static int __net_init afs_net_init(struct net *net_ns)
generate_random_uuid((unsigned char *)&net->uuid);
INIT_WORK(&net->charge_preallocation_work, afs_charge_preallocation);
- INIT_WORK(&net->rx_oob_work, afs_process_oob_queue);
mutex_init(&net->socket_mutex);
net->cells = RB_ROOT;
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index 1a110448dbdb..4f6d699b5d22 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -25,14 +25,12 @@ static void afs_process_async_call(struct work_struct *);
static void afs_rx_new_call(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_rx_discard_new_call(struct rxrpc_call *, unsigned long);
static void afs_rx_attach(struct rxrpc_call *rxcall, unsigned long user_call_ID);
-static void afs_rx_notify_oob(struct sock *sk, struct sk_buff *oob);
static int afs_deliver_cm_op_id(struct afs_call *);
static const struct rxrpc_kernel_ops afs_rxrpc_callback_ops = {
.notify_new_call = afs_rx_new_call,
.discard_new_call = afs_rx_discard_new_call,
.user_attach_call = afs_rx_attach,
- .notify_oob = afs_rx_notify_oob,
};
/* asynchronous incoming call initial processing */
@@ -74,10 +72,6 @@ int afs_open_socket(struct afs_net *net)
if (ret < 0)
goto error_2;
- ret = rxrpc_sock_set_manage_response(socket->sk, true);
- if (ret < 0)
- goto error_2;
-
ret = afs_create_token_key(net, socket);
if (ret < 0)
pr_err("Couldn't create RxGK CM key: %d\n", ret);
@@ -128,7 +122,6 @@ void afs_close_socket(struct afs_net *net)
_enter("");
cancel_work_sync(&net->charge_preallocation_work);
- cancel_work_sync(&net->rx_oob_work);
/* Future work items should now see ->live is false. */
kernel_listen(net->socket, 0);
@@ -149,7 +142,6 @@ void afs_close_socket(struct afs_net *net)
kernel_sock_shutdown(net->socket, SHUT_RDWR);
flush_workqueue(afs_async_calls);
- cancel_work_sync(&net->rx_oob_work);
net->socket->sk->sk_user_data = NULL;
sock_release(net->socket);
key_put(net->fs_cm_token_key);
@@ -963,14 +955,3 @@ noinline int afs_protocol_error(struct afs_call *call,
call->unmarshalling_error = true;
return -EBADMSG;
}
-
-/*
- * Wake up OOB notification processing.
- */
-static void afs_rx_notify_oob(struct sock *sk, struct sk_buff *oob)
-{
- struct afs_net *net = sk->sk_user_data;
-
- if (READ_ONCE(net->live))
- queue_work(afs_wq, &net->rx_oob_work);
-}
diff --git a/fs/afs/server.c b/fs/afs/server.c
index b08d9080b0c5..4c3a7bd0f7d8 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -398,7 +398,6 @@ static void afs_server_rcu(struct rcu_head *rcu)
afs_put_endpoint_state(rcu_access_pointer(server->endpoint_state),
afs_estate_trace_put_server);
afs_put_cell(server->cell, afs_cell_trace_put_server);
- kfree(server->cm_rxgk_appdata.data);
key_put(server->yfs_rxgk_appdata);
kfree(server);
}
diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h
index 19c61a2f5af3..a9b8e1ba67ef 100644
--- a/include/net/af_rxrpc.h
+++ b/include/net/af_rxrpc.h
@@ -92,24 +92,6 @@ bool rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *);
int rxrpc_sock_set_min_security_level(struct sock *sk, unsigned int val);
int rxrpc_sock_set_security_keyring(struct sock *, struct key *);
-int rxrpc_sock_set_manage_response(struct sock *sk, bool set);
-
-enum rxrpc_oob_type rxrpc_kernel_query_oob(struct sk_buff *oob,
- struct rxrpc_peer **_peer,
- unsigned long *_peer_appdata);
-struct sk_buff *rxrpc_kernel_dequeue_oob(struct socket *sock,
- enum rxrpc_oob_type *_type);
-void rxrpc_kernel_free_oob(struct sk_buff *oob);
-void rxrpc_kernel_query_challenge(struct sk_buff *challenge,
- struct rxrpc_peer **_peer,
- unsigned long *_peer_appdata,
- u16 *_service_id, u8 *_security_index);
-int rxrpc_kernel_reject_challenge(struct sk_buff *challenge, u32 abort_code,
- int error, enum rxrpc_abort_reason why);
-int rxkad_kernel_respond_to_challenge(struct sk_buff *challenge);
-u32 rxgk_kernel_query_challenge(struct sk_buff *challenge);
-int rxgk_kernel_respond_to_challenge(struct sk_buff *challenge,
- struct krb5_buffer *appdata);
void rxrpc_kernel_query_key(const struct key *key, u8 *_security_index,
u32 *_krb5_enctype);
u8 rxrpc_kernel_query_call_security(struct rxrpc_call *call,
diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index d7c7b04d69fc..45e4550a18ed 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -263,7 +263,6 @@
EM(rxrpc_conn_free, "FREE ") \
EM(rxrpc_conn_get_activate_call, "GET act-call") \
EM(rxrpc_conn_get_call_input, "GET inp-call") \
- EM(rxrpc_conn_get_challenge_input, "GET inp-chal") \
EM(rxrpc_conn_get_conn_input, "GET inp-conn") \
EM(rxrpc_conn_get_idle, "GET idle ") \
EM(rxrpc_conn_get_poke_abort, "GET pk-abort") \
@@ -275,7 +274,6 @@
EM(rxrpc_conn_new_service, "NEW service ") \
EM(rxrpc_conn_put_call, "PUT call ") \
EM(rxrpc_conn_put_call_input, "PUT inp-call") \
- EM(rxrpc_conn_put_challenge_input, "PUT inp-chal") \
EM(rxrpc_conn_put_conn_input, "PUT inp-conn") \
EM(rxrpc_conn_put_discard_idle, "PUT disc-idl") \
EM(rxrpc_conn_put_local_dead, "PUT loc-dead") \
diff --git a/include/uapi/linux/rxrpc.h b/include/uapi/linux/rxrpc.h
index bcdfdf9c67a1..7d6a50151931 100644
--- a/include/uapi/linux/rxrpc.h
+++ b/include/uapi/linux/rxrpc.h
@@ -58,11 +58,6 @@ enum rxrpc_cmsg_type {
RXRPC_TX_LENGTH = 12, /* -s-: Total length of Tx data */
RXRPC_SET_CALL_TIMEOUT = 13, /* -s-: Set one or more call timeouts */
RXRPC_CHARGE_ACCEPT = 14, /* Ss-: Charge the accept pool with a user call ID */
- RXRPC_OOB_ID = 15, /* -sr: OOB message ID */
- RXRPC_CHALLENGED = 16, /* C-r: Info on a received CHALLENGE */
- RXRPC_RESPOND = 17, /* Cs-: Respond to a challenge */
- RXRPC_RESPONDED = 18, /* S-r: Data received in RESPONSE */
- RXRPC_RESP_RXGK_APPDATA = 19, /* Cs-: RESPONSE: RxGK app data to include */
RXRPC_RESPONSE_APPDATA = 20, /* Cs-: User key holding app data for RESPONSE */
RXRPC__SUPPORTED
};
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index f994f9f30a29..f7a5e0a592ee 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -24,7 +24,6 @@ rxrpc-y := \
local_object.o \
misc.o \
net_ns.o \
- oob.o \
output.o \
peer_event.o \
peer_object.o \
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 9eb6ca3c5ebf..9d5ce1c7ad0d 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -633,7 +633,7 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
- unsigned int min_sec_level, val;
+ unsigned int min_sec_level;
u16 service_upgrade[2];
int ret;
@@ -710,23 +710,7 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname,
case RXRPC_MANAGE_RESPONSE:
ret = -EINVAL;
- if (optlen != sizeof(unsigned int))
- goto error;
- ret = -EISCONN;
- if (rx->sk.sk_state != RXRPC_UNBOUND)
- goto error;
- ret = copy_safe_from_sockptr(&val, sizeof(val),
- optval, optlen);
- if (ret)
- goto error;
- ret = -EINVAL;
- if (val > 1)
- goto error;
- if (val)
- set_bit(RXRPC_SOCK_MANAGE_RESPONSE, &rx->flags);
- else
- clear_bit(RXRPC_SOCK_MANAGE_RESPONSE, &rx->flags);
- goto success;
+ goto error;
default:
break;
@@ -835,8 +819,6 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
rx->calls = RB_ROOT;
spin_lock_init(&rx->incoming_lock);
- skb_queue_head_init(&rx->recvmsg_oobq);
- rx->pending_oobq = RB_ROOT;
INIT_LIST_HEAD(&rx->sock_calls);
INIT_LIST_HEAD(&rx->to_be_accepted);
INIT_LIST_HEAD(&rx->recvmsg_q);
@@ -884,23 +866,6 @@ static int rxrpc_shutdown(struct socket *sock, int flags)
return ret;
}
-/*
- * Purge the out-of-band queue.
- */
-static void rxrpc_purge_oob_queue(struct sock *sk)
-{
- struct rxrpc_sock *rx = rxrpc_sk(sk);
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&rx->recvmsg_oobq)))
- rxrpc_kernel_free_oob(skb);
- while (!RB_EMPTY_ROOT(&rx->pending_oobq)) {
- skb = rb_entry(rx->pending_oobq.rb_node, struct sk_buff, rbnode);
- rb_erase(&skb->rbnode, &rx->pending_oobq);
- rxrpc_kernel_free_oob(skb);
- }
-}
-
/*
* RxRPC socket destructor
*/
@@ -908,7 +873,6 @@ static void rxrpc_sock_destructor(struct sock *sk)
{
_enter("%p", sk);
- rxrpc_purge_oob_queue(sk);
rxrpc_purge_queue(&sk->sk_receive_queue);
WARN_ON(refcount_read(&sk->sk_wmem_alloc));
@@ -961,7 +925,6 @@ static int rxrpc_release_sock(struct sock *sk)
rxrpc_discard_prealloc(rx);
rxrpc_release_calls_on_socket(rx);
flush_workqueue(rxrpc_workqueue);
- rxrpc_purge_oob_queue(sk);
rxrpc_purge_queue(&sk->sk_receive_queue);
rxrpc_unuse_local(rx->local, rxrpc_local_unuse_release_sock);
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 20c10428a50e..1c6f4a279034 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -171,9 +171,6 @@ struct rxrpc_sock {
const struct rxrpc_kernel_ops *app_ops; /* Table of kernel app notification funcs */
struct rxrpc_local *local; /* local endpoint */
struct rxrpc_backlog *backlog; /* Preallocation for services */
- struct sk_buff_head recvmsg_oobq; /* OOB messages for recvmsg to pick up */
- struct rb_root pending_oobq; /* OOB messages awaiting userspace to respond to */
- u64 oob_id_counter; /* OOB message ID counter */
spinlock_t incoming_lock; /* Incoming call vs service shutdown lock */
struct list_head sock_calls; /* List of calls owned by this socket */
struct list_head to_be_accepted; /* calls awaiting acceptance */
@@ -184,7 +181,6 @@ struct rxrpc_sock {
struct rb_root calls; /* User ID -> call mapping */
unsigned long flags;
#define RXRPC_SOCK_CONNECTED 0 /* connect_srx is set */
-#define RXRPC_SOCK_MANAGE_RESPONSE 1 /* User wants to manage RESPONSE packets */
rwlock_t call_lock; /* lock for calls */
u32 min_sec_level; /* minimum security level */
#define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT
@@ -241,7 +237,6 @@ struct rxrpc_skb_priv {
u8 reason; /* Reason for ack */
} ack;
struct {
- struct rxrpc_connection *conn; /* Connection referred to */
union {
u32 rxkad_nonce;
};
@@ -310,13 +305,6 @@ struct rxrpc_security {
bool (*validate_challenge)(struct rxrpc_connection *conn,
struct sk_buff *skb);
- /* Fill out the cmsg for recvmsg() to pass on a challenge to userspace.
- * The security class gets to add additional information.
- */
- int (*challenge_to_recvmsg)(struct rxrpc_connection *conn,
- struct sk_buff *challenge,
- struct msghdr *msg);
-
/* Parse sendmsg() control message and respond to challenge. */
int (*sendmsg_respond_to_challenge)(struct sk_buff *challenge,
struct msghdr *msg);
@@ -1379,13 +1367,6 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net)
return net_generic(net, rxrpc_net_id);
}
-/*
- * oob.c
- */
-bool rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb);
-void rxrpc_add_pending_oob(struct rxrpc_sock *rx, struct sk_buff *skb);
-int rxrpc_sendmsg_oob(struct rxrpc_sock *rx, struct msghdr *msg, size_t len);
-
/*
* output.c
*/
diff --git a/net/rxrpc/oob.c b/net/rxrpc/oob.c
deleted file mode 100644
index c80ee2487d09..000000000000
--- a/net/rxrpc/oob.c
+++ /dev/null
@@ -1,387 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Out of band message handling (e.g. challenge-response)
- *
- * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/net.h>
-#include <linux/gfp.h>
-#include <linux/skbuff.h>
-#include <linux/export.h>
-#include <linux/sched/signal.h>
-#include <net/sock.h>
-#include <net/af_rxrpc.h>
-#include "ar-internal.h"
-
-enum rxrpc_oob_command {
- RXRPC_OOB_CMD_UNSET,
- RXRPC_OOB_CMD_RESPOND,
-} __mode(byte);
-
-struct rxrpc_oob_params {
- u64 oob_id; /* ID number of message if reply */
- s32 abort_code;
- enum rxrpc_oob_command command;
- bool have_oob_id:1;
-};
-
-/*
- * Post an out-of-band message for attention by the socket or kernel service
- * associated with a reference call.
- */
-bool rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
- struct rxrpc_sock *rx;
- struct sock *sk;
- bool queued = false;
-
- rcu_read_lock();
-
- rx = rcu_dereference(call->socket);
- if (rx) {
- sk = &rx->sk;
- spin_lock_irq(&rx->recvmsg_lock);
-
- if (sk->sk_state < RXRPC_CLOSE) {
- skb->skb_mstamp_ns = rx->oob_id_counter++;
- rxrpc_get_skb(skb, rxrpc_skb_get_post_oob);
- skb_queue_tail(&rx->recvmsg_oobq, skb);
- queued = true;
-
- trace_rxrpc_notify_socket(call->debug_id, sp->hdr.serial);
- if (rx->app_ops)
- rx->app_ops->notify_oob(sk, skb);
- }
-
- spin_unlock_irq(&rx->recvmsg_lock);
- if (queued && !rx->app_ops && !sock_flag(sk, SOCK_DEAD))
- sk->sk_data_ready(sk);
- }
-
- rcu_read_unlock();
- return queued;
-}
-
-/*
- * Locate the OOB message to respond to by its ID.
- */
-static struct sk_buff *rxrpc_find_pending_oob(struct rxrpc_sock *rx, u64 oob_id)
-{
- struct rb_node *p;
- struct sk_buff *skb;
-
- p = rx->pending_oobq.rb_node;
- while (p) {
- skb = rb_entry(p, struct sk_buff, rbnode);
-
- if (oob_id < skb->skb_mstamp_ns)
- p = p->rb_left;
- else if (oob_id > skb->skb_mstamp_ns)
- p = p->rb_right;
- else
- return skb;
- }
-
- return NULL;
-}
-
-/*
- * Add an OOB message into the pending-response set. We always assign the next
- * value from a 64-bit counter to the oob_id, so just assume we're always going
- * to be on the right-hand edge of the tree and that the counter won't wrap.
- * The tree is also given a ref to the message.
- */
-void rxrpc_add_pending_oob(struct rxrpc_sock *rx, struct sk_buff *skb)
-{
- struct rb_node **pp = &rx->pending_oobq.rb_node, *p = NULL;
-
- while (*pp) {
- p = *pp;
- pp = &(*pp)->rb_right;
- }
-
- rb_link_node(&skb->rbnode, p, pp);
- rb_insert_color(&skb->rbnode, &rx->pending_oobq);
-}
-
-/*
- * Extract control messages from the sendmsg() control buffer.
- */
-static int rxrpc_sendmsg_oob_cmsg(struct msghdr *msg, struct rxrpc_oob_params *p)
-{
- struct cmsghdr *cmsg;
- int len;
-
- if (msg->msg_controllen == 0)
- return -EINVAL;
-
- for_each_cmsghdr(cmsg, msg) {
- if (!CMSG_OK(msg, cmsg))
- return -EINVAL;
-
- len = cmsg->cmsg_len - sizeof(struct cmsghdr);
- _debug("CMSG %d, %d, %d",
- cmsg->cmsg_level, cmsg->cmsg_type, len);
-
- if (cmsg->cmsg_level != SOL_RXRPC)
- continue;
-
- switch (cmsg->cmsg_type) {
- case RXRPC_OOB_ID:
- if (len != sizeof(p->oob_id) || p->have_oob_id)
- return -EINVAL;
- memcpy(&p->oob_id, CMSG_DATA(cmsg), sizeof(p->oob_id));
- p->have_oob_id = true;
- break;
- case RXRPC_RESPOND:
- if (p->command != RXRPC_OOB_CMD_UNSET)
- return -EINVAL;
- p->command = RXRPC_OOB_CMD_RESPOND;
- break;
- case RXRPC_ABORT:
- if (len != sizeof(p->abort_code) || p->abort_code)
- return -EINVAL;
- memcpy(&p->abort_code, CMSG_DATA(cmsg), sizeof(p->abort_code));
- if (p->abort_code == 0)
- return -EINVAL;
- break;
- case RXRPC_RESP_RXGK_APPDATA:
- if (p->command != RXRPC_OOB_CMD_RESPOND)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- }
-
- switch (p->command) {
- case RXRPC_OOB_CMD_RESPOND:
- if (!p->have_oob_id)
- return -EBADSLT;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * Allow userspace to respond to an OOB using sendmsg().
- */
-static int rxrpc_respond_to_oob(struct rxrpc_sock *rx,
- struct rxrpc_oob_params *p,
- struct msghdr *msg)
-{
- struct rxrpc_connection *conn;
- struct rxrpc_skb_priv *sp;
- struct sk_buff *skb;
- int ret;
-
- skb = rxrpc_find_pending_oob(rx, p->oob_id);
- if (skb)
- rb_erase(&skb->rbnode, &rx->pending_oobq);
- release_sock(&rx->sk);
- if (!skb)
- return -EBADSLT;
-
- sp = rxrpc_skb(skb);
-
- switch (p->command) {
- case RXRPC_OOB_CMD_RESPOND:
- ret = -EPROTO;
- if (skb->mark != RXRPC_OOB_CHALLENGE)
- break;
- conn = sp->chall.conn;
- ret = -EOPNOTSUPP;
- if (!conn->security->sendmsg_respond_to_challenge)
- break;
- if (p->abort_code) {
- rxrpc_abort_conn(conn, NULL, p->abort_code, -ECONNABORTED,
- rxrpc_abort_response_sendmsg);
- ret = 0;
- } else {
- ret = conn->security->sendmsg_respond_to_challenge(skb, msg);
- }
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- switch (skb->mark) {
- case RXRPC_OOB_CHALLENGE:
- rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob);
- break;
- }
- rxrpc_free_skb(skb, rxrpc_skb_put_oob);
- return ret;
-}
-
-/*
- * Send an out-of-band message or respond to a received out-of-band message.
- * - caller gives us the socket lock
- * - the socket may be either a client socket or a server socket
- */
-int rxrpc_sendmsg_oob(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
-{
- struct rxrpc_oob_params p = {};
- int ret;
-
- _enter("");
-
- ret = rxrpc_sendmsg_oob_cmsg(msg, &p);
- if (ret < 0)
- goto error_release_sock;
-
- if (p.have_oob_id)
- return rxrpc_respond_to_oob(rx, &p, msg);
-
- release_sock(&rx->sk);
-
- switch (p.command) {
- default:
- ret = -EINVAL;
- break;
- }
-
- _leave(" = %d", ret);
- return ret;
-
-error_release_sock:
- release_sock(&rx->sk);
- return ret;
-}
-
-/**
- * rxrpc_kernel_query_oob - Query the parameters of an out-of-band message
- * @oob: The message to query
- * @_peer: Where to return the peer record
- * @_peer_appdata: The application data attached to a peer record
- *
- * Extract useful parameters from an out-of-band message. The source peer
- * parameters are returned through the argument list and the message type is
- * returned.
- *
- * Return:
- * * %RXRPC_OOB_CHALLENGE - Challenge wanting a response.
- */
-enum rxrpc_oob_type rxrpc_kernel_query_oob(struct sk_buff *oob,
- struct rxrpc_peer **_peer,
- unsigned long *_peer_appdata)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(oob);
- enum rxrpc_oob_type type = oob->mark;
-
- switch (type) {
- case RXRPC_OOB_CHALLENGE:
- *_peer = sp->chall.conn->peer;
- *_peer_appdata = sp->chall.conn->peer->app_data;
- break;
- default:
- WARN_ON_ONCE(1);
- *_peer = NULL;
- *_peer_appdata = 0;
- break;
- }
-
- return type;
-}
-EXPORT_SYMBOL(rxrpc_kernel_query_oob);
-
-/**
- * rxrpc_kernel_dequeue_oob - Dequeue and return the front OOB message
- * @sock: The socket to query
- * @_type: Where to return the message type
- *
- * Dequeue the front OOB message, if there is one, and return it and
- * its type.
- *
- * Return: The sk_buff representing the OOB message or %NULL if the queue was
- * empty.
- */
-struct sk_buff *rxrpc_kernel_dequeue_oob(struct socket *sock,
- enum rxrpc_oob_type *_type)
-{
- struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
- struct sk_buff *oob;
-
- oob = skb_dequeue(&rx->recvmsg_oobq);
- if (oob)
- *_type = oob->mark;
- return oob;
-}
-EXPORT_SYMBOL(rxrpc_kernel_dequeue_oob);
-
-/**
- * rxrpc_kernel_free_oob - Free an out-of-band message
- * @oob: The OOB message to free
- *
- * Free an OOB message along with any resources it holds.
- */
-void rxrpc_kernel_free_oob(struct sk_buff *oob)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(oob);
-
- switch (oob->mark) {
- case RXRPC_OOB_CHALLENGE:
- rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob);
- break;
- }
-
- rxrpc_free_skb(oob, rxrpc_skb_put_purge_oob);
-}
-EXPORT_SYMBOL(rxrpc_kernel_free_oob);
-
-/**
- * rxrpc_kernel_query_challenge - Query the parameters of a challenge
- * @challenge: The challenge to query
- * @_peer: Where to return the peer record
- * @_peer_appdata: The application data attached to a peer record
- * @_service_id: Where to return the connection service ID
- * @_security_index: Where to return the connection security index
- *
- * Extract useful parameters from a CHALLENGE message.
- */
-void rxrpc_kernel_query_challenge(struct sk_buff *challenge,
- struct rxrpc_peer **_peer,
- unsigned long *_peer_appdata,
- u16 *_service_id, u8 *_security_index)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(challenge);
-
- *_peer = sp->chall.conn->peer;
- *_peer_appdata = sp->chall.conn->peer->app_data;
- *_service_id = sp->hdr.serviceId;
- *_security_index = sp->hdr.securityIndex;
-}
-EXPORT_SYMBOL(rxrpc_kernel_query_challenge);
-
-/**
- * rxrpc_kernel_reject_challenge - Allow a kernel service to reject a challenge
- * @challenge: The challenge to be rejected
- * @abort_code: The abort code to stick into the ABORT packet
- * @error: Local error value
- * @why: Indication as to why.
- *
- * Allow a kernel service to reject a challenge by aborting the connection if
- * it's still in an abortable state. The error is returned so this function
- * can be used with a return statement.
- *
- * Return: The %error parameter.
- */
-int rxrpc_kernel_reject_challenge(struct sk_buff *challenge, u32 abort_code,
- int error, enum rxrpc_abort_reason why)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(challenge);
-
- _enter("{%x},%d,%d,%u", sp->hdr.serial, abort_code, error, why);
-
- rxrpc_abort_conn(sp->chall.conn, NULL, abort_code, error, why);
- return error;
-}
-EXPORT_SYMBOL(rxrpc_kernel_reject_challenge);
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index 33577522ec02..5b8c7f573b7f 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -201,61 +201,6 @@ static int rxrpc_recvmsg_user_id(struct rxrpc_call *call, struct msghdr *msg,
}
}
-/*
- * Deal with a CHALLENGE packet.
- */
-static int rxrpc_recvmsg_challenge(struct socket *sock, struct msghdr *msg,
- struct sk_buff *challenge, unsigned int flags)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(challenge);
- struct rxrpc_connection *conn = sp->chall.conn;
-
- return conn->security->challenge_to_recvmsg(conn, challenge, msg);
-}
-
-/*
- * Process OOB packets. Called with the socket locked.
- */
-static int rxrpc_recvmsg_oob(struct socket *sock, struct msghdr *msg,
- unsigned int flags)
-{
- struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
- struct sk_buff *skb;
- bool need_response = false;
- int ret;
-
- skb = skb_peek(&rx->recvmsg_oobq);
- if (!skb)
- return -EAGAIN;
- rxrpc_see_skb(skb, rxrpc_skb_see_recvmsg);
-
- ret = put_cmsg(msg, SOL_RXRPC, RXRPC_OOB_ID, sizeof(u64),
- &skb->skb_mstamp_ns);
- if (ret < 0)
- return ret;
-
- switch ((enum rxrpc_oob_type)skb->mark) {
- case RXRPC_OOB_CHALLENGE:
- need_response = true;
- ret = rxrpc_recvmsg_challenge(sock, msg, skb, flags);
- break;
- default:
- WARN_ONCE(1, "recvmsg() can't process unknown OOB type %u\n",
- skb->mark);
- ret = -EIO;
- break;
- }
-
- if (!(flags & MSG_PEEK)) {
- skb_unlink(skb, &rx->recvmsg_oobq);
- if (need_response)
- rxrpc_add_pending_oob(rx, skb);
- else
- rxrpc_free_skb(skb, rxrpc_skb_put_oob);
- }
- return ret;
-}
-
/*
* Deliver messages to a call. This keeps processing packets until the buffer
* is filled and we find either more DATA (returns 0) or the end of the DATA
@@ -267,7 +212,6 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
size_t len, int flags, size_t *_offset)
{
struct rxrpc_skb_priv *sp;
- struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
struct sk_buff *skb;
rxrpc_seq_t seq = 0;
size_t remain;
@@ -362,13 +306,6 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
if (!(flags & MSG_PEEK))
rxrpc_rotate_rx_window(call);
-
- if (!rx->app_ops &&
- !skb_queue_empty_lockless(&rx->recvmsg_oobq)) {
- trace_rxrpc_recvdata(call, rxrpc_recvmsg_oobq, seq,
- rx_pkt_offset, rx_pkt_len, ret);
- break;
- }
}
out:
@@ -416,14 +353,12 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
/* Return immediately if a client socket has no outstanding calls */
if (RB_EMPTY_ROOT(&rx->calls) &&
list_empty(&rx->recvmsg_q) &&
- skb_queue_empty_lockless(&rx->recvmsg_oobq) &&
rx->sk.sk_state != RXRPC_SERVER_LISTENING) {
release_sock(&rx->sk);
return -EAGAIN;
}
- if (list_empty(&rx->recvmsg_q) &&
- skb_queue_empty_lockless(&rx->recvmsg_oobq)) {
+ if (list_empty(&rx->recvmsg_q)) {
ret = -EWOULDBLOCK;
if (timeo == 0) {
call = NULL;
@@ -439,8 +374,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
if (ret)
goto wait_error;
- if (list_empty(&rx->recvmsg_q) &&
- skb_queue_empty_lockless(&rx->recvmsg_oobq)) {
+ if (list_empty(&rx->recvmsg_q)) {
if (signal_pending(current))
goto wait_interrupted;
trace_rxrpc_recvmsg(0, rxrpc_recvmsg_wait, 0);
@@ -450,15 +384,6 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
goto try_again;
}
- /* Deal with OOB messages before we consider getting normal data. */
- if (!skb_queue_empty_lockless(&rx->recvmsg_oobq)) {
- ret = rxrpc_recvmsg_oob(sock, msg, flags);
- release_sock(&rx->sk);
- if (ret == -EAGAIN)
- goto try_again;
- goto error_trace;
- }
-
/* Find the next call and dequeue it if we're not just peeking. If we
* do dequeue it, that comes with a ref that we will need to release.
* We also want to weed out calls that got requeued whilst we were
@@ -469,8 +394,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
call = list_entry(l, struct rxrpc_call, recvmsg_link);
if (!rxrpc_call_is_complete(call) &&
- skb_queue_empty(&call->recvmsg_queue) &&
- skb_queue_empty(&rx->recvmsg_oobq)) {
+ skb_queue_empty(&call->recvmsg_queue)) {
list_del_init(&call->recvmsg_link);
spin_unlock_irq(&rx->recvmsg_lock);
release_sock(&rx->sk);
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
index f3e085b64502..b9efeec145dd 100644
--- a/net/rxrpc/rxgk.c
+++ b/net/rxrpc/rxgk.c
@@ -735,37 +735,6 @@ static bool rxgk_validate_challenge(struct rxrpc_connection *conn,
return true;
}
-/**
- * rxgk_kernel_query_challenge - Query RxGK-specific challenge parameters
- * @challenge: The challenge packet to query
- *
- * Return: The Kerberos 5 encoding type for the challenged connection.
- */
-u32 rxgk_kernel_query_challenge(struct sk_buff *challenge)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(challenge);
-
- return sp->chall.conn->rxgk.enctype;
-}
-EXPORT_SYMBOL(rxgk_kernel_query_challenge);
-
-/*
- * Fill out the control message to pass to userspace to inform about the
- * challenge.
- */
-static int rxgk_challenge_to_recvmsg(struct rxrpc_connection *conn,
- struct sk_buff *challenge,
- struct msghdr *msg)
-{
- struct rxgk_challenge chall;
-
- chall.base.service_id = conn->service_id;
- chall.base.security_index = conn->security_ix;
- chall.enctype = conn->rxgk.enctype;
-
- return put_cmsg(msg, SOL_RXRPC, RXRPC_CHALLENGED, sizeof(chall), &chall);
-}
-
/*
* Insert the requisite amount of XDR padding for the length given.
*/
@@ -1044,46 +1013,6 @@ static int rxgk_respond_to_challenge(struct rxrpc_connection *conn,
return ret;
}
-/**
- * rxgk_kernel_respond_to_challenge - Respond to a challenge with appdata
- * @challenge: The challenge to respond to
- * @appdata: The application data to include in the RESPONSE authenticator
- *
- * Allow a kernel application to respond to a CHALLENGE with application data
- * to be included in the RxGK RESPONSE Authenticator.
- *
- * Return: %0 if successful and a negative error code otherwise.
- */
-int rxgk_kernel_respond_to_challenge(struct sk_buff *challenge,
- struct krb5_buffer *appdata)
-{
- return -EINVAL;
-}
-EXPORT_SYMBOL(rxgk_kernel_respond_to_challenge);
-
-/*
- * Parse sendmsg() control message and respond to challenge. We need to see if
- * there's an appdata to fish out.
- */
-static int rxgk_sendmsg_respond_to_challenge(struct sk_buff *challenge,
- struct msghdr *msg)
-{
- struct krb5_buffer appdata = {};
- struct cmsghdr *cmsg;
-
- for_each_cmsghdr(cmsg, msg) {
- if (cmsg->cmsg_level != SOL_RXRPC ||
- cmsg->cmsg_type != RXRPC_RESP_RXGK_APPDATA)
- continue;
- if (appdata.data)
- return -EINVAL;
- appdata.data = CMSG_DATA(cmsg);
- appdata.len = cmsg->cmsg_len - sizeof(struct cmsghdr);
- }
-
- return rxgk_kernel_respond_to_challenge(challenge, &appdata);
-}
-
/*
* Verify the authenticator.
*
@@ -1357,8 +1286,6 @@ const struct rxrpc_security rxgk_yfs = {
.free_call_crypto = rxgk_free_call_crypto,
.issue_challenge = rxgk_issue_challenge,
.validate_challenge = rxgk_validate_challenge,
- .challenge_to_recvmsg = rxgk_challenge_to_recvmsg,
- .sendmsg_respond_to_challenge = rxgk_sendmsg_respond_to_challenge,
.respond_to_challenge = rxgk_respond_to_challenge,
.verify_response = rxgk_verify_response,
.clear = rxgk_clear,
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index ca9f0e82cb9a..e55bfca10e1a 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -748,32 +748,6 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
return ret;
}
-/*
- * RxKAD does automatic response only as there's nothing to manage that isn't
- * already in the key.
- */
-static int rxkad_sendmsg_respond_to_challenge(struct sk_buff *challenge,
- struct msghdr *msg)
-{
- return -EINVAL;
-}
-
-/**
- * rxkad_kernel_respond_to_challenge - Respond to a challenge with appdata
- * @challenge: The challenge to respond to
- *
- * Allow a kernel application to respond to a CHALLENGE.
- *
- * Return: %0 if successful and a negative error code otherwise.
- */
-int rxkad_kernel_respond_to_challenge(struct sk_buff *challenge)
-{
- struct rxrpc_skb_priv *csp = rxrpc_skb(challenge);
-
- return rxkad_respond_to_challenge(csp->chall.conn, challenge);
-}
-EXPORT_SYMBOL(rxkad_kernel_respond_to_challenge);
-
/* Decrypt data in-place using DES-PCBC. @len must be a multiple of 8. */
VISIBLE_IF_KUNIT void des_pcbc_decrypt_inplace(const struct des_ctx *key,
__le64 iv, u8 *data, size_t len)
@@ -1134,7 +1108,6 @@ const struct rxrpc_security rxkad = {
.free_call_crypto = rxkad_free_call_crypto,
.issue_challenge = rxkad_issue_challenge,
.validate_challenge = rxkad_validate_challenge,
- .sendmsg_respond_to_challenge = rxkad_sendmsg_respond_to_challenge,
.respond_to_challenge = rxkad_respond_to_challenge,
.verify_response = rxkad_verify_response,
.clear = rxkad_clear,
diff --git a/net/rxrpc/server_key.c b/net/rxrpc/server_key.c
index 3efe104b1930..fc6f478e1762 100644
--- a/net/rxrpc/server_key.c
+++ b/net/rxrpc/server_key.c
@@ -173,43 +173,3 @@ int rxrpc_sock_set_security_keyring(struct sock *sk, struct key *keyring)
return ret;
}
EXPORT_SYMBOL(rxrpc_sock_set_security_keyring);
-
-/**
- * rxrpc_sock_set_manage_response - Set the manage-response flag for a kernel service
- * @sk: The socket to set the keyring on
- * @set: True to set, false to clear the flag
- *
- * Set the flag on an rxrpc socket to say that the caller wants to manage the
- * RESPONSE packet and the user-defined data it may contain. Setting this
- * means that recvmsg() will return messages with RXRPC_CHALLENGED in the
- * control message buffer containing information about the challenge.
- *
- * The user should respond to the challenge by passing RXRPC_RESPOND or
- * RXRPC_RESPOND_ABORT control messages with sendmsg() to the same call.
- * Supplementary control messages, such as RXRPC_RESP_RXGK_APPDATA, may be
- * included to indicate the parts the user wants to supply.
- *
- * The server will be passed the response data with a RXRPC_RESPONDED control
- * message when it gets the first data from each call.
- *
- * Note that this is only honoured by security classes that need auxiliary data
- * (e.g. RxGK). Those that don't offer the facility (e.g. RxKAD) respond
- * without consulting userspace.
- *
- * Return: The previous setting.
- */
-int rxrpc_sock_set_manage_response(struct sock *sk, bool set)
-{
- struct rxrpc_sock *rx = rxrpc_sk(sk);
- int ret;
-
- lock_sock(sk);
- ret = !!test_bit(RXRPC_SOCK_MANAGE_RESPONSE, &rx->flags);
- if (set)
- set_bit(RXRPC_SOCK_MANAGE_RESPONSE, &rx->flags);
- else
- clear_bit(RXRPC_SOCK_MANAGE_RESPONSE, &rx->flags);
- release_sock(sk);
- return ret;
-}
-EXPORT_SYMBOL(rxrpc_sock_set_manage_response);
^ permalink raw reply related
* [PATCH net v3 00/14] rxrpc: Fix CHALLENGE packet handling
From: David Howells @ 2026-07-13 8:10 UTC (permalink / raw)
To: netdev
Cc: David Howells, Marc Dionne, Jakub Kicinski, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-afs, linux-kernel
Here's a fix for AF_RXRPC's CHALLENGE packet handling, addressing an issue
raised by Sashiko[1], plus some miscellaneous fixes found in the process of
fixing this, plus a number of things raised by Sashiko[2][3].
Firstly, the miscellaneous patches:
(1) Fix rxrpc_sendmsg so that it doesn't return an error if it queued the
last packet of a call. After that point, the error will be returned
by recvmsg() and returned it twice in two different places may
complicate userspace cleaning up its own structures.
(2) Fix a UAF in afs_make_call() whereby it looks at the call after the
first send (of the request) completes to see if it should make a
second send (e.g. for FS.StoreData content) - but the call may have
been freed if the first send wasn't marked MSG_MORE.
(3) Fix afs_fs_fetch_data() to set call->async on calls marked as being
asynchronous.
(4) Fix error handling in rxrpc_send_data() for if ->secure_packet()
returns an error.
(5) Fix the update of call->pending in rxrpc_send_data() in paths when the
call lock has been dropped.
(6) Fix the generation of notifications from rxrpc after call completion.
(7) Simplify afs_call refcounting to avoid trying to take refs when async
call notifications come in, and worse trying to get rid of them again
safely if the notifier work item is already queued. This is not
technically a bug fix, but the cleanup made patch 11 easier to handle.
(8) Make afs_put_call() use an enum value to indicate when it happens
rather than recording the function return address as the return
address isn't sufficiently unique if functions get inlined or
tail-called by the compiler. This is not a bug fix, but it made it
easier to debug the problem that patch 11 fixes.
(9) Fix another UAF in afs_make_call() whereby the function tries to abort
a call if the send fails, but the call may get torn down by async
notification before we get there.
And then there are the patches to fix CHALLENGE packet overqueuing and
simplify RESPONSE packet generation by pre-creating the RxGK application
data up front and passing it in a user key (thereby allowing userspace to
partake). This is split into five patches:
(10) Add a refcount to the user key payload.
(11) Make the AFS filesystem generate per-server appdata keys.
(12) Pass the appdata from AFS (or userspace) to rxrpc.
(13) Change over to using the appdata key to supply the appdata.
(14) Remove all the OOB stuff.
[!] Note that this entails a significant change in the UAPI for AF_RXRPC,
with the CMSG types and sockopt to support the OOB queuing being removed
and replaced with a new single CMSG type that conveys the user key ID. I
don't think it likely anyone is using this outside of my kafs-utils
package.
This also involves a change to the user-defined key type, making the
payload refcounted so that it can be accessed and the length read, then a
buffer allocated that will hold it and other data, and then the content
copied. The problem is that the user is perfectly at liberty to change the
content of a user-defined key (which will RCU-replace the content of the
key), so the length might change when we drop the RCU read lock in order to
allocate. This could be got around by locking the key->rwsem sharedly, but
that might be able to deadlock part of the rxrpc protocol engine if memory
reclaim occurs.
David
The patches can be found here also:
http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=rxrpc-fixes
Changes
=======
ver #3)
- Rebased on latest net/main.
- Removed two obsoleted patches.
ver #2)
- Split the CHALLENGE/RESPONSE fix into smaller patches.
- Fixed more Sashiko-reported bugs[2][3]:
- Added some more patches to fix some more bugs.
- Get rid of the AFS_SERVER_FL_APPDATA flag and check the pointer to the
appdata instead.
- Rename the appdata key pointer in the AFS_SERVER to reflect this one is
only for the YFS-RxGK security class.
- Use barriers when reading or writing the server appdata key pointer.
- Ignore the appdata for RxNULL, RxKAD and OpenAFS's RxGK for now.
- Check that sendmsg() with RXRPC_RESPONSE_APPDATA is passed a user key.
- Check that the appdata key's payload isn't NULL, for instance if it
gets revoked.
- Add some error path key_put()s in rxrpc_do_sendmsg().
[1] https://sashiko.dev/#/patchset/20260624163819.3017002-1-dhowells%40redhat.com
[2] https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com
[3] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260702144919.172295-1-dhowells%40redhat.com
David Howells (14):
rxrpc: Fix sendmsg to not return an error if last packet queued
afs: Fix UAF when sending a message
afs: Fix afs_fs_fetch_data() to set call->async
rxrpc: Fix packet encryption error handling
rxrpc: Fix update of call->tx_pending without holding lock
rxrpc: Fix generation of notifications after call completion
afs: Simplify call refcounting
afs: Make afs_put_call() take trace argument
afs: Fix UAF in afs_make_call()
keys: Add refcounting to user-defined key type payload
afs: Create a server appdata key
rxrpc: Pass appdata key to rxrpc_call and thence to rxrpc_bundle
rxrpc: Fix CHALLENGE packet overqueuing and simplify RESPONSE
generation
rxrpc: Remove OOB challenge/response code
fs/afs/cm_security.c | 311 +++++++++++++---------------
fs/afs/cmservice.c | 33 +--
fs/afs/file.c | 12 +-
fs/afs/fs_operation.c | 2 +-
fs/afs/fs_probe.c | 5 +
fs/afs/fsclient.c | 7 +-
fs/afs/internal.h | 63 +++---
fs/afs/main.c | 1 -
fs/afs/rxrpc.c | 137 +++++--------
fs/afs/server.c | 2 +-
fs/afs/vl_probe.c | 2 +-
fs/afs/vlclient.c | 8 +-
include/keys/user-type.h | 2 +
include/net/af_rxrpc.h | 21 +-
include/trace/events/afs.h | 46 +++--
include/trace/events/rxrpc.h | 4 +-
include/uapi/linux/rxrpc.h | 6 +-
net/dns_resolver/dns_key.c | 1 +
net/rxrpc/Makefile | 1 -
net/rxrpc/af_rxrpc.c | 49 +----
net/rxrpc/ar-internal.h | 23 +--
net/rxrpc/call_object.c | 2 +
net/rxrpc/call_state.c | 57 +++++-
net/rxrpc/conn_client.c | 2 +
net/rxrpc/conn_event.c | 68 +-----
net/rxrpc/key.c | 37 ++++
net/rxrpc/oob.c | 387 -----------------------------------
net/rxrpc/recvmsg.c | 126 ++----------
net/rxrpc/rxgk.c | 128 +++---------
net/rxrpc/rxkad.c | 27 ---
net/rxrpc/sendmsg.c | 107 +++++++---
net/rxrpc/server_key.c | 40 ----
security/keys/user_defined.c | 23 ++-
33 files changed, 540 insertions(+), 1200 deletions(-)
delete mode 100644 net/rxrpc/oob.c
^ permalink raw reply
* [PATCH net v3 13/14] rxrpc: Fix CHALLENGE packet overqueuing and simplify RESPONSE generation
From: David Howells @ 2026-07-13 8:10 UTC (permalink / raw)
To: netdev
Cc: David Howells, Marc Dionne, Jakub Kicinski, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-afs, linux-kernel,
Jeffrey Altman, Jarkko Sakkinen, keyrings, stable
In-Reply-To: <20260713081022.2186481-1-dhowells@redhat.com>
Currently, when a CHALLENGE packet comes in, it's queued in an OOB queue on
the AF_RXRPC socket that generated one of the calls on that connection for
the application (which might be in userspace) to service. The application
then picks up the CHALLENGE and requests a RESPONSE packet be generated,
allowing the app to include app-specific data in it if appropriate. There
is, however, no actual limit on the capacity of the CHALLENGE queue, and
this could be abused remotely - and also getting the OOB mechanism right
has proven tricky.
Further, by analogy with other AFS codebases, it's not actually necessary
to generate the application data in response to the CHALLENGE. The reason
I did this was to set the encryption on the app-data to be the same as that
specified in the CHALLENGE as the server must be able to handle that.
However, it's sufficient to use the encoding type set in the token that is
going to be sent to the server; presumably the kerberos server knows that
the fileserver can handle that type - otherwise why tell the client to use
it?
This is the main part of the fix. It switches the code over from using the
OOB communication mechanism to get the appdata on the fly from the
application (either the AFS filesystem or userspace) to using the appdata
key preemptively provided by the application.
The following changes are made:
(1) Revert to making the connection event processor work item parse the
CHALLENGE and generate the RESPONSE directly.
(2) Add another (optional) parameter that is passed in when an rxrpc
client call is created and ends up attached to the connection bundle
and is a user-type key containing the application data.
(3) RESPONSE generation looks at the bundle and if the app-data is there,
it will include it (if the security class is YFS-RxGK; RxKAD ignores
it).
The AFS filesystem driver creates an app-data key when it probes a
fileserver and attaches it to the afs_server struct. This is picked up
when a call is made to that server and thence passed to rxrpc.
Direct userspace users of AF_RXRPC can partake by creating a user-type key
containing the app-data they want to use and passing its serial ID in a
CMSG of type RXRPC_RESPONSE_APPDATA in the initial sendmsg() of a call.
Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE")
Link: https://sashiko.dev/#/patchset/20260624163819.3017002-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeffrey Altman <jaltman@auristor.com>
cc: Eric Dumazet <edumazet@google.com>
cc: "David S. Miller" <davem@davemloft.net>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: Jarkko Sakkinen <jarkko@kernel.org>
cc: linux-afs@lists.infradead.org
cc: keyrings@vger.kernel.org
cc: stable@kernel.org
---
net/rxrpc/af_rxrpc.c | 5 +---
net/rxrpc/conn_event.c | 68 ++----------------------------------------
net/rxrpc/recvmsg.c | 2 +-
net/rxrpc/rxgk.c | 57 +++++++++++++++++++++--------------
4 files changed, 39 insertions(+), 93 deletions(-)
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index a19c0fd3c51a..9eb6ca3c5ebf 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -598,10 +598,7 @@ static int rxrpc_sendmsg(struct socket *sock, struct msghdr *m, size_t len)
fallthrough;
case RXRPC_SERVER_BOUND:
case RXRPC_SERVER_LISTENING:
- if (m->msg_flags & MSG_OOB)
- ret = rxrpc_sendmsg_oob(rx, m, len);
- else
- ret = rxrpc_do_sendmsg(rx, m, len);
+ ret = rxrpc_do_sendmsg(rx, m, len);
/* The socket has been unlocked */
goto out;
default:
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 611c790bc6d0..9146b9d4c2ac 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -279,10 +279,7 @@ static int rxrpc_process_event(struct rxrpc_connection *conn,
switch (sp->hdr.type) {
case RXRPC_PACKET_TYPE_CHALLENGE:
- ret = conn->security->respond_to_challenge(conn, skb);
- sp->chall.conn = NULL;
- rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
- return ret;
+ return conn->security->respond_to_challenge(conn, skb);
case RXRPC_PACKET_TYPE_RESPONSE:
spin_lock_irq(&conn->state_lock);
@@ -425,66 +422,6 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
rxrpc_queue_conn(conn, rxrpc_conn_queue_rx_work);
}
-/*
- * Post a CHALLENGE packet to the socket of one of a connection's calls so that
- * it can get application data to include in the packet, possibly querying
- * userspace.
- */
-static bool rxrpc_post_challenge(struct rxrpc_connection *conn,
- struct sk_buff *skb)
-{
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
- struct rxrpc_call *call = NULL;
- struct rxrpc_sock *rx;
- bool respond = false, queued = false;
-
- sp->chall.conn =
- rxrpc_get_connection(conn, rxrpc_conn_get_challenge_input);
-
- if (!conn->security->challenge_to_recvmsg) {
- rxrpc_post_packet_to_conn(conn, skb);
- return true;
- }
-
- rcu_read_lock();
-
- for (int i = 0; i < ARRAY_SIZE(conn->channels); i++) {
- if (conn->channels[i].call) {
- call = conn->channels[i].call;
- rx = rcu_dereference(call->socket);
- if (!rx) {
- call = NULL;
- continue;
- }
-
- respond = true;
- if (test_bit(RXRPC_SOCK_MANAGE_RESPONSE, &rx->flags))
- break;
- call = NULL;
- }
- }
-
- if (!respond) {
- rcu_read_unlock();
- rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
- sp->chall.conn = NULL;
- return false;
- }
-
- if (call)
- queued = rxrpc_notify_socket_oob(call, skb);
- rcu_read_unlock();
- if (call && !queued) {
- rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
- sp->chall.conn = NULL;
- return false;
- }
-
- if (!call)
- rxrpc_post_packet_to_conn(conn, skb);
- return true;
-}
-
/*
* Input a connection-level packet.
*/
@@ -513,7 +450,8 @@ bool rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb)
}
if (!conn->security->validate_challenge(conn, skb))
return false;
- return rxrpc_post_challenge(conn, skb);
+ rxrpc_post_packet_to_conn(conn, skb);
+ return true;
case RXRPC_PACKET_TYPE_RESPONSE:
if (rxrpc_is_conn_aborted(conn)) {
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index 28b2148b5693..33577522ec02 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -405,7 +405,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
trace_rxrpc_recvmsg(0, rxrpc_recvmsg_enter, 0);
- if (flags & (MSG_OOB | MSG_TRUNC))
+ if (flags & MSG_TRUNC)
return -EOPNOTSUPP;
timeo = sock_rcvtimeo(&rx->sk, flags & MSG_DONTWAIT);
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
index 77a67ace1d24..f3e085b64502 100644
--- a/net/rxrpc/rxgk.c
+++ b/net/rxrpc/rxgk.c
@@ -11,6 +11,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/key-type.h>
+#include <keys/user-type.h>
#include "ar-internal.h"
#include "rxgk_common.h"
@@ -837,7 +838,7 @@ static noinline ssize_t rxgk_insert_response_header(struct rxrpc_connection *con
*/
static ssize_t rxgk_construct_authenticator(struct rxrpc_connection *conn,
struct sk_buff *challenge,
- const struct krb5_buffer *appdata,
+ const struct user_key_payload *appdata,
struct sk_buff *response,
size_t offset)
{
@@ -859,20 +860,20 @@ static ssize_t rxgk_construct_authenticator(struct rxrpc_connection *conn,
if (ret < 0)
return -EPROTO;
- a.appdata_len = htonl(appdata->len);
+ a.appdata_len = htonl(appdata->datalen);
ret = skb_store_bits(response, offset, &a, sizeof(a));
if (ret < 0)
return ret;
offset += sizeof(a);
- if (appdata->len) {
- ret = skb_store_bits(response, offset, appdata->data, appdata->len);
+ if (appdata->datalen) {
+ ret = skb_store_bits(response, offset, appdata->data, appdata->datalen);
if (ret < 0)
return ret;
- offset += appdata->len;
+ offset += appdata->datalen;
- ret = rxgk_pad_out(response, appdata->len, offset);
+ ret = rxgk_pad_out(response, appdata->datalen, offset);
if (ret < 0)
return ret;
offset += ret;
@@ -890,7 +891,7 @@ static ssize_t rxgk_construct_authenticator(struct rxrpc_connection *conn,
ret = skb_store_bits(response, offset, &b, sizeof(b));
if (ret < 0)
return ret;
- return sizeof(a) + xdr_round_up(appdata->len) + sizeof(b);
+ return sizeof(a) + xdr_round_up(appdata->datalen) + sizeof(b);
}
static ssize_t rxgk_encrypt_authenticator(struct rxrpc_connection *conn,
@@ -923,7 +924,7 @@ static ssize_t rxgk_encrypt_authenticator(struct rxrpc_connection *conn,
*/
static int rxgk_construct_response(struct rxrpc_connection *conn,
struct sk_buff *challenge,
- struct krb5_buffer *appdata)
+ const struct user_key_payload *appdata)
{
struct rxrpc_skb_priv *csp, *rsp;
struct rxgk_context *gk;
@@ -936,7 +937,7 @@ static int rxgk_construct_response(struct rxrpc_connection *conn,
if (IS_ERR(gk))
return PTR_ERR(gk);
- auth_len = 20 + (4 + appdata->len) + 12 + (1 + 4) * 4;
+ auth_len = 20 + (4 + appdata->datalen) + 12 + (1 + 4) * 4;
authx_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_ENCRYPT_MODE,
auth_len, &auth_offset);
len = sizeof(struct rxrpc_wire_header) +
@@ -1011,24 +1012,36 @@ static int rxgk_construct_response(struct rxrpc_connection *conn,
* Respond to a challenge packet.
*/
static int rxgk_respond_to_challenge(struct rxrpc_connection *conn,
- struct sk_buff *challenge,
- struct krb5_buffer *appdata)
+ struct sk_buff *challenge)
{
- _enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
+ struct user_key_payload dummy = {}, *appdata = &dummy;
+ int ret;
+
+ _enter("{%d,%u,%x,%x}",
+ conn->debug_id, conn->service_id,
+ key_serial(conn->key), key_serial(conn->bundle->app_data));
if (key_validate(conn->key) < 0)
return rxrpc_abort_conn(conn, NULL, RXGK_EXPIRED, -EPROTO,
rxgk_abort_chall_key_expired);
- return rxgk_construct_response(conn, challenge, appdata);
-}
+ if (conn->bundle->app_data) {
+ rcu_read_lock();
+ appdata = (struct user_key_payload *)
+ user_key_payload_rcu(conn->bundle->app_data);
+ if (appdata && !refcount_inc_not_zero(&appdata->ref))
+ appdata = NULL;
+ rcu_read_unlock();
+ if (!appdata)
+ return rxrpc_abort_conn(conn, NULL, RXGK_EXPIRED, -EKEYREVOKED,
+ rxgk_abort_chall_key_expired);
+ }
-static int rxgk_respond_to_challenge_no_appdata(struct rxrpc_connection *conn,
- struct sk_buff *challenge)
-{
- struct krb5_buffer appdata = {};
+ ret = rxgk_construct_response(conn, challenge, appdata);
- return rxgk_respond_to_challenge(conn, challenge, &appdata);
+ if (appdata != &dummy)
+ put_user_key_payload(appdata);
+ return ret;
}
/**
@@ -1044,9 +1057,7 @@ static int rxgk_respond_to_challenge_no_appdata(struct rxrpc_connection *conn,
int rxgk_kernel_respond_to_challenge(struct sk_buff *challenge,
struct krb5_buffer *appdata)
{
- struct rxrpc_skb_priv *csp = rxrpc_skb(challenge);
-
- return rxgk_respond_to_challenge(csp->chall.conn, challenge, appdata);
+ return -EINVAL;
}
EXPORT_SYMBOL(rxgk_kernel_respond_to_challenge);
@@ -1348,7 +1359,7 @@ const struct rxrpc_security rxgk_yfs = {
.validate_challenge = rxgk_validate_challenge,
.challenge_to_recvmsg = rxgk_challenge_to_recvmsg,
.sendmsg_respond_to_challenge = rxgk_sendmsg_respond_to_challenge,
- .respond_to_challenge = rxgk_respond_to_challenge_no_appdata,
+ .respond_to_challenge = rxgk_respond_to_challenge,
.verify_response = rxgk_verify_response,
.clear = rxgk_clear,
.default_decode_ticket = rxgk_yfs_decode_ticket,
^ permalink raw reply related
* [PATCH net v3 12/14] rxrpc: Pass appdata key to rxrpc_call and thence to rxrpc_bundle
From: David Howells @ 2026-07-13 8:10 UTC (permalink / raw)
To: netdev
Cc: David Howells, Marc Dionne, Jakub Kicinski, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-afs, linux-kernel,
Jeffrey Altman, Jarkko Sakkinen, keyrings, stable
In-Reply-To: <20260713081022.2186481-1-dhowells@redhat.com>
Currently, when a CHALLENGE packet comes in, it's queued in an OOB queue on
the AF_RXRPC socket that generated one of the calls on that connection for
the application (which might be in userspace) to service. The application
then picks up the CHALLENGE and requests a RESPONSE packet be generated,
allowing the app to include app-specific data in it if appropriate. There
is, however, no actual limit on the capacity of the CHALLENGE queue, and
this could be abused remotely - and also getting the OOB mechanism right
has proven tricky.
Further, by analogy with other AFS codebases, it's not actually necessary
to generate the application data in response to the CHALLENGE. The reason
I did this was to set the encryption on the app-data to be the same as that
specified in the CHALLENGE as the server must be able to handle that.
However, it's sufficient to use the encoding type set in the token that is
going to be sent to the server; presumably the kerberos server knows that
the fileserver can handle that type - otherwise why tell the client to use
it?
This is a part of the fix. With this, the appdata key created by the AFS
filesystem or passed in via sendmsg CMSG to a user AF_RXRPC socket is added
to an rxrpc_call struct and will then be added to an rxrpc_bundle struct.
Note that afs_make_op_call() has to be moved so that it can call
afs_use_server(). afs_operation-based calls did not heretofore 'use' the
server and server will be 'un-used' by afs_free_call().
Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE")
Link: https://sashiko.dev/#/patchset/20260624163819.3017002-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeffrey Altman <jaltman@auristor.com>
cc: Eric Dumazet <edumazet@google.com>
cc: "David S. Miller" <davem@davemloft.net>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: Jarkko Sakkinen <jarkko@kernel.org>
cc: linux-afs@lists.infradead.org
cc: keyrings@vger.kernel.org
cc: stable@kernel.org
---
fs/afs/internal.h | 33 +++++++++++++++++----------------
fs/afs/rxrpc.c | 21 ++++++++++++++++++++-
include/net/af_rxrpc.h | 1 +
include/trace/events/afs.h | 1 +
include/uapi/linux/rxrpc.h | 1 +
net/rxrpc/af_rxrpc.c | 3 +++
net/rxrpc/ar-internal.h | 3 +++
net/rxrpc/call_object.c | 2 ++
net/rxrpc/conn_client.c | 2 ++
net/rxrpc/sendmsg.c | 25 ++++++++++++++++++++++++-
10 files changed, 74 insertions(+), 18 deletions(-)
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index d1c29593c5d0..57519888b978 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1414,22 +1414,6 @@ static inline void afs_see_call(struct afs_call *call, enum afs_call_trace why)
atomic_read(&call->net->nr_outstanding_calls));
}
-static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *call,
- gfp_t gfp)
-{
- struct afs_addr_list *alist = op->estate->addresses;
-
- op->call = afs_get_call(call, afs_call_trace_get_op_call);
- op->type = call->type;
- call->op = op;
- call->key = op->key;
- call->intr = !(op->flags & AFS_OPERATION_UNINTR);
- call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer);
- call->service_id = op->server->service_id;
- afs_make_call(call, gfp);
- afs_put_call(call, afs_call_trace_put_made_call);
-}
-
static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size)
{
call->iov_len = size;
@@ -1749,6 +1733,23 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
return &vnode->netfs.inode;
}
+static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *call,
+ gfp_t gfp)
+{
+ struct afs_addr_list *alist = op->estate->addresses;
+
+ op->call = afs_get_call(call, afs_call_trace_get_op_call);
+ op->type = call->type;
+ call->op = op;
+ call->server = afs_use_server(op->server, false, afs_server_trace_use_call);
+ call->key = op->key;
+ call->intr = !(op->flags & AFS_OPERATION_UNINTR);
+ call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer);
+ call->service_id = op->server->service_id;
+ afs_make_call(call, gfp);
+ afs_put_call(call, afs_call_trace_put_made_call);
+}
+
/*
* Note that a dentry got changed. We need to set d_fsdata to the data version
* number derived from the result of the operation. It doesn't matter if
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index 6dc6fd853832..1a110448dbdb 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -312,6 +312,7 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
struct msghdr msg;
struct kvec iov[1];
unsigned int debug_id = call->debug_id;
+ struct key *app_data = NULL;
size_t len;
bool write_iter = call->write_iter;
s64 tx_total_len;
@@ -342,8 +343,26 @@ void afs_make_call(struct afs_call *call, gfp_t gfp)
if (call->async)
afs_get_call(call, afs_call_trace_get_make_async_call);
+ if (call->key && call->server) {
+ u32 krb5_enctype = 0;
+ u8 security_index = 0;
+
+ rxrpc_kernel_query_key(call->key, &security_index, &krb5_enctype);
+ switch (security_index) {
+#ifdef CONFIG_RXGK
+ case RXRPC_SECURITY_YFS_RXGK:
+ /* Read the key pointer before the appdata */
+ app_data = smp_load_acquire(&call->server->yfs_rxgk_appdata);
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
/* create a call */
- rxcall = rxrpc_kernel_begin_call(call->net->socket, call->peer, call->key,
+ rxcall = rxrpc_kernel_begin_call(call->net->socket, call->peer,
+ call->key, app_data,
(unsigned long)call,
tx_total_len,
call->max_lifespan,
diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h
index c4b68049c06f..19c61a2f5af3 100644
--- a/include/net/af_rxrpc.h
+++ b/include/net/af_rxrpc.h
@@ -55,6 +55,7 @@ void rxrpc_kernel_set_notifications(struct socket *sock,
struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
struct rxrpc_peer *peer,
struct key *key,
+ struct key *app_data,
unsigned long user_call_ID,
s64 tx_total_len,
u32 hard_timeout,
diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h
index a1963e21f034..a4f57414d527 100644
--- a/include/trace/events/afs.h
+++ b/include/trace/events/afs.h
@@ -160,6 +160,7 @@ enum yfs_cm_operation {
EM(afs_server_trace_unuse_slist_isort, "UNU isort") \
EM(afs_server_trace_update, "UPDATE ") \
EM(afs_server_trace_use_by_uuid, "USE uuid ") \
+ EM(afs_server_trace_use_call, "USE call ") \
EM(afs_server_trace_use_cm_call, "USE cm-cl") \
EM(afs_server_trace_use_get_caps, "USE gcaps") \
EM(afs_server_trace_use_give_up_cb, "USE gvupc") \
diff --git a/include/uapi/linux/rxrpc.h b/include/uapi/linux/rxrpc.h
index d9735abd4c79..bcdfdf9c67a1 100644
--- a/include/uapi/linux/rxrpc.h
+++ b/include/uapi/linux/rxrpc.h
@@ -63,6 +63,7 @@ enum rxrpc_cmsg_type {
RXRPC_RESPOND = 17, /* Cs-: Respond to a challenge */
RXRPC_RESPONDED = 18, /* S-r: Data received in RESPONSE */
RXRPC_RESP_RXGK_APPDATA = 19, /* Cs-: RESPONSE: RxGK app data to include */
+ RXRPC_RESPONSE_APPDATA = 20, /* Cs-: User key holding app data for RESPONSE */
RXRPC__SUPPORTED
};
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 9ab0f22c881e..a19c0fd3c51a 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -318,6 +318,7 @@ EXPORT_SYMBOL(rxrpc_kernel_put_peer);
* @sock: The socket on which to make the call
* @peer: The peer to contact
* @key: The security context to use (defaults to socket setting)
+ * @app_data: The security response application data (or NULL)
* @user_call_ID: The ID to use
* @tx_total_len: Total length of data to transmit during the call (or -1)
* @hard_timeout: The maximum lifespan of the call in sec
@@ -340,6 +341,7 @@ EXPORT_SYMBOL(rxrpc_kernel_put_peer);
struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
struct rxrpc_peer *peer,
struct key *key,
+ struct key *app_data,
unsigned long user_call_ID,
s64 tx_total_len,
u32 hard_timeout,
@@ -368,6 +370,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
key = NULL; /* a no-security key */
memset(&p, 0, sizeof(p));
+ p.app_data = app_data;
p.user_call_ID = user_call_ID;
p.tx_total_len = tx_total_len;
p.interruptibility = interruptibility;
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index b6e7e8c5e96f..20c10428a50e 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -516,6 +516,7 @@ struct rxrpc_bundle {
struct rxrpc_local *local; /* Representation of local endpoint */
struct rxrpc_peer *peer; /* Remote endpoint */
struct key *key; /* Security details */
+ struct key *app_data; /* Security response app data */
struct list_head proc_link; /* Link in net->bundle_proc_list */
const struct rxrpc_security *security; /* applied security module */
refcount_t ref;
@@ -720,6 +721,7 @@ struct rxrpc_call {
struct rxrpc_sock __rcu *socket; /* socket responsible */
struct rxrpc_net *rxnet; /* Network namespace to which call belongs */
struct key *key; /* Security details */
+ struct key *app_data; /* Security response app data */
const struct rxrpc_security *security; /* applied security module */
struct mutex user_mutex; /* User access mutex */
struct sockaddr_rxrpc dest_srx; /* Destination address */
@@ -914,6 +916,7 @@ enum rxrpc_command {
};
struct rxrpc_call_params {
+ struct key *app_data; /* Security response app data */
s64 tx_total_len; /* Total Tx data length (if send data) */
unsigned long user_call_ID; /* User's call ID */
struct {
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c
index 817ed9acb91e..9f6130d90c4c 100644
--- a/net/rxrpc/call_object.c
+++ b/net/rxrpc/call_object.c
@@ -211,6 +211,7 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx,
call->interruptibility = p->interruptibility;
call->tx_total_len = p->tx_total_len;
call->key = key_get(cp->key);
+ call->app_data = key_get(p->app_data);
call->peer = rxrpc_get_peer(cp->peer, rxrpc_peer_get_call);
call->local = rxrpc_get_local(cp->local, rxrpc_local_get_call);
call->security_level = cp->security_level;
@@ -697,6 +698,7 @@ static void rxrpc_destroy_call(struct work_struct *work)
rxrpc_put_peer(call->peer, rxrpc_peer_put_call);
rxrpc_put_local(call->local, rxrpc_local_put_call);
key_put(call->key);
+ key_put(call->app_data);
call_rcu(&call->rcu, rxrpc_rcu_free_call);
}
diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c
index 48519f0de185..5cbfa7b223e0 100644
--- a/net/rxrpc/conn_client.c
+++ b/net/rxrpc/conn_client.c
@@ -81,6 +81,7 @@ static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_call *call,
bundle->local = call->local;
bundle->peer = rxrpc_get_peer(call->peer, rxrpc_peer_get_bundle);
bundle->key = key_get(call->key);
+ bundle->app_data = key_get(call->app_data);
bundle->security = call->security;
bundle->exclusive = test_bit(RXRPC_CALL_EXCLUSIVE, &call->flags);
bundle->upgrade = test_bit(RXRPC_CALL_UPGRADE, &call->flags);
@@ -118,6 +119,7 @@ static void rxrpc_free_bundle(struct rxrpc_bundle *bundle)
write_unlock(&bundle->local->rxnet->conn_lock);
rxrpc_put_peer(bundle->peer, rxrpc_peer_put_bundle);
key_put(bundle->key);
+ key_put(bundle->app_data);
kfree(bundle);
}
diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c
index 71343998b87d..cdf35440317d 100644
--- a/net/rxrpc/sendmsg.c
+++ b/net/rxrpc/sendmsg.c
@@ -12,6 +12,7 @@
#include <linux/skbuff.h>
#include <linux/export.h>
#include <linux/sched/signal.h>
+#include <keys/user-type.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
@@ -530,6 +531,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
static int rxrpc_sendmsg_cmsg(struct msghdr *msg, struct rxrpc_send_params *p)
{
struct cmsghdr *cmsg;
+ key_serial_t key_id;
+ key_ref_t key;
bool got_user_ID = false;
int len;
@@ -614,6 +617,22 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg, struct rxrpc_send_params *p)
return -ERANGE;
break;
+ case RXRPC_RESPONSE_APPDATA:
+ if (len != sizeof(key_serial_t))
+ return -EINVAL;
+ if (p->call.app_data)
+ return -EINVAL;
+ key_id = *(key_serial_t *)CMSG_DATA(cmsg);
+ key = lookup_user_key(key_id, 0, KEY_NEED_SEARCH);
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+ if (key_ref_to_ptr(key)->type == &key_type_user) {
+ key_ref_put(key);
+ return -EINVAL;
+ }
+ p->call.app_data = key_ref_to_ptr(key);
+ break;
+
default:
return -EINVAL;
}
@@ -722,8 +741,10 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
goto error_release_sock;
call = rxrpc_new_client_call_for_sendmsg(rx, msg, &p);
/* The socket is now unlocked... */
- if (IS_ERR(call))
+ if (IS_ERR(call)) {
+ key_put(p.call.app_data);
return PTR_ERR(call);
+ }
/* ... and we have the call lock. */
p.call.nr_timeouts = 0;
ret = 0;
@@ -808,11 +829,13 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
mutex_unlock(&call->user_mutex);
error_put:
rxrpc_put_call(call, rxrpc_call_put_sendmsg);
+ key_put(p.call.app_data);
_leave(" = %d", ret);
return ret;
error_release_sock:
release_sock(&rx->sk);
+ key_put(p.call.app_data);
return ret;
}
^ permalink raw reply related
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