7.2.7-rc1 review
Subsystems (5)
Greg Kroah-Hartman
maintainerPATCH 221/733 erofs: disable LZ4 rolling decompression for now
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gao Xiang <[email protected]>
[ Upstream commit 82e664cf1219c459c33aae931b222cf951af9cb7 ]
LZ4 rolling decompression [1] was introduced to reduce the memory footprint of temporary pages:
For many cases, it is needed for users to read small data within a compressed extent (pcluster), either due to random small read, or since uptodate folios (typically order-0) cannot be reused for decompression again since decompression algorithm refills already-uptodate folios.
Rolling decompression works because LZ4 is LZ77-based and only refers to the most recent 64 KiB of decompressed data, so in theory only a bounded rolling window of temporary pages is needed when decompressing.
It can save a lot of temporary memory, e.g.
601,960-byte data can be compressed into a 256k LZ4 compressed extent, which means it needs 146 extra pages per request in the worst case if rolling decompression is disabled.
However, the upstream LZ4 implementation is not under EROFS' control:
For example, the literal copy memmove() may still **copy long literals backward** on x86 based on the address comparison even when the source and destination ranges do not overlap (IOWs, inline decompression doesn't need to be considered here). That breaks the rolling assumption and makes the optimization broken.
Disable it for now to make sure the data correctness first since EROFS is used everywhere now: The rolling window approach can be revived once we either ensure that the official LZ4 code always copies forward for non-overlapping ranges or maintain our own LZ4 implementation in EROFS.
The main impact is a higher runtime memory footprint; However, recent commit 0f6273ab4637 ("erofs: add a reserved buffer pool for lz4 decompression") helps mitigate this when enabled but it's still not perfect.
[1] https://www.usenix.org/conference/atc19/presentation/gao
§ 3.3 Decompression
--- fs/erofs/decompressor.c | 55 +++++++++-------------------------------- fs/erofs/internal.h | 6 +---- fs/erofs/zdata.c | 18 +++----------- 3 files changed, 16 insertions(+), 63 deletions(-)
fs/erofs/decompressor.c +12 -43
@@ -7,8 +7,6 @@#include "compress.h" #include <linux/lz4.h> -#define LZ4_MAX_DISTANCE_PAGES (DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1) -static int z_erofs_load_lz4_config (struct super_block *sb,struct erofs_super_block *dsb,void *data,int size) {
@@ -21,8 +19,6 @@ static int z_erofs_load_lz4_config(struct super_block *sb,erofs_err (sb,"invalid lz4 cfgs, size=%u" , size);return -EINVAL ; } - distance =le16_to_cpu (lz4->max_distance); - sbi->lz4.max_pclusterblks =le16_to_cpu (lz4->max_pclusterblks);if (!sbi->lz4.max_pclusterblks) { sbi->lz4.max_pclusterblks =1 ;/* reserved case */
@@ -39,45 +35,25 @@ static int z_erofs_load_lz4_config(struct super_block *sb, sbi->lz4.max_pclusterblks =1 ; sbi->available_compr_algs =1 <<Z_EROFS_COMPRESSION_LZ4 ; } - - sbi->lz4.max_distance_pages = distance ? -DIV_ROUND_UP (distance,PAGE_SIZE ) +1 : -LZ4_MAX_DISTANCE_PAGES ;return z_erofs_gbuf_growsize (sbi->lz4.max_pclusterblks); }/* -* Fill all gaps with bounce pages if it's a sparse page list. Also check if -* all physical pages are consecutive, which can be seen for moderate CR. +* Fill all gaps with bounce pages if it's a sparse page list (for example some +* folios are already uptodate and thus can be mapped into userspace). Also +* check if pages are physically consecutive, which can be seen for moderate CR. */ -static int z_erofs_lz4_prepare_dstpages (struct z_erofs_decompress_req *rq, -struct page **pagepool) +static int z_erofs_oneshot_prepare_dstpages (struct z_erofs_decompress_req *rq, +struct page **pagepool) { -struct page *availables[LZ4_MAX_DISTANCE_PAGES ] = {NULL }; -unsigned long bounced[DIV_ROUND_UP (LZ4_MAX_DISTANCE_PAGES , -BITS_PER_LONG )] = {0 }; -unsigned int lz4_max_distance_pages = -EROFS_SB (rq->sb)->lz4.max_distance_pages;void *kaddr =NULL ; -unsigned int i, j, top; +unsigned int i; - top =0 ; -for (i = j =0 ; i < rq->outpages; ++i, ++j) { -struct page *const page = rq->out[i]; -struct page *victim; - -if (j >= lz4_max_distance_pages) - j =0 ; - -/* 'valid' bounced can only be tested after a complete round */ -if (!rq->fillgaps &&test_bit (j, bounced)) { -DBG_BUGON (i < lz4_max_distance_pages); -DBG_BUGON (top >= lz4_max_distance_pages); - availables[top++] = rq->out[i - lz4_max_distance_pages]; - } +for (i =0 ; i < rq->outpages; ++i) { +struct page *page, *victim; + page = rq->out[i];if (page) { -__clear_bit (j, bounced);if (!PageHighMem (page)) {if (!i) { kaddr =page_address (page);
@@ -89,21 +65,14 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq,continue ; } } - kaddr =NULL ; -continue ; - } - kaddr =NULL ; -__set_bit (j, bounced); - -if (top) { - victim = availables[--top]; }else { victim =__erofs_allocpage (pagepool, rq->gfp, true);if (!victim)return -ENOMEM ;set_page_private (victim,Z_EROFS_SHORTLIVED_PAGE ); + rq->out[i] = victim; } - rq->out[i] = victim; + kaddr =NULL ; }return kaddr ?1 :0 ; }
@@ -266,7 +235,7 @@ static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, dst_maptype =0 ; }else {/* general decoding path which can be used for all cases */ - ret =z_erofs_lz4_prepare_dstpages (rq, pagepool); + ret =z_erofs_oneshot_prepare_dstpages (rq, pagepool);if (ret <0 )return ERR_PTR (ret);if (ret >0 ) {
fs/erofs/internal.h +1 -5
@@ -71,12 +71,8 @@ struct erofs_dev_context {bool flatdev; }; -/* all filesystem-wide lz4 configurations */ struct erofs_sb_lz4_info { -/* # of pages needed for EROFS lz4 rolling decompression */ -u16 max_distance_pages; -/* maximum possible blocks for pclusters in the filesystem */ -u16 max_pclusterblks; +u16 max_pclusterblks;/* maximum physical blocks for LZ4 pclusters */ };struct erofs_xattr_prefix_item {
fs/erofs/zdata.c +3 -15
@@ -1264,7 +1264,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)const struct z_erofs_decompressor *alg = z_erofs_decomp[pcl->algorithmformat];bool try_free = true; -int i, j, jtop, err2, err = eio ? -EIO :0 ; +int i, err2, err = eio ? -EIO :0 ;struct page *page;bool overlapped;const char *reason;
@@ -1353,7 +1353,6 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio) be->compressed_pages >= be->onstack_pages + Z_EROFS_ONSTACK_PAGES)kvfree (be->compressed_pages); - jtop =0 ;z_erofs_fill_other_copies (be, err);for (i =0 ; i < be->nr_pages; ++i) { page = be->decompressed_pages[i];
@@ -1361,22 +1360,11 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)continue ;DBG_BUGON (z_erofs_page_is_invalidated (page)); -if (!z_erofs_is_shortlived_page (page)) { +if (!z_erofs_is_shortlived_page (page))erofs_onlinefolio_end (page_folio (page), err, true); -continue ; - } -if (pcl->algorithmformat !=Z_EROFS_COMPRESSION_LZ4 ) { +else erofs_pagepool_add (be->pagepool, page); -continue ; - } -for (j =0 ; j < jtop && be->decompressed_pages[j] != page; ++j) - ; -if (j >= jtop)/* this bounce page is newly detected */ - be->decompressed_pages[jtop++] = page; } -while (jtop) -erofs_pagepool_add (be->pagepool, - be->decompressed_pages[--jtop]);if (be->decompressed_pages != be->onstack_pages)kvfree (be->decompressed_pages);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 222/733 selftests/alsa: Fix the step check for INTEGER controls
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit 8efd5f623c63584c2e284a837a7795d95a0491cb ]
The modulo sits inside the subtraction, so the check evaluates int_val - (min % step) rather than (int_val - min) % step. The INTEGER64 branch below it is parenthesised correctly.
The written form passes only when the value equals min % step, and such a value is always on a step boundary, so it never misses a real violation. It only reports valid values as invalid.
snd-aloop declares step 1 on four controls, so every non-zero value on them is reported. Before:
# PCM Rate Shift 100000.0 value 100000 invalid for step 1 minimum 80000 # Totals: pass:660 fail:101 xfail:0 xpass:0 skip:296 error:0
After, same card, nothing else changed:
# Totals: pass:740 fail:21 xfail:0 xpass:0 skip:296 error:0
Eighteen files under sound/ declare a non-zero step.
--- tools/testing/selftests/alsa/mixer-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/testing/selftests/alsa/mixer-test.c +2 -2
@@ -319,8 +319,8 @@ static bool ctl_value_index_valid(struct ctl_data *ctl,/* Only check step size if there is one and we're in bounds */ if (snd_ctl_elem_info_get_step (ctl->info) && - (int_val -snd_ctl_elem_info_get_min (ctl->info) % -snd_ctl_elem_info_get_step (ctl->info))) { + (int_val -snd_ctl_elem_info_get_min (ctl->info)) % +snd_ctl_elem_info_get_step (ctl->info)) { ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n", ctl->name, index, int_val, snd_ctl_elem_info_get_step(ctl->info),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 223/733 ALSA: caiaq: Fix potential double-free at error path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 3b26ceef88c110f4d188387cffa0df78657be904 ]
The fix for caiaq driver's resource management to handle the errors tries to release the resources in a common destructor call, but as a sashiko review for another patch suggested, some of the audio resources such as URBs have been already freed, and this may lead to a double-free.
For addressing the double-free, call the common destructor function from each place, and assure that the resource pointers get cleared.
--- sound/usb/caiaq/audio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
sound/usb/caiaq/audio.c +5 -5
@@ -828,16 +828,13 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) cdev->data_urbs_in =alloc_urbs (cdev,SNDRV_PCM_STREAM_CAPTURE , &ret);if (ret <0 ) { -kfree (cdev->data_cb_info); -free_urbs (cdev->data_urbs_in); +snd_usb_caiaq_audio_free (cdev);return ret; } cdev->data_urbs_out =alloc_urbs (cdev,SNDRV_PCM_STREAM_PLAYBACK , &ret);if (ret <0 ) { -kfree (cdev->data_cb_info); -free_urbs (cdev->data_urbs_in); -free_urbs (cdev->data_urbs_out); +snd_usb_caiaq_audio_free (cdev);return ret; }
@@ -858,6 +855,9 @@ void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev)dev_dbg (dev,"%s(%p)\n" , __func__, cdev);free_urbs (cdev->data_urbs_in); + cdev->data_urbs_in =NULL ;free_urbs (cdev->data_urbs_out); + cdev->data_urbs_out =NULL ;kfree (cdev->data_cb_info); + cdev->data_cb_info =NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 224/733 drm/rockchip: dw_dp: Select DRM_BRIDGE_CONNECTOR
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Igor Paunovic <[email protected]>
[ Upstream commit c83e3e806d4c115f1dad9ef756db4cd91448a262 ]
dw_dp-rockchip.c calls drm_bridge_connector_init(), but ROCKCHIP_DW_DP does not select DRM_BRIDGE_CONNECTOR. A configuration with ROCKCHIP_DW_DP as the only enabled Rockchip output option fails to link:
aarch64-linux-gnu-ld: drivers/gpu/drm/rockchip/dw_dp-rockchip.o: in function `dw_dp_rockchip_bind':
dw_dp-rockchip.c:(.text+0x1d4): undefined reference to `drm_bridge_connector_init'
Five other Rockchip encoder options that call drm_bridge_connector_init() (ROCKCHIP_ANALOGIX_DP, ROCKCHIP_CDN_DP, ROCKCHIP_DW_HDMI_QP, ROCKCHIP_LVDS, ROCKCHIP_RGB) already select it, which masks the gap in any configuration that enables one of them. ROCKCHIP_INNO_HDMI is covered through its DRM_INNO_HDMI core option.
The same change was posted by Marius Dinu in March and dropped when the failure stopped reproducing in his build. The failure is configuration-dependent - any other enabled option that selects DRM_BRIDGE_CONNECTOR hides it - and it still reproduces on current drm-misc-next with the configuration described above.
Select DRM_BRIDGE_CONNECTOR like the other users do.
--- drivers/gpu/drm/rockchip/Kconfig | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/rockchip/Kconfig +1 -0
@@ -68,6 +68,7 @@ config ROCKCHIP_CDN_DP config ROCKCHIP_DW_DP bool "Rockchip specific extensions for Synopsys DW DP" + select DRM_BRIDGE_CONNECTOR help This selects support for Rockchip SoC specific extensions to enable Synopsys DesignWare Cores based DisplayPort transmit
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/733 drm/rockchip: rk3066_hdmi: Add missing Kconfig selects
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Igor Paunovic <[email protected]>
[ Upstream commit d72aa5cf045a69d5fd433cde5dd4e113d9558fd1 ]
rk3066_hdmi.c calls drm_bridge_connector_init(), but ROCKCHIP_RK3066_HDMI selects neither DRM_BRIDGE_CONNECTOR nor DRM_DISPLAY_HELPER, whose module carries the bridge-connector code. A configuration with ROCKCHIP_RK3066_HDMI as the only enabled Rockchip output option fails to link:
aarch64-linux-gnu-ld: drivers/gpu/drm/rockchip/rk3066_hdmi.o: in function `rk3066_hdmi_bind':
rk3066_hdmi.c:(.text+0x7a4): undefined reference to `drm_bridge_connector_init'
aarch64-linux-gnu-ld: drivers/gpu/drm/rockchip/rk3066_hdmi.o: in function `rk3066_hdmi_bridge_atomic_enable':
rk3066_hdmi.c:(.text+0xe74): undefined reference to `drm_atomic_helper_connector_hdmi_update_infoframes'
Select both, like ROCKCHIP_CDN_DP, ROCKCHIP_LVDS and ROCKCHIP_RGB do. DRM_BRIDGE_CONNECTOR in turn selects DRM_DISPLAY_HDMI_STATE_HELPER, which resolves the second symbol.
--- drivers/gpu/drm/rockchip/Kconfig | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/rockchip/Kconfig +2 -0
@@ -146,6 +146,8 @@ config ROCKCHIP_RGB config ROCKCHIP_RK3066_HDMI bool "Rockchip specific extensions for RK3066 HDMI" depends on DRM_ROCKCHIP + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR help This selects support for Rockchip SoC specific extensions for the RK3066 HDMI driver. If you want to enable
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/733 bpf: Reject key-less BTF for hash maps
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 0895a0c0734703be5532f3883c42db95615fd98b ]
map_check_btf() allows a key-less BTF (btf_key_type_id == 0) only for maps that have a ->map_check_btf callback, and leaves the actual decision to that callback. Hash maps used to have no ->map_check_btf, so a key-less BTF was rejected outright.
That changed when htab and rhtab gained a ->map_check_btf to register a dtor - htab in commit 1df97a7453ee ("bpf: Register dtor for freeing special fields") and rhtab in commit 6905f8601298 ("bpf: Allow special fields in resizable hashtab"). Neither looks at the key, so a key-less hash map now passes map_check_btf() and gets created. Reading it back through bpffs feeds the key type_id 0 into btf_type_seq_show(); btf_type_by_id() returns the void type, kind_ops[BTF_KIND_UNKN] is NULL, and btf_type_show() dereferences it:
RIP: 0010:btf_type_show+0x223/0x2e0 kernel/bpf/btf.c:8232 RSP: 0018:ffffc9000399f868 EFLAGS: 00010206 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000005 RSI: 0000000000000000 RDI: 0000000000000028 RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000 R10: ffffc9000399f970 R11: 0000000000000001 R12: ffffffff9b96b140 R13: ffffc9000399f8e0 R14: ffff88803d393c00 R15: 0000000000000003 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000200000000000 CR3: 000000003d213000 CR4: 0000000000352ef0 DR0: 0000000039ae8f55 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Call Trace: <TASK> btf_type_seq_show_flags+0xca/0x120 kernel/bpf/btf.c:8250 htab_map_seq_show_elem+0x12e/0x350 kernel/bpf/hashtab.c:1669 map_seq_show+0x13d/0x1e0 kernel/bpf/inode.c:293 traverse.part.0.constprop.0+0x107/0x650 fs/seq_file.c:112 traverse fs/seq_file.c:99 [inline] seq_read_iter+0x93f/0x1270 fs/seq_file.c:196 seq_read+0x344/0x4d0 fs/seq_file.c:163 vfs_read+0x1e4/0xb40 fs/read_write.c:572 ksys_pread64 fs/read_write.c:764 [inline] __do_sys_pread64 fs/read_write.c:772 [inline] __se_sys_pread64 fs/read_write.c:769 [inline] __x64_sys_pread64+0x1eb/0x250 fs/read_write.c:769 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x123/0x790 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f
Reject a key-less BTF in htab_map_check_btf() and rhtab_map_check_btf(), restoring the previous behavior.
--- kernel/bpf/hashtab.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/bpf/hashtab.c +6 -0
@@ -530,6 +530,9 @@ static int htab_map_check_btf(struct bpf_map *map, const struct btf *btf, { struct bpf_htab *htab = container_of(map, struct bpf_htab, map); +if (btf_type_is_void (key_type)) +return -EINVAL ; +if (htab_is_prealloc (htab))return 0 ;/*
@@ -3113,6 +3116,9 @@ static int rhtab_map_check_btf(struct bpf_map *map, const struct btf *btf, { struct bpf_rhtab *rhtab = container_of(map, struct bpf_rhtab, map); +if (btf_type_is_void (key_type)) +return -EINVAL ; +return bpf_ma_set_dtor (map, &rhtab->ma, rhtab_mem_dtor); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/733 bpf: Fix NULL-ptr-deref when showing a void BTF type
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 4ea508b9ebd78bce7f212166d2e2cba66b875f08 ]
btf_modifier_show() resolves the modifier and then calls btf_type_ops(t)->show() unconditionally. For the void type (type_id 0, BTF_KIND_UNKN) kind_ops[] has no entry, so ->show is NULL.
A "const void" (a modifier resolving to void) cannot be a map key or value - map_check_btf() rejects it because void has no size - so the map dump path does not reach it. But bpf_snprintf_btf() takes a type_id straight from the BPF program, and passing such a "const void" from the vmlinux BTF NULL-derefs:
KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] RIP: 0010:btf_modifier_show (kernel/bpf/btf.c:2914) Call Trace: <TASK> btf_type_show (kernel/bpf/btf.c:8251) btf_type_snprintf_show (kernel/bpf/btf.c:8321) bpf_snprintf_btf (kernel/trace/bpf_trace.c:1047) bpf_prog_test_run_raw_tp (net/bpf/test_run.c:829) __sys_bpf (kernel/bpf/syscall.c:4804) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
Fall back to btf_df_show() when the resolved type has no show op; it emits the "<unsupported kind:N>" placeholder already used for kinds like FWD and FUNC. bpf_snprintf_btf() then returns the length as usual.
--- kernel/bpf/btf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
kernel/bpf/btf.c +8 -1
@@ -2912,7 +2912,14 @@ static void btf_modifier_show(const struct btf *btf,else t =btf_type_skip_modifiers (btf, type_id,NULL ); -btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); +/* +* A modifier can resolve to void, which has no show op; print a +* placeholder rather than dereferencing NULL. +*/ +if (!btf_type_ops (t)) +btf_df_show (btf, t, type_id, data, bits_offset, show); +else +btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); } static void btf_var_show(const struct btf *btf, const struct btf_type *t,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/733 bpf: Fix NULL-ptr-deref in btf_var_show()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 5403a383f52fc0905703b488f7c3db4b2447dc58 ]
btf_var_show() calls btf_type_id_resolve() unconditionally, which dereferences btf->resolved_ids. That is NULL for a base BTF - e.g. the vmlinux BTF that bpf_snprintf_btf() renders against - since base BTF is not resolved during parsing. btf_modifier_show() guards this with 'if (btf->resolved_ids)', but btf_var_show() does not.
A BPF program that passes the type_id of a BTF_KIND_VAR from the vmlinux BTF to bpf_snprintf_btf() thus NULL-derefs:
KASAN: probably user-memory-access in range [0x46638-0x4663f] RIP: 0010:btf_var_show (kernel/bpf/btf.c:2929) Call Trace: <TASK> btf_type_show (kernel/bpf/btf.c:8259) btf_type_snprintf_show (kernel/bpf/btf.c:8329) bpf_snprintf_btf (kernel/trace/bpf_trace.c:1047) bpf_prog_test_run_raw_tp (net/bpf/test_run.c:829) __sys_bpf (kernel/bpf/syscall.c:4804) do_syscall_64 (arch/x86/entry/syscall_64.c:84) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
Resolve the var's type directly with btf_type_skip_modifiers() when resolved_ids is NULL, mirroring btf_modifier_show().
--- kernel/bpf/btf.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
kernel/bpf/btf.c +9 -1
@@ -2926,7 +2926,15 @@ static void btf_var_show(const struct btf *btf, const struct btf_type *t, u32 type_id, void *data, u8 bits_offset, struct btf_show *show) { - t =btf_type_id_resolve (btf, &type_id); + /* + * btf_type_id_resolve() dereferences btf->resolved_ids, which is NULL + * for a base BTF (e.g. the vmlinux BTF that bpf_snprintf_btf() uses). + * Resolve the var's type directly in that case. + */ + if (btf->resolved_ids) + t =btf_type_id_resolve (btf, &type_id); + else + t =btf_type_skip_modifiers (btf, t->type, &type_id);btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/733 bpf: Mark signal tracepoint siginfo arguments as scalar
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 77515ab12e4983e6416f8c35039a3f0c0822ac70 ]
The signal_generate and signal_deliver tracepoints declare their info argument as a struct kernel_siginfo pointer. btf_ctx_access() therefore treats it as a trusted pointer for tp_btf programs.
Signal delivery also uses SEND_SIG_NOINFO and SEND_SIG_PRIV as special values for this argument. Those values are zero and one respectively, and are not pointers. A tp_btf program can currently dereference either value and fault the kernel. In particular, signal_generate can run from timer interrupt context, turning the fault into a kernel panic.
Record both tracepoints in raw_tp_null_args[] and mark argument one as a non-pointer. This preserves scalar access to the cookie while rejecting direct and helper-mediated pointer use. Merely marking it nullable would not suffice because SEND_SIG_PRIV is nonzero.
--- kernel/bpf/btf.c | 3 +++ 1 file changed, 3 insertions(+)
kernel/bpf/btf.c +3 -0
@@ -6740,6 +6740,9 @@ static const struct bpf_raw_tp_null_args raw_tp_null_args[] = { { "rxrpc_resend", 0x10 }, { "rxrpc_tq", 0x10 }, { "rxrpc_client", 0x1 }, +/* signal */ + { "signal_generate", 0x20 }, + { "signal_deliver", 0x20 },/* skb */ {"kfree_skb", 0x1000}, /* sunrpc */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 230/733 bpf: Reject tail calls directly from callback frames
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 266aa4ad0b2e82397cd9045752c9bff03d98eddd ]
A tail call from a non-zero frame is modeled as a return from that frame. The verifier makes R0 unknown and calls prepare_func_exit() for the taken branch.
When the current frame is a synchronous callback, prepare_func_exit() enforces the callback return-value contract and marks R0 precise. Since the tail-call path synthesized R0 rather than deriving it from an instruction, precision backtracking reaches the callback-calling instruction with R0 still requested and triggers the "callback unexpected regs" verifier bug. A CAP_BPF task can therefore cause a WARN and an -EFAULT BPF_PROG_LOAD.
Tail calls reachable from callbacks are already rejected later by check_max_stack_depth(). Reject a tail call made directly by a callback before constructing the inconsistent return state, using the existing diagnostic. Tail calls from ordinary subprograms keep their current behavior.
--- kernel/bpf/verifier.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
kernel/bpf/verifier.c +11 -0
@@ -10672,6 +10672,17 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insnif (env->cur_state->curframe) {struct bpf_verifier_state *branch; +/* +* A taken tail call is modeled as a return from the current +* frame. A callback frame cannot be left that way because +* prepare_func_exit() would apply its return contract to the +* unknown R0 synthesized below. Stack-depth validation rejects +* this construct anyway. +*/ +if (cur_func (env)->in_callback_fn) { +verbose (env,"cannot tail call within callback\n" ); +return -EINVAL ; + }mark_reg_scratched (env,BPF_REG_0 ); branch =push_stack (env, env->insn_idx +1 , env->insn_idx, false);if (IS_ERR (branch))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 231/733 bpf: Reject resilient lock operations in rbtree callbacks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 7b7b8b5960102566bd625ae829d1f330c5b5d104 ]
__bpf_rbtree_add() keeps parent and link pointers live across calls to the program-supplied comparison callback. The verifier therefore requires the root's lock to remain held throughout the callback.
The helper path enforces this rule for bpf_spin_lock() and bpf_spin_unlock(), but the resilient lock kfunc argument path does not. Since resilient locks may protect BPF rbtree roots, a callback can release the root lock and let another CPU remove and free the node referenced by the in-progress tree walk. The walk then resumes using freed pointers.
Reject resilient lock kfuncs in an rbtree comparison callback, matching the existing policy for the spin lock helpers. Resilient-lock-protected trees remain valid when their comparison callbacks leave lock state alone.
--- kernel/bpf/verifier.c | 5 +++++ 1 file changed, 5 insertions(+)
kernel/bpf/verifier.c +5 -0
@@ -12544,6 +12544,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ {int flags =PROCESS_RES_LOCK ; +if (in_rbtree_lock_required_cb (env)) { +verbose (env,"can't res_spin_{lock,unlock} in rbtree cb\n" ); +return -EACCES ; + } +if (reg->type !=PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID |MEM_ALLOC )) {verbose (env,"%s doesn't point to map value or allocated object\n" ,reg_arg_name (env, argno));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 232/733 bpf: Mark sched_process_wait argument as nullable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit a453d6e3b8e8e1a321c8744d6189d763af9287d0 ]
do_wait() passes wo->wo_pid to the sched_process_wait tracepoint. kernel_wait4() leaves wo_pid NULL for wait4(-1), and kernel_waitid_prepare() does likewise for waitid(P_ALL).
btf_ctx_access() currently types argument 0 as PTR_TO_BTF_ID | PTR_TRUSTED. Without PTR_MAYBE_NULL, the verifier accepts an unchecked dereference. Trusted pointer loads have no fault protection, so a wait for any child can then cause a NULL pointer dereference in JITed BPF code.
Add sched_process_wait to raw_tp_null_args[] with argument 0 marked nullable. The verifier rejects an unchecked dereference while preserving access after the program checks the pointer for NULL.
--- kernel/bpf/btf.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/bpf/btf.c +4 -0
@@ -6673,6 +6673,10 @@ struct bpf_raw_tp_null_args {static const struct bpf_raw_tp_null_args raw_tp_null_args[] = {/* sched */ {"sched_pi_setprio" ,0x10 }, +/* +* do_wait() passes NULL for wait4(-1) and waitid(P_ALL). +*/ + {"sched_process_wait" ,0x1 },/* ... from sched_numa_pair_template event class */ {"sched_stick_numa" ,0x100 }, {"sched_swap_numa" ,0x100 },
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/733 bpf: Mark syscall helpers as sleepable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit d05524794240b52fdc3b6c1220dd05505715824d ]
bpf_sys_bpf() executes the bpf(2) syscall body, which can take mutexes, allocate with GFP_KERNEL, and wait for an RCU grace period. bpf_sys_close() reaches close_fd() and filp_close(), which can sleep as well.
Both helpers are limited to BPF_PROG_TYPE_SYSCALL, whose main program is sleepable. That does not make every callback sleepable: a syscall program can register a bpf_timer callback, and the verifier checks that callback in a non-sleepable context while retaining the syscall helper set.
Without .might_sleep on the prototypes, such a callback can invoke bpf_sys_bpf() from hrtimer softirq context and trigger a scheduling-while-atomic failure. bpf_sys_close() is exposed through the same missing context check.
Set .might_sleep on both prototypes so the existing helper-context check rejects them from timer callbacks and other atomic regions. Calls from the sleepable main body remain valid.
--- kernel/bpf/syscall.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/bpf/syscall.c +2 -0
@@ -6633,6 +6633,7 @@ EXPORT_SYMBOL_NS(kern_sys_bpf, "BPF_INTERNAL");static const struct bpf_func_proto bpf_sys_bpf_proto = { .func = bpf_sys_bpf, .gpl_only = false, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_ANYTHING , .arg2_type =ARG_PTR_TO_MEM |MEM_RDONLY ,
@@ -6658,6 +6659,7 @@ BPF_CALL_1(bpf_sys_close, u32, fd)static const struct bpf_func_proto bpf_sys_close_proto = { .func = bpf_sys_close, .gpl_only = false, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_ANYTHING , };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 234/733 ring-buffer: Add checking nr_subbufs to persistent ring buffer validation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
[ Upstream commit 6c001a62c34f13fe1c6a24304c289b387d9e697d ]
Sashiko reported that the code was using meta->nr_subbufs without making sure that it matched the nr_pages + 1 on data that was assuming the two were the same.
Add a check to the persistent ring buffer validation code to make sure that the saved nr_subbufs matches what we expect.
--- kernel/trace/ring_buffer.c | 5 +++++ 1 file changed, 5 insertions(+)
kernel/trace/ring_buffer.c +5 -0
@@ -1836,6 +1836,11 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,return false; } +if (meta->nr_subbufs != nr_pages +1 ) { +pr_info ("Ring buffer boot meta [%d] invalid nr_subbufs\n" , cpu); +return false; + } + buffers_start = meta->first_buffer; buffers_end = meta->first_buffer + (subbuf_size * meta->nr_subbufs);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 235/733 nvme: remove stale namespaces by NSID range during scan
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohamed Khalfella <[email protected]>
[ Upstream commit 4ed7f3d7d435bf5b63da2814dc9270f5ba896011 ]
nvme_scan_ns_list() drops the stale namespaces in each gap in the reported NSID list one NSID at a time. Every iteration calls nvme_find_get_ns() to look the namespace up and removes it if it is present. The loop runs once per NSID in the gap rather than once per namespace actually present.
NSIDs are 32-bit, so a target with a sparse NSID space can make a single gap spin the loop billions of times with nothing to remove.
watchdog: BUG: soft lockup - CPU#4 stuck for 26s! Workqueue: nvme-wq nvme_scan_work [nvme_core] RIP: 0010:__srcu_read_unlock+0xb/0x20 Call Trace: nvme_find_get_ns+0x7d/0xb0 [nvme_core] nvme_scan_ns_list+0xe8/0x280 [nvme_core] nvme_scan_work+0x18a/0x280 [nvme_core] process_one_work+0x197/0x380 worker_thread+0x2fe/0x410 kthread+0xe0/0x100
Rename nvme_remove_invalid_namespaces() to nvme_remove_nsid_range() and give it an open (start, end) NSID range. ctrl->namespaces is sorted by NSID, so the whole gap is dropped in a single walk that stops once end is reached. This bounds the work by the namespaces that are present instead of by the size of the gap.
--- drivers/nvme/host/core.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/nvme/host/core.c +9 -9
@@ -148,8 +148,6 @@ static const struct class nvme_ns_chr_class = { };static void nvme_put_subsystem (struct nvme_subsystem *subsys); -static void nvme_remove_invalid_namespaces (struct nvme_ctrl *ctrl, -unsigned nsid);static void nvme_update_keep_alive (struct nvme_ctrl *ctrl,struct nvme_command *cmd); static int nvme_get_log_lsi(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page,
@@ -4485,15 +4483,16 @@ static void nvme_scan_ns_async(void *data, async_cookie_t cookie)nvme_scan_ns (scan_info->ctrl, nsid); } -static void nvme_remove_invalid_namespaces (struct nvme_ctrl *ctrl, -unsigned nsid) +static void nvme_remove_nsid_range (struct nvme_ctrl *ctrl,u32 start,u32 end) {struct nvme_ns *ns, *next;LIST_HEAD (rm_list);mutex_lock (&ctrl->namespaces_lock);list_for_each_entry_safe (ns, next, &ctrl->namespaces, list) { -if (ns->head->ns_id > nsid) { +if (ns->head->ns_id >= end) +break ; +if (ns->head->ns_id > start) {list_del_rcu (&ns->list);synchronize_srcu (&ctrl->srcu);list_add_tail_rcu (&ns->list, &rm_list);
@@ -4543,13 +4542,14 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl) goto out;async_schedule_domain (nvme_scan_ns_async, &scan_info, &domain); -while (++prev < nsid) -nvme_ns_remove_by_nsid (ctrl, prev); +if (prev +1 < nsid) +nvme_remove_nsid_range (ctrl, prev, nsid); + prev =max (prev +1 , nsid); }async_synchronize_full_domain (&domain); } out: -nvme_remove_invalid_namespaces (ctrl, prev); +nvme_remove_nsid_range (ctrl, prev,UINT_MAX ); free:async_synchronize_full_domain (&domain);kfree (ns_list);
@@ -4569,7 +4569,7 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl)for (i =1 ; i <= nn; i++)nvme_scan_ns (ctrl, i); -nvme_remove_invalid_namespaces (ctrl, nn); +nvme_remove_nsid_range (ctrl, nn,UINT_MAX ); }static void nvme_clear_changed_ns_log (struct nvme_ctrl *ctrl)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 236/733 nvme: print namespace IDs as unsigned 32bit value
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohamed Khalfella <[email protected]>
[ Upstream commit b2d8f2a3723103abd0f8b388691ad95817d4fff4 ]
NSIDs are 32-bit unsigned values, but a number of log messages print them with %d. An NSID larger than 0x7fffffff is rendered as a negative number, which is confusing in the kernel log and makes the message hard to correlate with the namespace it talks about. Sparse NSID spaces where high NSIDs are common are the most likely to hit this.
The nsid sysfs attribute has the same problem, and there it is worse because userspace parses the value.
For example:
$ grep . /sys/class/block/nvme0*/nsid /sys/class/block/nvme0c0n1/nsid:10 /sys/class/block/nvme0c0n2/nsid:-16 /sys/class/block/nvme0c0n3/nsid:11 /sys/class/block/nvme0c0n4/nsid:-2000000016 /sys/class/block/nvme0n1/nsid:10 /sys/class/block/nvme0n2/nsid:-16 /sys/class/block/nvme0n3/nsid:11 /sys/class/block/nvme0n4/nsid:-2000000016 $
Print all of them with %u. Several messages in these files, including two in zns.c right next to the ones being changed, already use %u, so this only makes the rest consistent with them. No functional change other than how the NSID is formatted.
--- drivers/nvme/host/core.c | 18 +++++++++--------- drivers/nvme/host/sysfs.c | 2 +- drivers/nvme/host/zns.c | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-)
drivers/nvme/host/core.c +9 -9
@@ -1592,7 +1592,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, }if (nvme_multi_css (ctrl) && !csi_seen) { -dev_warn (ctrl->device,"Command set not reported for nsid:%d\n" , +dev_warn (ctrl->device,"Command set not reported for nsid:%u\n" , info->nsid); status = -EINVAL ; }
@@ -4095,13 +4095,13 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) ((ns->ctrl->subsys->cmic & NVME_CTRL_CMIC_MULTI_CTRL) && info->is_shared)) {dev_err (ctrl->device, -"ignoring nsid %d because of duplicate IDs\n" , +"ignoring nsid %u because of duplicate IDs\n" , info->nsid);return ret; }dev_err (ctrl->device, -"clearing duplicate IDs for nsid %d\n" , info->nsid); +"clearing duplicate IDs for nsid %u\n" , info->nsid);dev_err (ctrl->device,"use of /dev/disk/by-id/ may cause data corruption\n" );memset (&info->ids.nguid,0 ,sizeof (info->ids.nguid));
@@ -4116,7 +4116,7 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) ret =nvme_subsys_check_duplicate_ids (ctrl->subsys, &info->ids);if (ret) {dev_err (ctrl->device, -"duplicate IDs in subsystem for nsid %d\n" , +"duplicate IDs in subsystem for nsid %u\n" , info->nsid); goto out_unlock; }
@@ -4130,20 +4130,20 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)if ((!info->is_shared || !head->shared) && !list_empty (&head->list)) {dev_err (ctrl->device, -"Duplicate unshared namespace %d\n" , +"Duplicate unshared namespace %u\n" , info->nsid); goto out_put_ns_head; }if (!nvme_ns_ids_equal (&head->ids, &info->ids)) {dev_err (ctrl->device, -"IDs don't match for shared namespace %d\n" , +"IDs don't match for shared namespace %u\n" , info->nsid); goto out_put_ns_head; }if (!multipath) {dev_warn (ctrl->device, -"Found shared namespace %d, but multipathing not supported.\n" , +"Found shared namespace %u, but multipathing not supported.\n" , info->nsid);dev_warn_once (ctrl->device,"Shared namespace support requires core_nvme.multipath=Y.\n" );
@@ -4392,7 +4392,7 @@ static void nvme_validate_ns(struct nvme_ns *ns, struct nvme_ns_info *info)if (!nvme_ns_ids_equal (&ns->head->ids, &info->ids)) {dev_err (ns->ctrl->device, -"identifiers changed for nsid %d\n" , ns->head->ns_id); +"identifiers changed for nsid %u\n" , ns->head->ns_id); goto out; }
@@ -4419,7 +4419,7 @@ static void nvme_scan_ns(struct nvme_ctrl *ctrl, unsigned nsid)if (info.ids.csi !=NVME_CSI_NVM && !nvme_multi_css (ctrl)) {dev_warn (ctrl->device, -"command set not reported for nsid: %d\n" , nsid); +"command set not reported for nsid: %u\n" , nsid);return ; }
drivers/nvme/host/sysfs.c +1 -1
@@ -166,7 +166,7 @@ static DEVICE_ATTR_RO(eui);static ssize_t nsid_show (struct device *dev,struct device_attribute *attr,char *buf) { -return sysfs_emit (buf,"%d\n" ,dev_to_ns_head (dev)->ns_id); +return sysfs_emit (buf,"%u\n" ,dev_to_ns_head (dev)->ns_id); }static DEVICE_ATTR_RO (nsid);
drivers/nvme/host/zns.c +2 -2
@@ -48,12 +48,12 @@ int nvme_query_zone_info(struct nvme_ns *ns, unsigned lbaf, NVME_CMD_EFFECTS_CSUPP)) { if (test_and_clear_bit(NVME_NS_FORCE_RO, &ns->flags)) dev_warn(ns->ctrl->device, - "Zone Append supported for zoned namespace:%d. Remove read-only mode\n", + "Zone Append supported for zoned namespace:%u. Remove read-only mode\n", ns->head->ns_id); } else {set_bit (NVME_NS_FORCE_RO , &ns->flags);dev_warn (ns->ctrl->device, -"Zone Append not supported for zoned namespace:%d. Forcing to read-only mode\n" , +"Zone Append not supported for zoned namespace:%u. Forcing to read-only mode\n" , ns->head->ns_id); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/733 nvmet: print namespace IDs as unsigned 32bit value
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohamed Khalfella <[email protected]>
[ Upstream commit 59fe1cbc57235495a5f08dd53db176e3e3250356 ]
struct nvmet_ns.nsid is a u32, but a few messages print it with %d. An NSID larger than 0x7fffffff is rendered as a negative number, which is misleading in general and particularly so for the configfs messages that echo back the NSID the user just asked for.
For example:
[ T200] nvmet: adding nsid -16 to subsystem mysubsystem
Print them with %u. The invalid-NSID error in nvmet_ns_make() keeps its %#x because the two values it rejects, 0 and NVME_NSID_ALL, are more readable in hex format. No functional change other than how the NSID is formatted.
--- drivers/nvme/target/configfs.c | 4 ++-- drivers/nvme/target/core.c | 2 +- drivers/nvme/target/pr.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-)
drivers/nvme/target/configfs.c +2 -2
@@ -814,7 +814,7 @@ static ssize_t nvmet_ns_resv_enable_store(struct config_item *item,mutex_lock (&ns->subsys->lock);if (ns->enabled) { -pr_err ("the ns:%d is already enabled.\n" , ns->nsid); +pr_err ("the ns:%u is already enabled.\n" , ns->nsid);mutex_unlock (&ns->subsys->lock);return -EINVAL ; }
@@ -880,7 +880,7 @@ static struct config_group *nvmet_ns_make(struct config_group *group, goto out;config_group_init_type_name (&ns->group, name, &nvmet_ns_type); -pr_info ("adding nsid %d to subsystem %s\n" , nsid, subsys->subsysnqn); +pr_info ("adding nsid %u to subsystem %s\n" , nsid, subsys->subsysnqn);return &ns->group; out:
drivers/nvme/target/core.c +1 -1
@@ -558,7 +558,7 @@ static void nvmet_p2pmem_ns_add_p2p(struct nvmet_ctrl *ctrl,if (ret <0 )pci_dev_put (p2p_dev); -pr_info ("using p2pmem on %s for nsid %d\n" ,pci_name (p2p_dev), +pr_info ("using p2pmem on %s for nsid %u\n" ,pci_name (p2p_dev), ns->nsid); }
drivers/nvme/target/pr.c +1 -1
@@ -145,7 +145,7 @@ static void nvmet_pr_add_resv_log(struct nvmet_ctrl *ctrl, u8 log_type, log.nsid =cpu_to_le32 (nsid);if (!kfifo_put (&log_mgr->log_queue, log)) { -pr_info ("a reservation log lost, cntlid:%d, log_type:%d, nsid:%d\n" , +pr_info ("a reservation log lost, cntlid:%d, log_type:%d, nsid:%u\n" , ctrl->cntlid, log_type, nsid); log_mgr->lost_count++; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/733 nvme-tcp: defer TLS inline send to io_work
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
[ Upstream commit 08acb54b063a33730eb1ae1e0f89bf36542bac9f ]
blk_mq holds set->srcu while queuing and running requests. The kTLS software send path takes ctx->tx_lock. lockdep knows that tx_lock nests under elevator_lock which then waits on srcu, so an inline send from that path under TLS triggers circular locking.
Skip the inline send optimization for TLS queues so the send runs from the workqueue instead. The same workqueue already retries TLS sends on write-space notifications. Plain TCP keeps the inline path.
--- drivers/nvme/host/tcp.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/nvme/host/tcp.c +5 -0
@@ -411,8 +411,13 @@ static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,* if we're the first on the send_list and we can try to send * directly, otherwise queue io_work. Also, only do that if we * are on the same cpu, so we don't introduce contention. +* +* TLS kTLS send takes ctx->tx_lock while blk_mq holds set->srcu. +* lockdep reports circular locking via elevator_lock. Defer TLS +* sends to the io workqueue instead of inline from this path. */ if (queue->io_cpu ==raw_smp_processor_id () && + !nvme_tcp_queue_tls (queue) && empty &&mutex_trylock (&queue->send_mutex)) {nvme_tcp_send_all (queue);mutex_unlock (&queue->send_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 239/733 ASoC: Intel: avs: Clean up the bus when fetching ML caps fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit 559ea14b7ae7c7562b48759fa545b64958f35b73 ]
snd_hdac_ext_bus_get_ml_capabilities() may fail and its return code shall be checked and accounted for. Address the issue by updating the error-path for avs_pci_probe().
At the same time, if the function in question succeeds but the next part of avs_pci_probe() fails, the hlink list shall be cleaned up before leaving the scope.
--- sound/soc/intel/avs/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
sound/soc/intel/avs/core.c +9 -2
@@ -473,8 +473,13 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) }snd_hdac_bus_parse_capabilities (bus); -if (bus->mlcap) -snd_hdac_ext_bus_get_ml_capabilities (bus); +if (bus->mlcap) { + ret =snd_hdac_ext_bus_get_ml_capabilities (bus); +if (ret <0 ) { +dev_err (dev,"failed to get ml capabilities: %d\n" , ret); + goto err_ml_cap; + } + }if (dma_set_mask_and_coherent (dev,DMA_BIT_MASK (64 )))dma_set_mask_and_coherent (dev,DMA_BIT_MASK (32 ));
@@ -516,6 +521,8 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)snd_hdac_bus_free_stream_pages (bus);snd_hdac_ext_stream_free_all (bus); err_init_streams: +snd_hdac_ext_link_free_all (bus); +err_ml_cap:iounmap (adev->dsp_ba); err_remap_bar4:iounmap (bus->remap_addr);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 240/733 ASoC: Intel: avs: Clean up streams if their initialization fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit f4ba00bb56a8511bafbd19826501d27157141c6d ]
When streams are being initialized the memory allocation may fail. Have an error path and return early if that is the case.
--- sound/soc/intel/avs/core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
sound/soc/intel/avs/core.c +15 -3
@@ -91,16 +91,28 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus) {unsigned int cp_streams, pb_streams;unsigned int gcap; +int ret; gcap =snd_hdac_chip_readw (bus,GCAP ); cp_streams = (gcap >>8 ) &0x0F ; pb_streams = (gcap >>12 ) &0x0F ; bus->num_streams = cp_streams + pb_streams; -snd_hdac_ext_stream_init_all (bus,0 , cp_streams,SNDRV_PCM_STREAM_CAPTURE ); -snd_hdac_ext_stream_init_all (bus, cp_streams, pb_streams,SNDRV_PCM_STREAM_PLAYBACK ); + ret =snd_hdac_ext_stream_init_all (bus,0 , cp_streams,SNDRV_PCM_STREAM_CAPTURE ); +if (ret) +return ret; + ret =snd_hdac_ext_stream_init_all (bus, cp_streams, pb_streams,SNDRV_PCM_STREAM_PLAYBACK ); +if (ret) + goto err; + + ret =snd_hdac_bus_alloc_stream_pages (bus); +if (ret) + goto err; -return snd_hdac_bus_alloc_stream_pages (bus); +return 0 ; +err: +snd_hdac_ext_stream_free_all (bus); +return ret; }static bool avs_hdac_bus_init_chip (struct hdac_bus *bus,bool full_reset)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 241/733 ASoC: Intel: avs: Do not ignore -ENOENT when loading a topology
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit c6dceca9f78fbd478c41735457c4de9c25a6b1c8 ]
avs_load_topology() combines request_firmware() and snd_soc_tplg_component_load(). The fallback mechanism introduced for the HDAudio based boards honors -ENOENT and checks for a generic topology if no specific is found before giving up and failing the component probing.
However, if -ENOENT is returned by the latter function - snd_soc_tplg_component_load() - is shall not be ignored. That means there is an actual problem with the topology file and no fallback shall be attempted.
--- sound/soc/intel/avs/pcm.c | 34 ++++++++++++++++++++-------------- sound/soc/intel/avs/topology.c | 2 +- sound/soc/intel/avs/topology.h | 1 + 3 files changed, 22 insertions(+), 15 deletions(-)
sound/soc/intel/avs/pcm.c +20 -14
@@ -6,6 +6,7 @@// Amadeusz Slawinski <[email protected]> // +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/device.h> #include <sound/hda_register.h>
@@ -987,13 +988,25 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)return ret; } +static int avs_request_topology (struct snd_soc_component *component,const char *name, +const struct firmware **fw) +{ + char *fullname __free(kfree) = NULL; + + fullname =kasprintf (GFP_KERNEL ,"%s/%s" , component->driver->topology_name_prefix, name); +if (!fullname) +return -ENOMEM ; + +return request_firmware (fw, fullname, component->dev); +} +static int avs_component_probe (struct snd_soc_component *component) {struct snd_soc_card *card = component->card;struct snd_soc_acpi_mach *mach;struct avs_soc_component *acomp; +const struct firmware *fw;struct avs_dev *adev; -char *filename;int ret;dev_dbg (card->dev,"probing %s card %s\n" , component->name, card->name);
@@ -1009,13 +1022,7 @@ static int avs_component_probe(struct snd_soc_component *component) goto finalize;/* Load specified topology and create debugfs for it. */ - filename =kasprintf (GFP_KERNEL ,"%s/%s" , component->driver->topology_name_prefix, - mach->tplg_filename); -if (!filename) -return -ENOMEM ; - - ret =avs_load_topology (component, filename); -kfree (filename); + ret =avs_request_topology (component, mach->tplg_filename, &fw);if (ret == -ENOENT && !strncmp (mach->tplg_filename,"hda-" ,4 )) {unsigned int vendor_id;
@@ -1030,18 +1037,17 @@ static int avs_component_probe(struct snd_soc_component *component) "hda-generic-tplg.bin");if (!mach->tplg_filename)return -ENOMEM ; - filename =kasprintf (GFP_KERNEL ,"%s/%s" , component->driver->topology_name_prefix, - mach->tplg_filename); -if (!filename) -return -ENOMEM ;dev_info (card->dev,"trying to load fallback topology %s\n" , mach->tplg_filename); - ret =avs_load_topology (component, filename); -kfree (filename); + ret =avs_request_topology (component, mach->tplg_filename, &fw); }if (ret <0 )return ret; + ret =snd_soc_tplg_component_load (component, &avs_tplg_ops, fw); +if (ret) +return ret; + ret =avs_component_load_libraries (acomp);if (ret <0 ) {dev_err (card->dev,"libraries loading failed: %d\n" , ret);
sound/soc/intel/avs/topology.c +1 -1
@@ -2194,7 +2194,7 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_return 0 ; } -static const struct snd_soc_tplg_ops avs_tplg_ops = { +const struct snd_soc_tplg_ops avs_tplg_ops = { .io_ops = avs_control_ops, .io_ops_count =ARRAY_SIZE (avs_control_ops), .control_load = avs_control_load,
sound/soc/intel/avs/topology.h +1 -0
@@ -230,6 +230,7 @@ struct avs_tplg_module {struct list_head node; }; +extern const struct snd_soc_tplg_ops avs_tplg_ops;struct avs_tplg *avs_tplg_new (struct snd_soc_component *comp);int avs_load_topology (struct snd_soc_component *comp,const char *filename);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 242/733 ASoC: Intel: avs: Fix unbalanced module reference count
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit d4fa6f94b91137e329ea3f5b360e140b227bb696 ]
strace_open() invokes try_module_get() which on success takes the module reference. If any follow up operation causes strace_open() to fail, the refcount shall be put down.
--- sound/soc/intel/avs/debugfs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
sound/soc/intel/avs/debugfs.c +9 -3
@@ -8,6 +8,7 @@#include <linux/debugfs.h> #include <linux/kfifo.h> +#include <linux/module.h> #include <linux/wait.h> #include <linux/sched/signal.h> #include <linux/string_helpers.h>
@@ -235,15 +236,20 @@ static int strace_open(struct inode *inode, struct file *file)if (!try_module_get (adev->dev->driver->owner))return -ENODEV ; -if (kfifo_initialized (&adev->trace_fifo)) -return -EBUSY ; +if (kfifo_initialized (&adev->trace_fifo)) { + ret = -EBUSY ; + goto err; + } ret =kfifo_alloc (&adev->trace_fifo,PAGE_SIZE ,GFP_KERNEL );if (ret <0 ) -return ret; + goto err; file->private_data = adev;return 0 ; +err: +module_put (adev->dev->driver->owner); +return ret; }static int strace_release (struct inode *inode,struct file *file)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 243/733 ASoC: Intel: avs: Refactor and fix init_config access
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit 681e91035dc794896a904852040837190e5041f5 ]
Existing code accesses enties found in ->init_configs array through indexes that are part of ->config_ids array. Those two are limited by:
->num_init_configs and ->num_config_ids respectively. Using ID larger or equal to ->num_init_configs leads to out-of-bounds access:
avs_path_module_send_init_configs()
loop:
(...) &acomp->tplg->init_configs[ids[i]] ^ out-of-bounds candidate
Rather than adding another if-statement, refactor the code. There is no need to store the IDs, have a list of pointers to actual config-entries instead. As the verification of ->init_config entries does not differ from verification of other types that are part of the topology.c file, simply reuse the code.
--- sound/soc/intel/avs/path.c | 11 +++----- sound/soc/intel/avs/topology.c | 47 +++++++++++++++++++--------------- sound/soc/intel/avs/topology.h | 4 +-- 3 files changed, 32 insertions(+), 30 deletions(-)
sound/soc/intel/avs/path.c +3 -8
@@ -838,15 +838,10 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_modstatic int avs_path_module_send_init_configs (struct avs_dev *adev,struct avs_path_module *mod) { -struct avs_soc_component *acomp; - - acomp =to_avs_soc_component (mod->template->owner->owner->owner->owner->comp); - -u32 num_ids = mod->template->num_config_ids; -u32 *ids = mod->template->config_ids; +struct avs_tplg_module *template = mod->template; -for (int i =0 ; i < num_ids; i++) { -struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]]; +for (int i =0 ; i < template->num_init_configs; i++) { +struct avs_tplg_init_config *config = template->init_configs[i];size_t len = config->length;void *data = config->data;u32 param = config->param;
sound/soc/intel/avs/topology.c +27 -20
77 lines not on this page · raw message
sound/soc/intel/avs/topology.h +2 -2
@@ -221,8 +221,8 @@ struct avs_tplg_module {u8 domain;struct avs_tplg_modcfg_ext *cfg_ext;u32 ctl_id; -u32 num_config_ids; -u32 *config_ids; +u32 num_init_configs; +struct avs_tplg_init_config **init_configs;struct avs_tplg_nhlt_config *nhlt_config;struct avs_tplg_pipeline *owner;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 244/733 net: bcmasp: clear txcb->last before writing each descriptor
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Chen <[email protected]>
[ Upstream commit 18e5e0ec0e9282c897e2aa81a3e43ccaee03b003 ]
bcmasp_xmit() only wrote txcb->last = true for the final fragment of an SKB; non-final fragments left the field untouched. If a descriptor slot was reused while it still held a stale true from a previous SKB (possible when tx_spb_ring_full() underreported fullness), bcmasp_tx_reclaim() would see last == true mid-SKB and call dev_consume_skb_any() prematurely, freeing the sk_buff while its remaining fragments were still in flight.
Unconditionally clear txcb->last before the conditional set so every descriptor slot starts from a known false state regardless of what a prior transmission left behind.
--- drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +1 -0
@@ -301,6 +301,7 @@ static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) txcb->bytes_sent = total_bytes;dma_unmap_addr_set (txcb, dma_addr, mapping);dma_unmap_len_set (txcb, dma_len, size); + txcb->last = false;if (!i) { desc->flags |=DESC_SOF ;if (csum_hw)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 245/733 net: bcmasp: fix tx_spb_ring_full() checking same slot cnt times
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Chen <[email protected]>
[ Upstream commit 0c5cf62e72d7a666ee4da757e122dc1600df1ecc ]
The loop initialised next_index from intf->tx_spb_index on every iteration, so incr_ring() always produced the same result and only one slot was ever tested. Move the initialisation before the loop so each iteration advances next_index and the function correctly checks that cnt consecutive descriptor slots are available before allowing a new transmission.
--- drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +2 -1
@@ -148,8 +148,9 @@ static int tx_spb_ring_full(struct bcmasp_intf *intf, int cnt)int next_index, i;/* Check if we have enough room for cnt descriptors */ + next_index = intf->tx_spb_index;for (i =0 ; i < cnt; i++) { - next_index =incr_ring (intf->tx_spb_index,DESC_RING_COUNT ); + next_index =incr_ring (next_index,DESC_RING_COUNT );if (next_index == intf->tx_spb_clean_index)return 1 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 246/733 mlxsw: spectrum_ptp: Fix napi_gro_receive() call from GC workqueue context
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Taylor Bates <[email protected]>
[ Upstream commit 2ac174dfcdde399fa95ba889541fb5e688d8bb35 ]
Currently mlxsw_sp1_ptp_ht_gc_collect() is run from the PTP garbage-collection workqueue, rather than the NAPI poll context. For any unmatched PTP entries carrying an SKB, it calls mlxsw_sp1_ptp_unmatched_finish() -> mlxsw_sp1_ptp_packet_finish(). For ingress packets, this calls mlxsw_sp_rx_listener_no_mark_func(). The end of that function is the following:
skb->protocol = eth_type_trans(skb, skb->dev);
napi_gro_receive(mlxsw_skb_cb(skb)->rx_md_info.napi, skb);The napi pointer is one that was placed in the SKB control block when the trapped packet was received in the NAPI context. Later, when the GC reaps the unmatched entry (up to MLXSW_SP1_PTP_HT_GC_TIMEOUT later), the call to napi_gro_receive() mutates the NAPI instance's GRO list, which is unsafe if the poll is running concurrently on another CPU.
In mlxsw_sp1_ptp_ht_gc_collect(), local_bh_disable() is called to prevent softirq processing, but this only applies to the local CPU. Additionally, its comment is stale. It states that mlxsw_sp1_ptp_unmatched_finish() invokes netif_receive_skb(). This has not been accurate since the referenced commit; this patch makes that comment accurate again. mlxsw_pci_napi_devs_init() calls netif_threaded_enable() on the NAPI RX net_device without any conditions. The NAPI instance's poll, which may be running concurrent to the GC, is running as an independently-scheduled kthread which may be on a different CPU. The call to local_bh_disable() does not guard against this.
If a tx-timestamp timeout produces an unmatched entry (which can be easily reproduced by running ptp4l and waiting for a port to reach the UNCALIBRATED/SLAVE state) while the owning NAPI thread is in the middle of a poll on another CPU, both sides mutate the GRO list concurrently, as shown below:
[39.846] port 1 (swp1): MASTER to UNCALIBRATED on RS_SLAVE list_add corruption. next->prev should be prev (ffff8d620faf4138), but was ffff8d624150f700. (next=ffff8d620faf4138). kernel BUG at lib/list_debug.c:29! Oops: invalid opcode: 0000 [#1] SMP PTI CPU: 1 UID: 0 PID: 539 Comm: napi/mlxsw_rx-0 Not tainted 6.18.48 #1-NixOS PREEMPT(lazy) Hardware name: Mellanox Technologies Ltd. MSN2410/VMOD0001, BIOS 4.6.5 09/13/2018 RIP: 0010:__list_add_valid_or_report+0x79/0xb0 RSP: 0018:ffffcdf8c0f27c08 EFLAGS: 00010246 RAX: 0000000000000075 RBX: ffff8d624150fd00 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8d6315d1e540 RBP: ffff8d620faf4070 R08: 0000000000000000 R09: 00000000ffffdfff R10: ffffffffa5c60fe0 R11: ffffcdf8c0f27ab8 R12: 0000000000000003 R13: 000000000000003d R14: 00000000000001bc R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff8d636f63f000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000562689a60c24 CR3: 000000015f224004 CR4: 00000000001726f0 Call Trace: <TASK> gro_receive_skb+0xee/0x230 mlxsw_sp1_ptp_got_packet+0x61/0x140 [mlxsw_spectrum] mlxsw_core_skb_receive+0xdf/0x1b0 [mlxsw_core] mlxsw_pci_napi_poll_cq_rx+0x780/0x9d0 [mlxsw_pci] __napi_poll+0x31/0x1e0 napi_threaded_poll_loop+0x16b/0x1c0 napi_threaded_poll+0x71/0xa0 kthread+0xfb/0x260 ret_from_fork+0x22d/0x260 ret_from_fork_asm+0x1a/0x30 </TASK> Kernel panic - not syncing: Fatal exception in interrupt
The machinery that leads to this kernel panic has not been changed between 6.18.48 and mainline.
This patch adds an ingress-delivery helper for the PTP packet_finish() path that calls netif_receive_skb() instead of napi_gro_receive(). netif_receive_skb(), unlike napi_gro_receive(), can be called from outside of the NAPI instance's poll context, which can occur at the call site for this path. RX stats accounting and the skb->dev assignment are still preserved; the only change is the delivery call itself.
This removes GRO batching for any PTP event traffic received by the mlxsw trap, but given the relatively low volume of traffic characteristic of the protocol, and impact limited to only Spectrum-1 ASICs, this is an acceptable solution.
--- .../ethernet/mellanox/mlxsw/spectrum_ptp.c | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +33 -1
@@ -572,6 +572,38 @@ mlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp *mlxsw_sp, mlxsw_sp1_ptp_unmatched_ht_params); } +/* mlxsw_sp1_ptp_packet_finish() is reached both from the NAPI poll context +* (mlxsw_sp1_ptp_got_packet(), mlxsw_sp1_ptp_got_piece() and +* mlxsw_sp1_packet_timestamp()) and from process context, by way of the GC +* workqueue (mlxsw_sp1_ptp_ht_gc_collect() -> +* mlxsw_sp1_ptp_unmatched_finish()). +* +* mlxsw_sp_rx_listener_no_mark_func() ends in napi_gro_receive(), using the +* NAPI pointer that was placed in the SKB control block when the trapped +* packet was received in the NAPI context. That pointer may only be used +* from its own poll context, which this call site cannot guarantee. +* +* netif_receive_skb(), unlike napi_gro_receive(), can be called from outside +* of the NAPI instance's poll context. RX stats accounting and the skb->dev +* assignment are still preserved; the only change is the delivery call. +*/ +static void mlxsw_sp1_ptp_rx_finish (struct mlxsw_sp_port *mlxsw_sp_port, +struct sk_buff *skb) +{ +struct mlxsw_sp_port_pcpu_stats *pcpu_stats; + + skb->dev = mlxsw_sp_port->dev; + + pcpu_stats =this_cpu_ptr (mlxsw_sp_port->pcpu_stats); +u64_stats_update_begin (&pcpu_stats->syncp); + pcpu_stats->rx_packets++; + pcpu_stats->rx_bytes += skb->len; +u64_stats_update_end (&pcpu_stats->syncp); + + skb->protocol =eth_type_trans (skb, skb->dev); +netif_receive_skb (skb); +} +/* This function is called in the following scenarios: * * 1) When a packet is matched with its timestamp.
@@ -600,7 +632,7 @@ static void mlxsw_sp1_ptp_packet_finish(struct mlxsw_sp *mlxsw_sp,if (ingress) {if (hwtstamps) *skb_hwtstamps (skb) = *hwtstamps; -mlxsw_sp_rx_listener_no_mark_func (skb, local_port, mlxsw_sp); +mlxsw_sp1_ptp_rx_finish (mlxsw_sp_port, skb); }else {/* skb_tstamp_tx() allows hwtstamps to be NULL. */ skb_tstamp_tx (skb, hwtstamps);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 247/733 bpf: zero extend the result of an arena 32-bit cmpxchg
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 4814ed6406f3493bd554ad046da5f7fc04833571 ]
bpf_convert_ctx_accesses() rewrites an atomic on an arena pointer from BPF_STX | BPF_ATOMIC to BPF_STX | BPF_PROBE_ATOMIC, and it runs before bpf_opt_subreg_zext_lo32_rnd_hi32().
That pass emits an explicit zero extension for a 32-bit cmpxchg even when bpf_jit_needs_zext() is false. This is done because on some architectures 32-bit cmpxchg requires explicit zero extension for the dst register. E.g. on x86-64 'lock cmpxchg' does not change the %eax if comparison is successful, while BPF semantics declare that each operation on a 32-bit register zero extends it's upper half.
is_cmpxchg_insn() matches BPF_MODE == BPF_ATOMIC only, so an arena cmpxchg misses said zero extension adjustment. This patch adjusts is_cmpxchg_insn() to match BPF_PROBE_ATOMIC alongside BPF_ATOMIC.
--- kernel/bpf/fixups.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
kernel/bpf/fixups.c +6 -1
@@ -13,10 +13,15 @@ #define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args) +/* +* Matches BPF_PROBE_ATOMIC too: bpf_convert_ctx_accesses() rewrites arena +* atomics before bpf_opt_subreg_zext_lo32_rnd_hi32() runs. +*/ static bool is_cmpxchg_insn (const struct bpf_insn *insn) {return BPF_CLASS (insn->code) ==BPF_STX && -BPF_MODE (insn->code) ==BPF_ATOMIC && + (BPF_MODE (insn->code) ==BPF_ATOMIC || +BPF_MODE (insn->code) ==BPF_PROBE_ATOMIC ) && insn->imm ==BPF_CMPXCHG ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 248/733 bpf: dont rewrite bpf_fastcall patterns entered by a jump
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 0b1c83dc3c4401cd7e846548f62e3caf3d06742e ]
mark_fastcall_pattern_for_call() must ensure that matched "spill; call; fill" instruction series is not interrupted by a jump. Otherwise the rewrite applied by bpf_remove_fastcall_spills_fills() is not sound.
Record the instructions targeted by jumps in insn_aux_data[*].jump_target when the CFG is built and use this flag to stop growing a pattern at such an instruction. Jumps to the first spill are fine.
Note that existing insn_aux_data[*].jmp_point field can't be reused, as it marks subprogram return instructions.
--- include/linux/bpf_verifier.h | 12 ++++++++++++ kernel/bpf/cfg.c | 3 +++ kernel/bpf/verifier.c | 8 ++++++++ 3 files changed, 23 insertions(+)
include/linux/bpf_verifier.h +12 -0
@@ -704,6 +704,8 @@ struct bpf_insn_aux_data {*/ u32 calls_callback:1; u32 indirect_target:1; /* if it is an indirect jump target */ +/* true if some jump or call instruction targets this instruction */ + u32 jump_target:1;/* * CFG strongly connected component this instruction belongs to, * zero if it is a singleton SCC.
@@ -1115,6 +1117,16 @@ static inline void mark_jmp_point(struct bpf_verifier_env *env, int idx) env->insn_aux_data[idx].jmp_point = true; } +static inline void mark_jump_target (struct bpf_verifier_env *env,int idx) +{ + env->insn_aux_data[idx].jump_target = true; +} + +static inline bool bpf_is_jump_target (struct bpf_verifier_env *env,int insn_idx) +{ +return env->insn_aux_data[insn_idx].jump_target; +} +static inline struct bpf_func_state *cur_func (struct bpf_verifier_env *env) {struct bpf_verifier_state *cur = env->cur_state;
kernel/bpf/cfg.c +3 -0
@@ -120,6 +120,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)/* mark branch target for state pruning */ mark_prune_point (env, w);mark_jmp_point (env, w); +mark_jump_target (env, w); }if (insn_state[w] ==0 ) {
@@ -378,6 +379,7 @@ static int visit_gotox_insn(int t, struct bpf_verifier_env *env) }mark_jmp_point (env, w); +mark_jump_target (env, w);/* EXPLORED || DISCOVERED */ if (insn_state[w])
@@ -539,6 +541,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env)mark_prune_point (env, t + off +1 );mark_jmp_point (env, t + off +1 ); +mark_jump_target (env, t + off +1 );return ret;
kernel/bpf/verifier.c +8 -0
@@ -16856,6 +16856,10 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,* r0 = *(u64 *)(r10 - 8); r0 += r1; * r0 += r1; exit; * exit; +* +* Both uses of the marks assume that a pattern is entered at its first +* spill and thus executes as a unit, hence a pattern is not grown past +* an instruction targeted by a jump. */ static void mark_fastcall_pattern_for_call(struct bpf_verifier_env *env, struct bpf_subprog_info *subprog,
@@ -16894,6 +16898,10 @@ static void mark_fastcall_pattern_for_call(struct bpf_verifier_env *env,for (i =1 , off = lowest_off; i <=ARRAY_SIZE (caller_saved); ++i, off +=BPF_REG_SIZE ) {if (insn_idx - i <0 || insn_idx + i >= env->prog->len)break ; +/* stx/ldx/call must not be a jump targets, a jump to the first stx is fine */ +if (bpf_is_jump_target (env, insn_idx - i +1 ) || +bpf_is_jump_target (env, insn_idx + i)) +break ; stx = &insns[insn_idx - i]; ldx = &insns[insn_idx + i];/* must be a stack spill/fill pair */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 249/733 bpf: Track verifier instruction stats for each subprogram
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 14c950ac2be8cadb63e1bfe22111ab0fdc829eb8 ]
The verifier currently records one instruction count for the main program and each global subprogram checked independently. Static subprograms are explored within callers, so their verification cost cannot be reported separately.
Track both self and inclusive instruction counts for every subprogram. Charge each processed instruction as self work to the current subprogram and to a path-local subtotal in its function frame. When a function returns, add the callee subtotal to its inclusive count and to its parent subtotal. Fold any remaining frames when a path terminates or is pruned.
Instruction subtotals are accounting state, not semantic verifier state. Clear them when a verifier state is copied so work before a path fork is charged once, rather than again when a saved branch is explored. If copying a saved state fails before all frames are allocated, skip missing frames while folding the current path.
This generic frame accounting also records self and inclusive totals when an asynchronous callback starts as a fresh frame-zero state. It does not yet charge that independently explored callback path back to the main or global exploration root which scheduled it. That will be done in subsequent changes.
This does not change the verification statistics output format. It only prepares the counters for per-subprogram reporting.
Stable-dep-of: 369f4ce73457 ("bpf: Check ancestor frames for rbtree callbacks")
--- include/linux/bpf_verifier.h | 5 +++- kernel/bpf/verifier.c | 55 ++++++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 10 deletions(-)
include/linux/bpf_verifier.h +4 -1
@@ -385,6 +385,8 @@ struct bpf_func_state {* | number of simulations is tracked in frame N */ u32 callback_depth; +/* Instructions processed in this frame and callees on the current path. */ +u32 insns_subtotal;/* The following fields should be last. See copy_func_state() */ /* The state of the stack. Each element of the array describes BPF_REG_SIZE
@@ -805,7 +807,8 @@ struct bpf_subprog_info {u32 exit_idx;/* Index of one of the BPF_EXIT instructions in this subprogram */ u16 stack_depth;/* max. stack depth used by this function */ u16 stack_extra; -u32 insn_processed; +u32 insns_total; +u32 insns_self;/* offsets in range [stack_depth .. fastcall_stack_off) * are used for bpf_fastcall spills and fills. */
kernel/bpf/verifier.c +46 -9
127 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 250/733 bpf: Check ancestor frames for rbtree callbacks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 369f4ce734570bdfedaa4b5ca50e2a3f6a892728 ]
bpf_rbtree_add() invokes its comparator while the caller holds the root lock. The native insertion code retains raw parent and link pointers across the callback, so the verifier prohibits unlocking, consuming tree nodes, or changing RCU state from that callback.
in_rbtree_lock_required_cb() only checks the innermost verifier frame. Static subprogram calls are permitted while holding a spin lock, and such a call pushes a frame without in_callback_fn set. Consequently, all callback restrictions disappear in the nested frame. The subprogram can unlock the tree, remove and drop the node being compared, then relock. Native insertion resumes with the stale parent pointer and links freed memory into the tree.
Walk all active frames for the rbtree callback instead. Benign static subprograms remain permitted, while callback restrictions follow execution into nested frames.
--- kernel/bpf/verifier.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
kernel/bpf/verifier.c +14 -11
@@ -9756,9 +9756,10 @@ static void account_current_path(struct bpf_verifier_env *env) frame ? state->frame[frame - 1] : NULL); } -/* Are we currently verifying the callback for a rbtree helper that must -* be called with lock held? If so, no need to complain about unreleased -* lock +/* +* Are we currently verifying the callback for an rbtree kfunc that must +* be called with a lock held, or one of that callback's subprogs? If so, +* no need to complain about an unreleased lock. */ static bool in_rbtree_lock_required_cb (struct bpf_verifier_env *env) {
@@ -9766,17 +9767,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)struct bpf_insn *insn = env->prog->insnsi;struct bpf_func_state *callee;int kfunc_btf_id; +u32 frame; -if (!state->curframe) -return false; - - callee = state->frame[state->curframe]; +for (frame = state->curframe; frame; frame--) { + callee = state->frame[frame]; +if (!callee->in_callback_fn) +continue ; -if (!callee->in_callback_fn) -return false; + kfunc_btf_id = insn[callee->callsite].imm; +if (is_rbtree_lock_required_kfunc (kfunc_btf_id)) +return true; + } - kfunc_btf_id = insn[callee->callsite].imm; -return is_rbtree_lock_required_kfunc (kfunc_btf_id); +return false; }static bool retval_range_within (struct bpf_retval_range range,const struct bpf_reg_state *reg)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 251/733 bpf: Mark bpf_btf_find_by_name_kind() as sleepable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 620614bf7672130c43b3cff375525a2202f61979 ]
When bpf_btf_find_by_name_kind() finds a type in module BTF, it returns a new BTF object fd through __btf_new_fd(). This reaches anon_inode_getfd(), which can sleep while allocating or expanding the current task fd table.
The helper prototype does not set might_sleep, so the verifier allows the helper in non-sleepable contexts such as BPF timer callbacks. The fd allocation can then sleep in softirq context and install the fd into the interrupted task.
Mark the helper as sleepable. This preserves calls from the main body of a sleepable syscall program while rejecting calls from its non-sleepable regions.
--- kernel/bpf/btf.c | 1 + 1 file changed, 1 insertion(+)
kernel/bpf/btf.c +1 -0
@@ -8729,6 +8729,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, intconst struct bpf_func_proto bpf_btf_find_by_name_kind_proto = { .func = bpf_btf_find_by_name_kind, .gpl_only = false, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_PTR_TO_MEM |MEM_RDONLY , .arg2_type =ARG_CONST_SIZE ,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 252/733 bpf: Mark faultable stack helpers as sleepable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 9d02927fdf4e930893c92e35fed01a2704496900 ]
The faultable variants of bpf_get_stack() and bpf_get_task_stack() pass may_fault=true into the common stack collection code. Resolving user-space build IDs may then call build_id_parse_file() and block on filesystem reads.
Neither helper prototype sets might_sleep. Since prototype selection uses the sleepability of the whole program, the verifier can still allow these helpers from a non-sleepable region within that program, such as an explicit RCU or preemption-disabled region. The task-stack helper can also be called from a non-sleepable timer callback of a sleepable program.
Mark both faultable prototypes as sleepable. The existing helper context check then rejects these calls while continuing to allow them in genuinely sleepable contexts.
--- kernel/bpf/stackmap.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/bpf/stackmap.c +2 -0
@@ -880,6 +880,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, siconst struct bpf_func_proto bpf_get_stack_sleepable_proto = { .func = bpf_get_stack_sleepable, .gpl_only = true, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_PTR_TO_CTX , .arg2_type =ARG_PTR_TO_UNINIT_MEM ,
@@ -933,6 +934,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, bufconst struct bpf_func_proto bpf_get_task_stack_sleepable_proto = { .func = bpf_get_task_stack_sleepable, .gpl_only = false, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_PTR_TO_BTF_ID , .arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK ],
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 253/733 bpf: Reject legacy packet loads from callbacks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit e7d28823c662128caae63f14e16bd394916c139b ]
check_ld_abs() models a failed BPF_LD_ABS or BPF_LD_IND in a subprogram as an implicit return with R0 set to zero. It calls prepare_func_exit() to explore this synthesized path.
When the load is reached directly from a synchronous callback, prepare_func_exit() enforces the callback return contract and marks R0 precise. R0 is not derived from a real instruction on this path, so precision backtracking reaches the callback call with R0 still requested and triggers the "callback unexpected regs" verifier bug. A privileged program loader can therefore cause a verifier warning and an -EFAULT BPF_PROG_LOAD.
These legacy packet-load instructions are deprecated. Reject them from callbacks rather than complicating their implicit-return model. Check all active frames before constructing the implicit return so nested static subprograms cannot hide the callback context.
Global functions are verified independently with a fresh frame zero, so an active-frame check cannot identify a global function called from a callback. Also check the complete subprogram call graph during stack-depth validation and reject a function containing a legacy load when any caller is a callback. This covers global and static descendants without making has_ld_abs transitive, preserving its per-function BTF return-type check. Ordinary uses outside callbacks remain supported.
--- kernel/bpf/verifier.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
kernel/bpf/verifier.c +17 -0
@@ -5119,6 +5119,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,if (!priv_stack_supported) subprog[idx].priv_stack_mode =NO_PRIV_STACK ; process_func: +if (subprog[idx].has_ld_abs) { +for (tmp = idx; tmp >=0 ; tmp = dinfo[tmp].caller) { +if (subprog[tmp].is_cb) { +verbose (env,"cannot use BPF_LD_[ABS|IND] within callback\n" ); +return -EINVAL ; + } + } + } +/* protect against potential stack overflow that might happen when * bpf2bpf calls get combined with tailcalls. Limit the caller's stack * depth for such case down to 256 so that the worst case scenario
@@ -16420,6 +16429,7 @@ static bool may_access_skb(enum bpf_prog_type type)*/ static int check_ld_abs (struct bpf_verifier_env *env,struct bpf_insn *insn) { +struct bpf_verifier_state *state = env->cur_state;struct bpf_reg_state *regs =cur_regs (env);static const int ctx_reg =BPF_REG_6 ;u8 mode =BPF_MODE (insn->code);
@@ -16430,6 +16440,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)return -EINVAL ; } +for (i = state->curframe; i; i--) { +if (state->frame[i]->in_callback_fn) { +verbose (env,"cannot use BPF_LD_[ABS|IND] within callback\n" ); +return -EINVAL ; + } + } +if (!env->ops->gen_ld_abs) {verifier_bug (env,"gen_ld_abs is null" );return -EFAULT ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 254/733 bpf: Dont infer non-NULL from a pointer with an unbounded offset
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 67b529f521a6676cdfc78b91b0217d7eaa84216b ]
reg_not_null() decides that a register holds a non-NULL value by looking at its type alone. For pointer types that allow arithmetic the type only guarantees a non-NULL base, in case of an unbound offset the runtime offset value might still add up to NULL. Consider the followng program:
r6 = bpf_map_lookup_elem(map, &0); /* present */
if (r6 == 0) return 0;
r7 = bpf_map_lookup_elem(map, &1); /* absent, NULL at runtime */
r8 = r7;
r8 -= r6; /* pointer - pointer: unknown scalar, -r6 */
r8 <<= 1;
r8 >>= 1; /* any non-negative offset is accepted by */
/* check_reg_sane_offset_ptr() */
r6 += r8; /* verifier: map value; runtime: zero */
if (r7 != r6) return 0;
*(u8 *)(r7 + 0); /* r7 is inferred non-NULL, both are zero */At runtime both registers are zero, the comparison is true and the load faults with NULL pointer dereference.
Require the offset to be within +-BPF_MAX_VAR_OFF in reg_not_null().
--- kernel/bpf/verifier.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/bpf/verifier.c +7 -0
@@ -367,6 +367,13 @@ static bool reg_not_null(struct bpf_verifier_env *env, const struct bpf_reg_statif (type_may_be_null (type))return false; +/* +* The types below guarantee a non-NULL base, an unbounded offset can +* still wrap base + offset to zero. +*/ +if (reg_smin (reg) <= -BPF_MAX_VAR_OFF ||reg_smax (reg) >=BPF_MAX_VAR_OFF ) +return false; + type =base_type (type);return type ==PTR_TO_SOCKET || type == PTR_TO_TCP_SOCK ||
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 255/733 bpf: Dont resurrect a scalar id dropped by collect_linked_regs()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 73a98f96811e2cb0f4210b1caa8cb322f92f2a2b ]
check_cond_jmp_op() copies the compared registers into env->{false,true}_reg{1,2} before collect_linked_regs() runs and copies those snapshots back into both branch states afterwards.
collect_linked_regs() records at most LINKED_REGS_MAX members of a linked registers group in the jump history and calls clear_scalar_id() for every member that does not fit. The compared register is not exempt from that.
As a consequence, sync_linked_regs() might adjust ranges for more registers than bpf_bt_sync_linked_regs() can propagate precision to.
Collect the linked registers before the snapshots are taken instead. This might lead to some unnecessary clear_scalar_id's, but from previous testing situations with many linked registers are extremely rare.
--- kernel/bpf/verifier.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
kernel/bpf/verifier.c +10 -4
@@ -16142,6 +16142,16 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,return err; } +/* +* Collect the linked registers before env->{true,false}_reg{1,2} setup, +* otherwise ids dropped by collect_linked_regs() would be resurrected +* when env->{true,false}_reg{1,2} are copied back. +*/ +if (BPF_SRC (insn->code) ==BPF_X && src_reg->type ==SCALAR_VALUE && src_reg->id) +collect_linked_regs (env, this_branch, src_reg->id, &linked_regs); +if (dst_reg->type ==SCALAR_VALUE && dst_reg->id) +collect_linked_regs (env, this_branch, dst_reg->id, &linked_regs); + is_jmp32 =BPF_CLASS (insn->code) ==BPF_JMP32 ; env->false_reg1 = *dst_reg; env->false_reg2 = *src_reg;
@@ -16196,10 +16206,6 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,* 'this_branch' and 'other_branch' share this history * if parent state is created. */ -if (BPF_SRC (insn->code) ==BPF_X && src_reg->type ==SCALAR_VALUE && src_reg->id) -collect_linked_regs (env, this_branch, src_reg->id, &linked_regs); -if (dst_reg->type ==SCALAR_VALUE && dst_reg->id) -collect_linked_regs (env, this_branch, dst_reg->id, &linked_regs);if (linked_regs.cnt >1 ) { err =bpf_push_jmp_history (env, this_branch,0 ,0 ,0 ,linked_regs_pack (&linked_regs));if (err)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 256/733 bpf: Dont predict JMP32 pointer vs zero comparisons
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit e51179a4e09846f8fd0f26a05068520de2b301bf ]
Consider the following program:
r1 = map_value; /* low 32 bits are zero at runtime */
r6 = 0xdead000000000000;
if w1 != 0 goto l1;
l0: r1 += r6;
r2 = *(u64 *)(r1 + 0);
exit;
l1: r6 = 0;
goto l0;At the moment is_branch_taken() reports the jump as always taken, because it does not distinguish between BPF_JMP and BPF_JMP32 comparisons when processing 'if w1 != 0 ...'.
--- kernel/bpf/verifier.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/bpf/verifier.c +7 -0
@@ -15576,6 +15576,13 @@ static int is_branch_taken(struct bpf_verifier_env *env, struct bpf_reg_state *rif (__is_pointer_value (false, reg1) ||__is_pointer_value (false, reg2)) {u64 val; +/* +* The low 32 bits of a valid pointer may well be zero, hence +* nothing below applies to a 32-bit comparison. +*/ +if (is_jmp32) +return -1 ; +/* arrange that reg2 is a scalar, and reg1 is a pointer */ if (!is_reg_const (reg2, is_jmp32)) { opcode =flip_opcode (opcode);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 257/733 bpf: Mark the zero register precise for a register-form NULL check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 6aed0134d3cda6382385a734ae0158eb7df6b142 ]
check_cond_jmp_op() accepts "if rA <op> rB" as a NULL check for a nullable pointer rA when rB is a scalar known to be zero, lifts PTR_MAYBE_NULL from rA in the corresponding branch and does not mark rB precise. Consider the following program:
r0 = bpf_get_prandom_u32();
r6 = 1; /* the r6 == 0 path is explored first */
if (r0 == 0) goto 1f;
r6 = 0;
1:
r0 = bpf_map_lookup_elem(map, &0); /* absent, NULL at runtime */
if (r0 == r6) goto 2f; /* taken as a NULL check for r0 */
*(u8 *)(r0 + 0); /* verifier: map value; runtime: zero */
2:
return 0;
The r6 == 0 path is explored first and the dereference is accepted. The r6 == 1 path is pruned at the checkpoint recorded for (1), so the comparison is never verified with a non-zero r6. At runtime a failed lookup returns NULL, NULL != 1 takes the non-NULL edge and the program dereferences a pointer that is zero.
--- kernel/bpf/verifier.c | 9 +++++++++ 1 file changed, 9 insertions(+)
kernel/bpf/verifier.c +9 -0
@@ -16296,6 +16296,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, type_may_be_null(dst_reg->type) && ((BPF_SRC(insn->code) == BPF_K && insn->imm == 0) || (BPF_SRC(insn->code) == BPF_X && bpf_register_is_null(src_reg)))) { +/* +* For BPF_X the zero is a property of this execution path, +* hence src_reg has to be precise. +*/ +if (BPF_SRC (insn->code) ==BPF_X ) { + err =mark_chain_precision (env, insn->src_reg); +if (err) +return err; + }/* Mark all identical registers in each branch as either * safe or unknown depending R == 0 or R != 0 conditional. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 258/733 thermal: sysfs: switch to use scnprintf() to suppress truncation warning
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 5ad0af4f4367202b1bc71813052fe39b5116cdb9 ]
Switch the sysfs code to use scnprintf() to avoid warnings about potential truncation of the names of the sysfs attributes. We can't increase the buffer size because the size is the part of an ABI for some reason. Note, with the current size of buffer the affected attributes have a room for up to 1000 names, which ought to be enough for all cases. There is no functional change, as the same limitation was implied before.
--- drivers/thermal/thermal_sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/thermal/thermal_sysfs.c +6 -6
@@ -400,8 +400,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz)struct thermal_trip_attrs *trip_attrs = &td->trip_attrs;/* create trip type attribute */ -snprintf (trip_attrs->type.name,THERMAL_NAME_LENGTH , -"trip_point_%d_type" , i); +scnprintf (trip_attrs->type.name,sizeof (trip_attrs->type.name), +"trip_point_%d_type" , i);sysfs_attr_init (&trip_attrs->type.attr.attr); trip_attrs->type.attr.attr.name = trip_attrs->type.name;
@@ -410,8 +410,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) attrs[i] = &trip_attrs->type.attr.attr;/* create trip temp attribute */ -snprintf (trip_attrs->temp.name,THERMAL_NAME_LENGTH , -"trip_point_%d_temp" , i); +scnprintf (trip_attrs->temp.name,sizeof (trip_attrs->temp.name), +"trip_point_%d_temp" , i);sysfs_attr_init (&trip_attrs->temp.attr.attr); trip_attrs->temp.attr.attr.name = trip_attrs->temp.name;
@@ -423,8 +423,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) } attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr; -snprintf (trip_attrs->hyst.name,THERMAL_NAME_LENGTH , -"trip_point_%d_hyst" , i); +scnprintf (trip_attrs->hyst.name,sizeof (trip_attrs->hyst.name), +"trip_point_%d_hyst" , i);sysfs_attr_init (&trip_attrs->hyst.attr.attr); trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 259/733 bpf: Require MEM_PERCPU for percpu kptr stores
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 048029ba1c793f8cabc4ad5eea765da01903f8f1 ]
map_kptr_match_type() treats perm_flags as the set of register type flags that a kptr field permits. Adding MEM_PERCPU to that set for BPF_KPTR_PERCPU does not require the source register to carry it, however. The subset test consequently accepts both a plain bpf_obj_new() allocation and a referenced kernel pointer into a __percpu_kptr map field.
Loads from the field are always marked MEM_PERCPU. Consumers then treat the stored value as the cookie returned by bpf_percpu_obj_new(): per-CPU pointer helpers relocate it, and map teardown selects the per-CPU free path. A plain allocation can therefore provide an arbitrary kernel read/write, while a kernel pointer can be relocated into an invalid address or sent through a missing destructor.
Require the source MEM_PERCPU flag to match the destination field kind. This preserves valid bpf_percpu_obj_new() stores and rejects both the program-BTF and kernel-BTF variants.
--- kernel/bpf/verifier.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/bpf/verifier.c +7 -0
@@ -4320,6 +4320,13 @@ static int map_kptr_match_type(struct bpf_verifier_env *env,if (type_flag (reg->type) & ~perm_flags) goto bad_type; +/* +* A BPF_KPTR_PERCPU field is read back as MEM_PERCPU, so the value +* stored in it must carry the same flag. +*/ +if ((kptr_field->type ==BPF_KPTR_PERCPU ) != !!(reg->type &MEM_PERCPU )) + goto bad_type; +/* We need to verify reg->type and reg->btf, before accessing reg->btf */ reg_name =btf_type_name (reg->btf, reg->btf_id);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 260/733 bpf: Keep refcount_acquire nullable for borrowed RCU kptrs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ning Ding <[email protected]>
[ Upstream commit dc36739e5cc9f60485418a910b42bc95339218d2 ]
bpf_refcount_acquire() is fallible for a borrowed reference because the object may have reached a zero refcount. The verifier therefore keeps KF_RET_NULL on the return value unless the argument is an owning reference.
An RCU-protected load of a local kptr is marked MEM_ALLOC, but it only receives NON_OWN_REF when the pointee contains a graph node. A refcounted object without a graph node consequently looks like an owning reference even though the loaded register has no acquired reference state. If the program drops the last real reference while remaining in the RCU critical section, refcount_inc_not_zero() returns NULL while the verifier treats the result as non-NULL.
Only classify the argument as owning when it is backed by a verifier-tracked reference. This retains the non-NULL return for pointers from bpf_obj_new(), bpf_kptr_xchg(), or an earlier successful acquisition, while requiring a NULL check for borrowed RCU kptrs.
--- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/bpf/verifier.c +1 -1
@@ -12537,7 +12537,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ reg_arg_name(env, argno));return -EINVAL ; } -if (!type_is_non_owning_ref (reg->type)) +if (!type_is_non_owning_ref (reg->type) &®_is_referenced (env, reg)) meta->arg_owning_ref = true; rec =reg_btf_record (reg);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 261/733 bpf: Reject untrusted allocated-object pointers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ning Ding <[email protected]>
[ Upstream commit 7441ee8276641bddaf1cba7bb75ef9c1458ceb3b ]
When the final RCU read-side critical section ends, a local kptr is demoted to PTR_UNTRUSTED but retains MEM_ALLOC. The pointer may be NULL or may refer to an object whose lifetime is no longer protected.
type_is_ptr_alloc_obj() nevertheless recognizes any PTR_TO_BTF_ID with MEM_ALLOC as a live allocated object. In particular, a refcount-only local kptr never carries NON_OWN_REF, so it still passes the bpf_refcount_acquire() argument check after RCU protection ends. The kfunc can then dereference NULL or stale memory.
Make type_is_ptr_alloc_obj() reject PTR_UNTRUSTED pointers. Since type_is_non_owning_ref() is based on the same predicate, graph kfunc arguments obey the same live-object requirement. Fault-protected reads of the demoted pointer remain valid: writes are already rejected, and read fixups use bpf_may_fault_on_deref() rather than this predicate.
--- include/linux/bpf_verifier.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/bpf_verifier.h +3 -1
@@ -1322,7 +1322,9 @@ static inline bool bpf_type_has_unsafe_modifiers(u32 type)static inline bool type_is_ptr_alloc_obj (u32 type) { -return base_type (type) ==PTR_TO_BTF_ID &&type_flag (type) &MEM_ALLOC ; +return base_type (type) ==PTR_TO_BTF_ID && +type_flag (type) &MEM_ALLOC && + !(type_flag (type) &PTR_UNTRUSTED ); }static inline bool type_is_non_owning_ref (u32 type)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 262/733 tracing: Fix to avoid creating trace instances with duplicate names
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
[ Upstream commit d7dbdd2ee01e12211046d4a535623ac732b749fb ]
Since commit e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance") changed trace_array_get_by_name() to trace_array_create_systems(), enable_instances() does not reuse the same name instance. Therefore, if an administrator mistakenly specifies multiple trace_instance= options with duplicate names, all are created but only the first is accessible via tracefs.
Check whether an instance with the same name already exists before creating a new one, and reject duplicates with a warning.
--- kernel/trace/trace.c | 5 +++++ 1 file changed, 5 insertions(+)
kernel/trace/trace.c +5 -0
@@ -9708,6 +9708,11 @@ __init static void enable_instances(void)if (flag_delim) *flag_delim++ ='\0' ; +if (trace_array_find (name)) { +pr_warn ("Tracing: Instance %s already exists\n" , name); +continue ; + } +if (backup) {if (backup_instance_area (backup, &addr, &size) <0 )continue ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 263/733 selftests/bpf: Fix flaky bpf_nf test when random NAT port is 0
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 5e8c349bc8d790fe031a4332e502f5d4f9878644 ]
The bpf_nf test allocs a ct, sets snat and dnat with random addr and port via bpf_ct_set_nat_info(), then looks the ct up and checks the reply tuple against what was set.
The port comes from bpf_get_prandom_u32() and can be 0. For bpf_ct_set_nat_info(), port 0 means "port not specified", so only the addr is mapped and the kernel keeps the original port. The check then compares that port with 0 and fails, which shows up as a flaky "Test for source natting" failure in CI [1][2].
Keep the random port in 1..65535 so it is always specified.
[1] https://github.com/kernel-patches/bpf/actions/runs/33830002889/job/100893868791
[2] https://github.com/kernel-patches/bpf/actions/runs/33829976794/job/100893220999
--- tools/testing/selftests/bpf/progs/test_bpf_nf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
tools/testing/selftests/bpf/progs/test_bpf_nf.c +4 -4
@@ -190,8 +190,8 @@ nf_ct_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple *, u32, ct =alloc_fn (ctx, &bpf_tuple,sizeof (bpf_tuple.ipv4), &opts_def,sizeof (opts_def));if (ct) { -__u16 sport =bpf_get_prandom_u32 (); -__u16 dport =bpf_get_prandom_u32 (); +__u16 sport =bpf_get_prandom_u32 () %65535 +1 ; +__u16 dport =bpf_get_prandom_u32 () %65535 +1 ;union nf_inet_addr saddr = {};union nf_inet_addr daddr = {};struct nf_conn *ct_ins;
@@ -293,8 +293,8 @@ nf_ct_opts_new_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple * ct =alloc_fn (ctx, &bpf_tuple,sizeof (bpf_tuple.ipv4), &opts_def,sizeof (opts_def));if (ct) { -__u16 sport =bpf_get_prandom_u32 (); -__u16 dport =bpf_get_prandom_u32 (); +__u16 sport =bpf_get_prandom_u32 () %65535 +1 ; +__u16 dport =bpf_get_prandom_u32 () %65535 +1 ;union nf_inet_addr saddr = {};union nf_inet_addr daddr = {};struct nf_conn *ct_ins;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 264/733 bpf: Preserve special fields in recycled rhtab elements
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuan Chen <[email protected]>
[ Upstream commit 5df46ddcb7b36878c1b691e9057a0509042a2567 ]
rhtab_map_update_elem() initializes special fields after obtaining an element from bpf_mem_cache_alloc(). The allocator can return a fresh, zeroed unit, or recycle one from its RCU-pending lists before the registered destructor has run.
A BPF program can retain a map-value pointer after deleting its element and initialize and arm a timer through that pointer. If the deleted unit is recycled, check_and_init_map_value() clears the only pointer to the timer. Neither a later deletion nor rhtab_mem_dtor() can then cancel it, and the callback can run with its key and value pointing into freed memory.
Do not reinitialize special fields on insertion. Fresh allocator units are already zeroed. For recycled units, the special fields are ownership state that must remain visible to the eventual destructor. copy_map_value() already skips those fields, matching the non-preallocated hash-map path and the lifecycle established by commit 275c30bcee66 ("bpf: Don't reinit map value in prealloc_lru_pop").
--- kernel/bpf/hashtab.c | 1 - 1 file changed, 1 deletion(-)
kernel/bpf/hashtab.c +0 -1
@@ -3072,7 +3072,6 @@ static long rhtab_map_update_elem(struct bpf_map *map, void *key, void *value, umemcpy (elem->data, key, map->key_size);copy_map_value (map,rhtab_elem_value (elem, map->key_size), value); -check_and_init_map_value (map,rhtab_elem_value (elem, map->key_size));/* Prevent deadlock for NMI programs attempting to take bucket lock */ bpf_disable_instrumentation ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 265/733 bpf: Cancel special fields when recycling rhtab elements
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nuoqi Gui <[email protected]>
[ Upstream commit 65cc95eba9e8b46312cac38c227473605a4b996a ]
rhtab_map_update_existing() and rhtab_delete_elem() call bpf_obj_free_fields() when replacing or deleting a value. These map operations can run from BPF programs in NMI context, where releasing a referenced kptr or another complex field is not generally safe.
Array and hash maps avoid that problem by cancelling only the asynchronous fields which can be stopped safely in the caller context. Other ownership state remains attached to the allocation until its memory allocator destructor performs the final cleanup.
Use bpf_obj_cancel_fields() for the corresponding rhtab paths as well. This cancels timers, workqueues, and task work while allowing rhtab_mem_dtor() to release referenced kptrs when the allocation is eventually destroyed.
--- kernel/bpf/hashtab.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-)
kernel/bpf/hashtab.c +3 -14
@@ -2869,16 +2869,6 @@ static int rhtab_map_alloc_check(union bpf_attr *attr)return htab_map_alloc_check (attr); } -static void rhtab_check_and_free_fields (struct bpf_rhtab *rhtab, -struct rhtab_elem *elem) -{ -if (IS_ERR_OR_NULL (rhtab->map.record)) -return ; - -bpf_obj_free_fields (rhtab->map.record, -rhtab_elem_value (elem, rhtab->map.key_size)); -} -static void rhtab_mem_dtor (void *obj,void *ctx) {struct htab_btf_record *hrec = ctx;
@@ -2968,8 +2958,8 @@ static int rhtab_delete_elem(struct bpf_rhtab *rhtab, struct rhtab_elem *elem, vrhtab_read_elem_value (&rhtab->map, copy, elem, flags);check_and_init_map_value (&rhtab->map, copy); } -/* Release internal structs: kptr, bpf_timer, task_work, wq */ -rhtab_check_and_free_fields (rhtab, elem); +bpf_obj_cancel_fields (&rhtab->map, +rhtab_elem_value (elem, rhtab->map.key_size));bpf_mem_cache_free_rcu (&rhtab->ma, elem);return 0 ; }
@@ -3011,7 +3001,6 @@ static int rhtab_map_lookup_and_delete_elem(struct bpf_map *map, void *key, voidstatic long rhtab_map_update_existing (struct bpf_map *map,struct rhtab_elem *elem,void *value,u64 map_flags) { - struct bpf_rhtab *rhtab = container_of(map, struct bpf_rhtab, map);void *old_val =rhtab_elem_value (elem, map->key_size);if (map_flags &BPF_NOEXIST )
@@ -3031,7 +3020,7 @@ static long rhtab_map_update_existing(struct bpf_map *map, struct rhtab_elem *el* kptrs/etc. still sit in the slot. Cancel them after the copy * to match arraymap's update semantics. */ -rhtab_check_and_free_fields (rhtab, elem); +bpf_obj_cancel_fields (map, old_val);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 266/733 bpf: Mark NULL kptr stores precise
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit ecdc5043794c9184aa8e6c814603899479c46b35 ]
check_map_kptr_access() permits a scalar store into an untrusted kptr field only when the register is known to contain zero. Unlike other verifier checks whose outcome depends on a scalar value, it does not mark that register precise.
A state checkpoint reached with an imprecise zero can therefore prune a second path that reaches the store with an arbitrary nonzero scalar. The program can write attacker-controlled bits into the kptr field and load them back as a PTR_TO_BTF_ID.
Call mark_chain_precision() before accepting a known-zero register. This forces state equivalence to compare its scalar range and makes the verifier visit and reject a path carrying a nonzero value.
--- kernel/bpf/verifier.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
kernel/bpf/verifier.c +9 -2
@@ -4528,8 +4528,15 @@ static int check_map_kptr_access(struct bpf_verifier_env *env,return ret; }else if (class ==BPF_STX ) { val_reg =reg_state (env, value_regno); -if (!bpf_register_is_null (val_reg) && -map_kptr_match_type (env, kptr_field, val_reg, value_regno)) +if (bpf_register_is_null (val_reg)) { +/* +* This store is valid only because the scalar is known to be +* zero. Mark it precise so another scalar cannot be pruned +* against this state. +*/ +return mark_chain_precision (env, value_regno); + } +if (map_kptr_match_type (env, kptr_field, val_reg, value_regno))return -EACCES ; }else if (class ==BPF_ST ) {if (insn->imm) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 267/733 bpf: Preserve inner map identity in callback frames
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit b90c5d770dad910fb89e6c1b15052a8a1e8db752 ]
Callback frame constructors initialize map-typed argument registers with __mark_reg_known_zero() and then restore map_ptr. This clears map_uid, which is the only field distinguishing inner maps that share an inner_map_meta template.
When a timer callback invokes bpf_for_each_map_elem() on a second inner map, both the saved first map and the second map value can reach the nested callback as the same template with map_uid zero. bpf_timer_init() then accepts pairing the timer from the second map with the first map.
The runtime records the first map in the timer without taking a reference. Freeing that map does not find the timer stored in the second map, so a later timer callback dereferences the freed map.
Copy map_uid from the same caller register as map_ptr when constructing for-each, timer/workqueue, and task-work callback arguments. The existing identity check can then reject mismatched inner maps while allowing a callback value to be paired with its actual map.
--- kernel/bpf/verifier.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/bpf/verifier.c +10 -0
@@ -9529,10 +9529,12 @@ int map_set_for_each_callback_args(struct bpf_verifier_env *env, callee->regs[BPF_REG_2 ].type =PTR_TO_MAP_KEY ;__mark_reg_known_zero (&callee->regs[BPF_REG_2 ]); callee->regs[BPF_REG_2 ].map_ptr = caller->regs[BPF_REG_1 ].map_ptr; + callee->regs[BPF_REG_2 ].map_uid = caller->regs[BPF_REG_1 ].map_uid; callee->regs[BPF_REG_3 ].type =PTR_TO_MAP_VALUE ;__mark_reg_known_zero (&callee->regs[BPF_REG_3 ]); callee->regs[BPF_REG_3 ].map_ptr = caller->regs[BPF_REG_1 ].map_ptr; + callee->regs[BPF_REG_3 ].map_uid = caller->regs[BPF_REG_1 ].map_uid;/* pointer to stack or null */ callee->regs[BPF_REG_4 ] = caller->regs[BPF_REG_3 ];
@@ -9610,6 +9612,7 @@ static int set_timer_callback_state(struct bpf_verifier_env *env, int insn_idx) {struct bpf_map *map_ptr = caller->regs[BPF_REG_1 ].map_ptr; +u32 map_uid = caller->regs[BPF_REG_1 ].map_uid;/* bpf_timer_set_callback(struct bpf_timer *timer, void *callback_fn); * callback_fn(struct bpf_map *map, void *key, void *value);
@@ -9617,14 +9620,17 @@ static int set_timer_callback_state(struct bpf_verifier_env *env, callee->regs[BPF_REG_1 ].type =CONST_PTR_TO_MAP ;__mark_reg_known_zero (&callee->regs[BPF_REG_1 ]); callee->regs[BPF_REG_1 ].map_ptr = map_ptr; + callee->regs[BPF_REG_1 ].map_uid = map_uid; callee->regs[BPF_REG_2 ].type =PTR_TO_MAP_KEY ;__mark_reg_known_zero (&callee->regs[BPF_REG_2 ]); callee->regs[BPF_REG_2 ].map_ptr = map_ptr; + callee->regs[BPF_REG_2 ].map_uid = map_uid; callee->regs[BPF_REG_3 ].type =PTR_TO_MAP_VALUE ;__mark_reg_known_zero (&callee->regs[BPF_REG_3 ]); callee->regs[BPF_REG_3 ].map_ptr = map_ptr; + callee->regs[BPF_REG_3 ].map_uid = map_uid;/* unused */ bpf_mark_reg_not_init (env, &callee->regs[BPF_REG_4 ]);
@@ -9724,6 +9730,7 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env, int insn_idx) {struct bpf_map *map_ptr = caller->regs[BPF_REG_3 ].map_ptr; +u32 map_uid = caller->regs[BPF_REG_3 ].map_uid;/* * callback_fn(struct bpf_map *map, void *key, void *value);
@@ -9731,14 +9738,17 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env, callee->regs[BPF_REG_1 ].type =CONST_PTR_TO_MAP ;__mark_reg_known_zero (&callee->regs[BPF_REG_1 ]); callee->regs[BPF_REG_1 ].map_ptr = map_ptr; + callee->regs[BPF_REG_1 ].map_uid = map_uid; callee->regs[BPF_REG_2 ].type =PTR_TO_MAP_KEY ;__mark_reg_known_zero (&callee->regs[BPF_REG_2 ]); callee->regs[BPF_REG_2 ].map_ptr = map_ptr; + callee->regs[BPF_REG_2 ].map_uid = map_uid; callee->regs[BPF_REG_3 ].type =PTR_TO_MAP_VALUE ;__mark_reg_known_zero (&callee->regs[BPF_REG_3 ]); callee->regs[BPF_REG_3 ].map_ptr = map_ptr; + callee->regs[BPF_REG_3 ].map_uid = map_uid;/* unused */ bpf_mark_reg_not_init (env, &callee->regs[BPF_REG_4 ]);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 268/733 ring-buffer: Remove ring_buffer_per_cpu::mapped
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit 8b502bf6eb3da15f4b954ad3632335ff10ed746a ]
ring_buffer_per_cpu::mapped tracks if a ring-buffer is either mapped by user-space or if it is a persistent buffer. We already have user_mapped for the former and ring_meta for the latter. Get rid of mapped and instead create rb_is_static(). A static ring-buffer cannot be resized, swapped or have its pages extracted.
Stable-dep-of: dae8dda341d2 ("tracing: Fix subbuf resize races with trace_pipe_raw readers")
--- kernel/trace/ring_buffer.c | 45 ++++++++++++++------------------------ 1 file changed, 17 insertions(+), 28 deletions(-)
kernel/trace/ring_buffer.c +17 -28
@@ -514,7 +514,7 @@ struct ring_buffer_per_cpu {int cpu;atomic_t record_disabled;atomic_t resize_disabled; -struct trace_buffer *buffer; +struct trace_buffer *buffer;raw_spinlock_t reader_lock;/* serialize readers */ arch_spinlock_t lock;struct lock_class_key lock_key;
@@ -552,7 +552,6 @@ struct ring_buffer_per_cpu {/* pages removed since last reset */ unsigned long pages_removed; -unsigned int mapped;unsigned int user_mapped;/* user space mapping */ struct mutex mapping_lock;struct buffer_page **subbuf_ids;/* ID to subbuf VA */
@@ -632,6 +631,11 @@ unsigned long rb_subbuf_start(struct trace_buffer *buffer, unsigned long addr)return addr & ~((unsigned long )(rb_subbuf_size (buffer) -1 )); } +static bool rb_is_static (struct ring_buffer_per_cpu *cpu_buffer) +{ +return cpu_buffer->user_mapped || cpu_buffer->remote || cpu_buffer->ring_meta; +} +struct ring_buffer_iter {struct ring_buffer_per_cpu *cpu_buffer;unsigned long head;
@@ -2567,7 +2571,6 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)* Range mapped buffers have the same restrictions as memory * mapped ones do. */ - cpu_buffer->mapped =1 ; cpu_buffer->ring_meta =rb_range_meta (buffer, nr_pages, cpu); bpage->page =rb_range_buffer (cpu_buffer,0 );if (!bpage->page)
@@ -6661,12 +6664,11 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer)rb_head_page_activate (cpu_buffer); cpu_buffer->pages_removed =0 ; -if (cpu_buffer->mapped) { -rb_update_meta_page (cpu_buffer); -if (cpu_buffer->ring_meta) { -struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta; - meta->commit_buffer = meta->head_buffer; - } +rb_update_meta_page (cpu_buffer); +if (cpu_buffer->ring_meta) { +struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta; + + meta->commit_buffer = meta->head_buffer; } }
@@ -6915,8 +6917,8 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a, cpu_buffer_a = buffer_a->buffers[cpu]; cpu_buffer_b = buffer_b->buffers[cpu]; -/* It's up to the callers to not try to swap mapped buffers */ -if (WARN_ON_ONCE (cpu_buffer_a->mapped || cpu_buffer_b->mapped)) +/* It's up to the callers to not try to swap static buffers */ +if (WARN_ON_ONCE (rb_is_static (cpu_buffer_a) ||rb_is_static (cpu_buffer_b)))return -EBUSY ;/* At least make sure the two buffers are somewhat the same */
@@ -7128,7 +7130,6 @@ int ring_buffer_read_page(struct trace_buffer *buffer,unsigned int size;unsigned int read;u64 save_timestamp; -bool force_memcpy;if (!cpumask_test_cpu (cpu, buffer->cpumask))return -1 ;
@@ -7167,8 +7168,6 @@ int ring_buffer_read_page(struct trace_buffer *buffer,/* Check if any events were dropped */ missed_events = cpu_buffer->lost_events; - force_memcpy = cpu_buffer->mapped || cpu_buffer->remote; -/* * If this page has been partially read or * if len is not big enough to read the rest of the page or
@@ -7178,7 +7177,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,*/ if (read || (len < (size - read)) || cpu_buffer->reader_page == cpu_buffer->commit_page || - force_memcpy) { +rb_is_static (cpu_buffer)) {struct buffer_data_page *rpage = cpu_buffer->reader_page->page;unsigned int rpos = read;unsigned int pos =0 ;
@@ -7620,11 +7619,7 @@ static int __rb_inc_dec_mapped(struct ring_buffer_per_cpu *cpu_buffer,lockdep_assert_held (&cpu_buffer->mapping_lock); -/* mapped is always greater or equal to user_mapped */ -if (WARN_ON (cpu_buffer->mapped < cpu_buffer->user_mapped)) -return -EINVAL ; - -if (inc && cpu_buffer->mapped ==UINT_MAX ) +if (inc && cpu_buffer->user_mapped ==UINT_MAX )return -EBUSY ;if (WARN_ON (!inc && cpu_buffer->user_mapped ==0 ))
@@ -7633,13 +7628,10 @@ static int __rb_inc_dec_mapped(struct ring_buffer_per_cpu *cpu_buffer,mutex_lock (&cpu_buffer->buffer->mutex);raw_spin_lock_irqsave (&cpu_buffer->reader_lock, flags); -if (inc) { +if (inc) cpu_buffer->user_mapped++; - cpu_buffer->mapped++; - }else { +else cpu_buffer->user_mapped--; - cpu_buffer->mapped--; - }raw_spin_unlock_irqrestore (&cpu_buffer->reader_lock, flags);mutex_unlock (&cpu_buffer->buffer->mutex);
@@ -7811,7 +7803,6 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu,if (!err) {raw_spin_lock_irqsave (&cpu_buffer->reader_lock, flags);/* This is the first time it is mapped by user */ - cpu_buffer->mapped++; cpu_buffer->user_mapped =1 ;raw_spin_unlock_irqrestore (&cpu_buffer->reader_lock, flags); }else {
@@ -7868,8 +7859,6 @@ int ring_buffer_unmap(struct trace_buffer *buffer, int cpu)raw_spin_lock_irqsave (&cpu_buffer->reader_lock, flags);/* This is the last user space mapping */ -if (!WARN_ON_ONCE (cpu_buffer->mapped < cpu_buffer->user_mapped)) - cpu_buffer->mapped--; cpu_buffer->user_mapped =0 ;raw_spin_unlock_irqrestore (&cpu_buffer->reader_lock, flags);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 269/733 tracing: Fix subbuf resize races with trace_pipe_raw readers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit dae8dda341d2d9034a90d59e8a7d502e1263813f ]
Concurrent subbuffer resizes may crash trace_pipe_raw readers or leak uninitialized memory to userspace due to stale size values.
Modify ring_buffer_alloc_read_page() to handle the resizing of an existing buffer_data_read_page if necessary and add a new ring_buffer_read_page_size(). This new function enables ring-buffer buffer_data_read_page users to not call the racy ring_buffer_subbuf_size_get(). This makes the spare_size member of ftrace_buffer_info redundant.
Finally, handle buffer_data_read_page/reader_page order discrepancy in ring_buffer_read_page(). On a mismatch simply copy manually the data to the buffer_data_read_page.
--- include/linux/ring_buffer.h | 5 +- kernel/trace/ring_buffer.c | 135 ++++++++++++++++++--------- kernel/trace/ring_buffer_benchmark.c | 6 +- kernel/trace/trace.c | 97 +++++++++---------- kernel/trace/trace.h | 9 +- 5 files changed, 144 insertions(+), 108 deletions(-)
include/linux/ring_buffer.h +3 -2
@@ -218,14 +218,15 @@ bool ring_buffer_time_stamp_abs(struct trace_buffer *buffer);size_t ring_buffer_nr_dirty_pages (struct trace_buffer *buffer,int cpu);struct buffer_data_read_page ; -struct buffer_data_read_page * -ring_buffer_alloc_read_page (struct trace_buffer *buffer,int cpu); +int ring_buffer_alloc_read_page (struct trace_buffer *buffer,int cpu, +struct buffer_data_read_page **rpage);void ring_buffer_free_read_page (struct trace_buffer *buffer,int cpu,struct buffer_data_read_page *page);int ring_buffer_read_page (struct trace_buffer *buffer,struct buffer_data_read_page *data_page,size_t len,int cpu,int full);void *ring_buffer_read_page_data (struct buffer_data_read_page *page); +unsigned int ring_buffer_read_page_size (struct buffer_data_read_page *rpage);struct trace_seq ;
kernel/trace/ring_buffer.c +92 -43
258 lines not on this page · raw message
kernel/trace/ring_buffer_benchmark.c +3 -3
@@ -104,7 +104,7 @@ static enum event_status read_event(int cpu)static enum event_status read_page (int cpu) { -struct buffer_data_read_page *bpage; +struct buffer_data_read_page *bpage =NULL ;struct ring_buffer_event *event;struct rb_page *rpage;unsigned long commit;
@@ -114,8 +114,8 @@ static enum event_status read_page(int cpu)int inc;int i; - bpage =ring_buffer_alloc_read_page (buffer, cpu); -if (IS_ERR (bpage)) + ret =ring_buffer_alloc_read_page (buffer, cpu, &bpage); +if (ret <0 )return EVENT_DROPPED ; page_size =ring_buffer_subbuf_size_get (buffer);
kernel/trace/trace.c +42 -55
170 lines not on this page · raw message
kernel/trace/trace.h +4 -5
@@ -745,11 +745,10 @@ static inline int tracing_get_cpu(struct inode *inode)void tracing_reset_cpu (struct array_buffer *buf,int cpu);struct ftrace_buffer_info { -struct trace_iterator iter; -void *spare; -unsigned int spare_cpu; -unsigned int spare_size; -unsigned int read; +struct trace_iterator iter; +struct buffer_data_read_page *spare; +unsigned int spare_cpu; +unsigned int read; };/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 270/733 ring-buffer: Cap static ring buffer nr_pages
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit f2b2b645595c82b4e824880f6cb987e077a8da19 ]
Static ring buffers (i.e. persistent, user-mapped and remote) rely on the bpage::id field. The number of pages for those ring buffers must fit into that variable. Enforce this limit on ring buffer creation or user-mapping.
While at it, prevent nr_pages underflow when allocating a persistent buffer.
--- kernel/trace/ring_buffer.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
kernel/trace/ring_buffer.c +22 -0
@@ -641,6 +641,15 @@ static bool rb_is_static(struct ring_buffer_per_cpu *cpu_buffer)return cpu_buffer->user_mapped || cpu_buffer->remote || cpu_buffer->ring_meta; } +static unsigned long rb_static_max_pages (void ) +{ +/* +* Static ring buffers are using bpage::id and must account for the +* reader page. +*/ +return (1UL <<30 ) -1 ; +} +struct ring_buffer_iter {struct ring_buffer_per_cpu *cpu_buffer;unsigned long head;
@@ -2825,6 +2834,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, size = end - buffers_start; size = size / nr_cpu_ids; +if (size <sizeof (struct ring_buffer_cpu_meta )) + goto fail_free_buffers;/* * The number of sub-buffers (nr_pages) is determined by the * total size allocated minus the meta data size.
@@ -2834,6 +2845,10 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,*/ nr_pages = (size -sizeof (struct ring_buffer_cpu_meta )) / (subbuf_size +sizeof (int )); + +if (nr_pages >rb_static_max_pages ()) + goto fail_free_buffers; +/* Need at least two pages plus the reader page */ if (nr_pages <3 ) goto fail_free_buffers;
@@ -2866,6 +2881,10 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,/* The writer is remote. This ring-buffer is read-only */ atomic_inc (&buffer->record_disabled); nr_pages = desc->nr_page_va -1 ; + +if (nr_pages >rb_static_max_pages ()) + goto fail_free_buffers; +if (nr_pages <2 ) goto fail_free_buffers; }else {
@@ -7826,6 +7845,9 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu,/* prevent another thread from changing buffer/sub-buffer sizes */ guard (mutex)(&buffer->mutex); +if (cpu_buffer->nr_pages >rb_static_max_pages ()) +return -E2BIG ; + err =rb_alloc_meta_page (cpu_buffer);if (err)return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 271/733 tracing: Fix comment in tracing_buffers_splice_read()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
[ Upstream commit 5cbea500775dd1944995f23320af030b9b24b24b ]
The comment about returning an error if the read fails on the first iteration is slightly incorrect. It makes it sound like the only reason it could fail on a later iteration is if the subbuf order changed. That is incorrect, it could also fail if the length passed in was not a multiple of the subbuf size. Fix the comment.
--- kernel/trace/trace.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
kernel/trace/trace.c +7 -5
@@ -7294,11 +7294,13 @@ ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos, r =ring_buffer_read_page (ref->buffer, ref->rpage, len, iter->cpu_file,1 ); }else if (!i) {/* -* We failed to read because the length is too small -* or unaligned. If this is the first iteration, it's -* an invalid userspace input. Otherwise, this is due -* to a subbuf order change. Do not report an error -* and just finish the read. +* If this fails to read on the first iteration, it +* means the length was too small and an error should +* be returned to user space. Otherwise, at least +* one sub-buffer was successfully read but this failed +* due to either the length was unaligned or the +* subbuf order changed. Either case, do not report +* an error. */ ret = -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 272/733 nexthop: Initialize extack in remove_nh_grp_entry()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ido Schimmel <[email protected]>
[ Upstream commit 5bd9e4e7cdaa03879e9b73b12ab52cceb1edd55b ]
remove_nh_grp_entry() prints the extack message when a listener fails to replace the reduced nexthop group. However, extack is not initialized and listeners are not required to set a message when returning an error. Neither netdevsim nor mlxsw do so when an allocation fails, resulting in the dereference of an uninitialized stack pointer.
Fix by zero-initializing extack, as was done in commit 6347c5314cee ("nexthop: initialize extack in nh_res_bucket_migrate()").
--- net/ipv4/nexthop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/nexthop.c +1 -1
@@ -2029,7 +2029,7 @@ remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, {struct nh_grp_entry *nhges, *new_nhges;struct nexthop *nhp = nhge->nh_parent; -struct netlink_ext_ack extack; +struct netlink_ext_ack extack = {};struct nexthop *nh = nhge->nh;struct nh_group *nhg, *newg;int i, j, err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 273/733 bonding: use skb_cow_head() in bond_do_alb_xmit() and rlb_arp_xmit()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 1746ef2e2df2ad71c66eca56364d56bde284523b ]
In bond_do_alb_xmit() and rlb_arp_xmit(), make sure to unclone skb head via skb_cow_head() before modifying the source MAC address (Ethernet header and ARP payload) to avoid silent corruption if the skb is shared or cloned. Avoid caching the header pointers across skb_cow_head().
In rlb_arp_xmit(), only modify arp->mac_src if it differs from tx_slave->dev->dev_addr to avoid an unnecessary copy and head reallocation.
Also, we should not assume mac header is set in output path.
Use skb_eth_hdr() instead of eth_hdr() to fix the issue, and remove now redundant skb_reset_mac_header() calls.
--- drivers/net/bonding/bond_alb.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)
drivers/net/bonding/bond_alb.c +12 -7
@@ -678,9 +678,15 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)if (arp->op_code ==htons (ARPOP_REPLY )) {/* the arp must be sent on the selected rx channel */ tx_slave =rlb_choose_channel (skb, bond, arp); -if (tx_slave) +if (tx_slave && + !ether_addr_equal_64bits (arp->mac_src, + tx_slave->dev->dev_addr)) { +if (unlikely (skb_cow_head (skb,0 ))) +return NULL ; + arp = (struct arp_pkt *)skb_network_header (skb);bond_hw_addr_copy (arp->mac_src, tx_slave->dev->dev_addr, tx_slave->dev->addr_len); + }netdev_dbg (bond->dev,"(slave %s): Server sent ARP Reply packet\n" , tx_slave ? tx_slave->dev->name :"NULL" ); }else if (arp->op_code ==htons (ARPOP_REQUEST )) {
@@ -1340,7 +1346,6 @@ static netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond, struct slave *tx_slave) {struct alb_bond_info *bond_info = &(BOND_ALB_INFO (bond)); -struct ethhdr *eth_data =eth_hdr (skb);if (!tx_slave) {/* unbalanced or unassigned, send through primary */
@@ -1351,7 +1356,9 @@ static netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,if (tx_slave &&bond_slave_can_tx (tx_slave)) {if (tx_slave !=rcu_access_pointer (bond->curr_active_slave)) { -ether_addr_copy (eth_data->h_source, +if (unlikely (skb_cow_head (skb,0 ))) +return bond_tx_drop (bond->dev, skb); +ether_addr_copy (skb_eth_hdr (skb)->h_source, tx_slave->dev->dev_addr); }
@@ -1375,8 +1382,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,struct ethhdr *eth_data;u32 hash_index; -skb_reset_mac_header (skb); - eth_data =eth_hdr (skb); + eth_data =skb_eth_hdr (skb);/* Do not TX balance any multicast or broadcast */ if (!is_multicast_ether_addr (eth_data->h_dest)) {
@@ -1428,8 +1434,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,u32 hash_index =0 ;int hash_size =0 ; -skb_reset_mac_header (skb); - eth_data =eth_hdr (skb); + eth_data =skb_eth_hdr (skb);switch (ntohs (skb->protocol)) {case ETH_P_IP : {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 274/733 net: dsa: bcm_sf2: bound the CFP rule dump by the callers buffer size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit cdb719f4b8596d9ccee2d56d204c2c4dce982f46 ]
bcm_sf2_cfp_rule_get_all() walks the whole cfp.unique bitmap into rule_locs[] without consulting nfc->rule_cnt, which is how many entries the caller had room for. ETHTOOL_GRXCLSRLALL requires no CAP_NET_ADMIN and the ioctl sizes the buffer from the rule_cnt userspace passes in, so once an admin has installed CFP rules any user can ask for fewer slots than there are rules and run off the end of the allocation. A rule_cnt of 0 leaves the buffer pointer NULL and the walk dereferences it.
--- drivers/net/dsa/bcm_sf2_cfp.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/dsa/bcm_sf2_cfp.c +2 -0
@@ -1088,6 +1088,8 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv,unsigned int index =1 , rules_cnt =0 ;for_each_set_bit_from (index, priv->cfp.unique, priv->num_cfp_rules) { +if (rules_cnt == nfc->rule_cnt) +return -EMSGSIZE ; rule_locs[rules_cnt] = index; rules_cnt++; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 275/733 eth: nfp: bound the ntuple rule dump by the callers buffer size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit f1986bf87b0709c95126fe196cf39e5b8c8453a1 ]
nfp_net_get_fs_loc() dumps every entry of nn->fs.list into rule_locs[] without consulting cmd->rule_cnt, which is how many entries the caller had room for. ETHTOOL_GRXCLSRLALL requires no CAP_NET_ADMIN and the ioctl sizes the buffer from the rule_cnt userspace passes in, so once an admin has installed flow steering rules any user can ask for fewer slots than there are rules and run off the end of the allocation. A rule_cnt of 0 leaves the buffer pointer NULL and the walk dereferences it.
Bail out with -EMSGSIZE when the buffer fills up, the way the other ntuple capable drivers do, and report how many locations were filled so a shrinking rule list does not leave the caller reading stale slots.
--- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +8 -3
@@ -1421,7 +1421,8 @@ static int nfp_net_get_fs_rule(struct nfp_net *nn, struct ethtool_rxnfc *cmd)return -ENOENT ; } -static int nfp_net_get_fs_loc (struct nfp_net *nn,u32 *rule_locs) +static int nfp_net_get_fs_loc (struct nfp_net *nn,struct ethtool_rxnfc *cmd, +u32 *rule_locs) {struct nfp_fs_entry *entry;u32 count =0 ;
@@ -1429,8 +1430,12 @@ static int nfp_net_get_fs_loc(struct nfp_net *nn, u32 *rule_locs)if (!(nn->cap_w1 &NFP_NET_CFG_CTRL_FLOW_STEER ))return -EOPNOTSUPP ; -list_for_each_entry (entry, &nn->fs.list, node) +list_for_each_entry (entry, &nn->fs.list, node) { +if (count == cmd->rule_cnt) +return -EMSGSIZE ; rule_locs[count++] = entry->loc; + } + cmd->rule_cnt = count;return 0 ; }
@@ -1455,7 +1460,7 @@ static int nfp_net_get_rxnfc(struct net_device *netdev,return nfp_net_get_fs_rule (nn, cmd);case ETHTOOL_GRXCLSRLALL : cmd->data =NFP_FS_MAX_ENTRY ; -return nfp_net_get_fs_loc (nn, rule_locs); +return nfp_net_get_fs_loc (nn, cmd, rule_locs);default :return -EOPNOTSUPP ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 276/733 eth: nfp: drop the replaced rule from the list when reprogramming fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 108bb2142e3a12c9ad625ad662973127a113ddc6 ]
nfp_net_fs_add() replaces an existing rule by deleting it from the hardware, decrementing nn->fs.count and programming the new one. If nfp_net_fs_add_hw() fails the old entry stays on nn->fs.list - only the success path reaches list_replace() - so the list is one longer than nn->fs.count, and it advertises a rule whose hardware entry has already been torn down.
nn->fs.count is what ETHTOOL_GRXCLSRLCNT reports, so userspace then sizes its buffer one entry short of what the GRXCLSRLALL walk wants to write. That used to overwrite one u32 past the allocation; since the walk is bounded it is a permanent -EMSGSIZE instead, as nothing ever resyncs the counter.
--- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +7 -1
@@ -1703,8 +1703,14 @@ static int nfp_net_fs_add(struct nfp_net *nn, struct ethtool_rxnfc *cmd) nn->fs.count--; err =nfp_net_fs_add_hw (nn, new); -if (err) +if (err) { +/* mbox broken, adding the old rule back will +* likely also fail. +*/ +list_del (&entry->node); +kfree (entry); goto err; + } nn->fs.count++;list_replace (&entry->node, &new->node);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 277/733 net: dsa: mv88e6xxx: bound the policy rule dump by the callers buffer size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit b1fffc273112e7284c5b705e186b43b5770cd3d5 ]
mv88e6xxx_get_rxnfc() uses rxnfc->rule_cnt as the write index while dumping the policy IDR, clobbering the input value before it has been looked at. That input is the number of entries the caller had room for. ETHTOOL_GRXCLSRLALL requires no CAP_NET_ADMIN and the ioctl sizes the buffer from the rule_cnt userspace passes in, so once an admin has installed policy rules any user can ask for fewer slots than there are rules and run off the end of the allocation. A rule_cnt of 0 leaves the buffer pointer NULL and the walk dereferences it.
Count into a local so the caller's limit survives the walk, and stop with -EMSGSIZE once it is reached.
--- drivers/net/dsa/mv88e6xxx/chip.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
drivers/net/dsa/mv88e6xxx/chip.c +12 -4
@@ -2438,6 +2438,7 @@ static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port,struct ethtool_rx_flow_spec *fs = &rxnfc->fs;struct mv88e6xxx_chip *chip = ds->priv;struct mv88e6xxx_policy *policy; +u32 cnt =0 ;int err;int id;
@@ -2463,11 +2464,18 @@ static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port,break ;case ETHTOOL_GRXCLSRLALL : rxnfc->data =0 ; - rxnfc->rule_cnt =0 ; -idr_for_each_entry (&chip->policies, policy, id) -if (policy->port == port) - rule_locs[rxnfc->rule_cnt++] = id; err =0 ; +idr_for_each_entry (&chip->policies, policy, id) { +if (policy->port != port) +continue ; +if (cnt == rxnfc->rule_cnt) { + err = -EMSGSIZE ; +break ; + } + rule_locs[cnt++] = id; + } +if (!err) + rxnfc->rule_cnt = cnt;break ;default : err = -EOPNOTSUPP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 278/733 net: bridge: mcast: properly convert mglist to rcu
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Aleksandrov <[email protected]>
[ Upstream commit 4b772869a1e5f9da5cef5b9c722ec0aa424ee0a0 ]
Sashiko reported a bug [1] that br_multicast_del_port_group unlists the port group not using proper rcu helper that preserves the next pointer and after that immediately frees the port group without waiting for rcu grace period. The only rcu walker of mglist is br_multicast_list_adjacent() and it turns out that function has always been buggy because mglist was never properly converted to RCU. Fix it by converting it to rcu and moving its initialization after eth_addr's. Initializing p->next can use RCU_INIT_POINTER because we have a barrier from the hlist_add_head_rcu call later, besides we're initializing an unpublished structure anyway.
[1] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260826014200.362304-1-littleddfu%40gmail.com
--- net/bridge/br_multicast.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
net/bridge/br_multicast.c +5 -4
@@ -1435,16 +1435,17 @@ struct net_bridge_port_group *br_multicast_new_port_group( goto free_out; } -rcu_assign_pointer (p->next, next);timer_setup (&p->timer, br_multicast_port_group_expired,0 );timer_setup (&p->rexmit_timer, br_multicast_port_group_rexmit,0 ); -hlist_add_head (&p->mglist, &port->mglist);if (src)memcpy (p->eth_addr, src,ETH_ALEN );else eth_broadcast_addr (p->eth_addr); +RCU_INIT_POINTER (p->next, next); +hlist_add_head_rcu (&p->mglist, &port->mglist); +return p; free_out:
@@ -1459,11 +1460,11 @@ void br_multicast_del_port_group(struct net_bridge_port_group *p)struct net_bridge_port *port = p->key.port;__u16 vid = p->key.addr.vid; -hlist_del_init (&p->mglist); +hlist_del_init_rcu (&p->mglist);if (!br_multicast_is_star_g (&p->key.addr))rhashtable_remove_fast (&port->br->sg_port_tbl, &p->rhnode, br_sg_port_rht_params); -kfree (p); +kfree_rcu (p, rcu);br_multicast_port_ngroups_dec (port, vid); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 279/733 octeontx2-af: mcs: Clear stale X2P calibration state before calibration
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viswajith Murali <[email protected]>
[ Upstream commit 1f29543126dde307e8b5fb6a740c54e59deaa2ff ]
Some firmware versions leave MCSX_MIL_GLOBAL bit 5 set on boot. If the bit is already set when the driver attempts X2P calibration, the hardware sees no rising edge and calibration never triggers. Clear the bit and wait briefly before starting calibration to ensure a clean rising edge.
--- drivers/net/ethernet/marvell/octeontx2/af/mcs.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/net/ethernet/marvell/octeontx2/af/mcs.c +10 -0
@@ -1417,6 +1417,16 @@ static int mcs_x2p_calibration(struct mcs *mcs)int i, err =0 ;u64 val; +/* Clear any stale calibration state left by firmware/bootloader. +* Some firmware versions may leave MCSX_MIL_GLOBAL bit 5 set, +* preventing the hardware from detecting the rising edge needed to +* trigger X2P calibration. +*/ + val =mcs_reg_read (mcs,MCSX_MIL_GLOBAL ); + val &= ~BIT_ULL (5 ); +mcs_reg_write (mcs,MCSX_MIL_GLOBAL , val); +usleep_range (100 ,200 ); +/* set X2P calibration */ val =mcs_reg_read (mcs,MCSX_MIL_GLOBAL ); val |=BIT_ULL (5 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 280/733 ionic: use netif_txq_maybe_stop() in ionic_tx()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil P. Rao <[email protected]>
[ Upstream commit c91b4d6e5cc30ceea3f23ebe29aec012709a065f ]
Commit 061b9bedbef1 ("ionic: Rework Tx start/stop flow") replaced ionic_maybe_stop_tx() with netif_txq_maybe_stop() to get the memory barriers around the stop/start bits right, but did not cover the stop in ionic_tx() added by commit 138506ab249b ("ionic: Check stop no restart"). Convert the remaining site.
netif_txq_maybe_stop() requires the ring indexes to be updated before it is invoked, so the post has to come first. But ring_dbell comes from __netdev_tx_sent_queue(), which runs after that and reads the stop bit, so it is not known in time to pass to ionic_txq_post(). Post without the doorbell and ring it separately.
The stop condition is unchanged. The re-check only clears the stop bit when space has become available, so the doorbell starvation fixed by commit 138506ab249b ("ionic: Check stop no restart") cannot recur.
--- drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/net/ethernet/pensando/ionic/ionic_txrx.c +10 -3
@@ -1672,15 +1672,22 @@ static int ionic_tx(struct net_device *netdev, struct ionic_queue *q, stats->pkts++; stats->bytes += skb->len; +ionic_txq_post (q, false); +if (likely (!ionic_txq_hwstamp_enabled (q))) {struct netdev_queue *ndq =q_to_ndq (netdev, q); -if (unlikely (!ionic_q_has_space (q,MAX_SKB_FRAGS +1 ))) -netif_tx_stop_queue (ndq); +netif_txq_maybe_stop (ndq,ionic_q_space_avail (q), +MAX_SKB_FRAGS +1 ,MAX_SKB_FRAGS +1 ); ring_dbell =__netdev_tx_sent_queue (ndq, skb->len,netdev_xmit_more ()); } -ionic_txq_post (q, ring_dbell); + +if (ring_dbell) { +ionic_dbell_ring (q->lif->kern_dbpage, q->hw_type, + q->dbval | q->head_idx); + q->dbell_jiffies = jiffies; + }return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 281/733 net: usb: cx82310_eth: drop URB after 0xffff reboot sentinel to prevent partial_data heap overflow
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Winter <[email protected]>
[ Upstream commit 5d50e90add8b4a978395e893e81954d19d58a7c5 ]
The 0xffff length sentinel detects a router reboot and schedules re-enabling of ethernet mode, but then falls through to the rest of the loop body. The next check is
} else if (len > CX82310_MTU) {which is the else of the just-matched if -- it never fires for len == 0xffff. The MTU bound that normally caps the incomplete-packet save path is silently bypassed.
With 0xffff > skb->len always true (rx_urb_size is 4096), the incomplete-packet branch saves dev->partial_len = skb->len bytes into dev->partial_data. partial_data is kmalloc(hard_mtu) = kmalloc(CX82310_MTU + 2) = 1516 bytes, but skb->len after the 2-byte header pull can be up to 4094. A device that sends a 4096-byte URB starting with [0xff 0xff] therefore copies 4094 device-provided bytes into a buffer allocated for 1516 bytes, exceeding its requested size by 2578 bytes.
The next URB then reads dev->partial_len (4094) back from the same 1516-byte buffer and dev->partial_rem (65535 - 4094 = 61441) from the new URB's ~4KB skb, both well past their allocations, and delivers the spliced result as a 64KB "frame" to the network stack.
Bail out of rx_fixup after scheduling the re-enable work; the remainder of a reboot-marker URB is not meaningful packet data. This restores the invariant that partial_len < CX82310_MTU + 2 on the save path, since every other route there has already passed the MTU check.
--- drivers/net/usb/cx82310_eth.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/usb/cx82310_eth.c +1 -0
@@ -282,6 +282,7 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb)if (len ==0xffff ) {netdev_info (dev->net,"router was rebooted, re-enabling ethernet mode" );schedule_work (&priv->reenable_work); +return 0 ; }else if (len >CX82310_MTU ) {netdev_err (dev->net,"RX packet too long: %d B\n" , len);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 282/733 dibs: Unregister dibs_class after error
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
[ Upstream commit 1668a31e3b1ad358d981ddb6dbd3db1fe0533621 ]
In case dibs_loopback_init() fails, e.g. because of -ENOMEM, dibs_init() must unregister dibs_class. Otherwise dibs_class and /sys/class/dibs exist even though the functionality is not available. A retry to load the module fails with -EEXIST.
Unregister dibs_class in the error path of dibs_init.
Note that before
commit ad3dfa80be76 ("dibs: change dibs_class to a const struct") class_destroy(dibs_class) is required instead of class_unregister(&dibs_class).
--- drivers/dibs/dibs_main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/dibs/dibs_main.c +8 -2
@@ -251,13 +251,19 @@ static int __init dibs_init(void) rc =class_register (&dibs_class);if (rc) -return rc; + goto err; rc =dibs_loopback_init ();if (rc) -pr_err ("%s fails with %d\n" , __func__, rc); + goto err_unregister;return rc; + +err_unregister: +class_unregister (&dibs_class); +err: +pr_err ("%s fails with %d\n" , __func__, rc); +return rc; } static void __exit dibs_exit(void)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 283/733 s390/ism: folio_put() after error
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
[ Upstream commit 907a56ab3eb8a58500a58daa76087f17bb2b6826 ]
dmb->cpu_addr was allocated via folio_alloc(). Use folio_put() instead of kfree() in the error exit of ism_alloc_dmb() to avoid slab allocator corruption.
While at it, reset dmb->cpu_addr after folio_put to avoid unintentional UAF by future callers.
--- drivers/s390/net/ism_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/s390/net/ism_drv.c +3 -1
@@ -231,6 +231,7 @@ static void ism_free_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)dma_unmap_page (&ism->pdev->dev, dmb->dma_addr, dmb->dmb_len,DMA_FROM_DEVICE );folio_put (virt_to_folio (dmb->cpu_addr)); + dmb->cpu_addr =NULL ; }static int ism_alloc_dmb (struct ism_dev *ism,struct dibs_dmb *dmb)
@@ -274,7 +275,8 @@ static int ism_alloc_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)return 0 ; out_free: -kfree (dmb->cpu_addr); +folio_put (folio); + dmb->cpu_addr =NULL ; out_bit:clear_bit (dmb->idx, ism->sba_bitmap);return rc;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 284/733 vxlan: reject dynamic fdb entries that reference a nexthop id
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seungwon Bae <[email protected]>
[ Upstream commit 98fc57d167446b95b4e719815fe79edef93f8e7a ]
The commit cited in the Fixes tag allowed VXLAN FDB entries to point to FDB nexthops so that overlay traffic could be load balanced across multiple VTEPs. Such entries can only be configured from user space, cannot be learned and cannot roam. They only make sense with a user space control plane such as E-VPN where data plane learning is disabled.
Despite that, the VXLAN driver does not currently prevent such entries from being configured with the "dynamic" flag. The per-nexthop FDB list is only protected by the per-device hash lock, which is not sufficient when two VXLAN devices point to the same FDB nexthop and therefore share the list. Aging runs in softirq context without RTNL, so an entry deleted by one device can race with an addition or deletion from the other, leading to list corruption:
list_del corruption. next->prev should be ffff8881069d9548, but was dead000000000122. (next=ffff8881069d9448)
WARNING: CPU: 0 PID: 90 at lib/list_debug.c:65 __list_del_entry_valid_or_report+0x1aa/0x210 ... vxlan_fdb_destroy+0x5b8/0xad0 vxlan_cleanup+0x328/0x450 call_timer_fn+0x2a/0x1c0 run_timer_softirq+0x18c/0x210 BUG: KASAN: slab-use-after-free in vxlan_fdb_destroy
Fix this by rejecting the bogus configuration of dynamic FDB entries that point to FDB nexthops, both when created and when an existing entry is updated. As such, the per-nexthop FDB list is only ever mutated under the RTNL lock. Add test cases to make sure that this does not regress in the future.
--- drivers/net/vxlan/vxlan_core.c | 11 ++++++++ tools/testing/selftests/net/fib_nexthops.sh | 28 +++++++++++++++++++++ 2 files changed, 39 insertions(+)
drivers/net/vxlan/vxlan_core.c +11 -0
@@ -996,6 +996,12 @@ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,return -EOPNOTSUPP ; } +if (rcu_access_pointer (f->nh) && + !(state & (NUD_PERMANENT |NUD_NOARP ))) { +NL_SET_ERR_MSG (extack,"Cannot make a nexthop fdb dynamic" ); +return -EOPNOTSUPP ; + } +/* Do not allow an externally learned entry to take over an entry added * by the user. */
@@ -1257,6 +1263,11 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],if (err)return err; +if (nhid && !(ndm->ndm_state & (NUD_PERMANENT |NUD_NOARP ))) { +NL_SET_ERR_MSG (extack,"A nexthop fdb cannot be dynamic" ); +return -EINVAL ; + } +if (vxlan->default_dst.remote_ip.sa.sa_family != ip.sa.sa_family)return -EAFNOSUPPORT ;
tools/testing/selftests/net/fib_nexthops.sh +28 -0
@@ -522,6 +522,20 @@ ipv6_fdb_grp_fcnal() run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 61 self" log_test $? 255 "Fdb mac add with nexthop" + # fdb entries with a nexthop group cannot be aged out + run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static" + log_test $? 0 "Fdb mac add with nexthop group and static state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic" + log_test $? 255 "Fdb mac add with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self" + run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic" + log_test $? 255 "Fdb mac replace with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic" + log_test $? 255 "Fdb mac append with nexthop group and dynamic state" + run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 66" log_test $? 2 "Route add with fdb nexthop"
@@ -622,6 +636,20 @@ ipv4_fdb_grp_fcnal() run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 12 self" log_test $? 255 "Fdb mac add with nexthop" + # fdb entries with a nexthop group cannot be aged out + run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static" + log_test $? 0 "Fdb mac add with nexthop group and static state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic" + log_test $? 255 "Fdb mac add with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self" + run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic" + log_test $? 255 "Fdb mac replace with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic" + log_test $? 255 "Fdb mac append with nexthop group and dynamic state" + run_cmd "$IP ro add 172.16.0.0/22 nhid 16" log_test $? 2 "Route add with fdb nexthop"
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 285/733 net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 66ab4c59b74db7ab53a1c9083feaaede393a96a0 ]
Several subsystems allocate ring buffers sized by dev->tx_queue_len with no upper bound. An unprivileged user (via unshare -Urn) can set a huge tx_queue_len and exhaust global memory with ring allocations:
- pfifo_fast: pfifo_fast_init() and pfifo_fast_change_tx_queue_len() allocate 3 skb_array rings of tx_queue_len entries each.
- tun: tun_queue_resize() and the queue-attach path resize ptr_rings to tx_queue_len on the NETDEV_CHANGE_TX_QUEUE_LEN notifier.
- tap (macvtap/ipvtap): tap_queue_resize() and tap_init() resize/init ptr_rings to tx_queue_len on the same notifier.
netif_change_tx_queue_len() is the single entry point for IFLA_TXQLEN, sysfs, and the SIOCSIFTXQLEN ioctl. Cap new_len at S16_MAX (32767) there so the oversized value is rejected at set time. This takes effect whether the device is up or down, before dev->tx_queue_len is written, before any notifier fires, and before any ring is allocated. The "> S16_MAX" check also subsumes the previous unsigned-long truncation test, and a negative ifr_qlen from the ioctl lands far above the cap after conversion, so both old failure modes are covered by the one comparison.
tx_queue_len is ambigious: both a per-ring sizing multiplier and a default queue-length/limit knob for consumers that allocate nothing at set time (pfifo/bfifo/gred/plug/sfb limits, htb direct_qlen, qfq max_classes, teql). 32767 is chosen as the largest value NLA_POLICY_FULL_RANGE can express for the u32 IFLA_TXQLEN policy in patch 2/3 while staying a legitimate queue length on high-BDP paths; the ring-memory trade-off of a shared knob is disclosed below.
Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_VETH=y, CONFIG_USER_NS=y, CONFIG_NET_NS=y.
- Unprivileged user in a fresh user+net namespace (unshare -Urn).
- pfifo_fast: create veth pairs, set tx_queue_len to 500000, attach mq+pfifo_fast. ~28 iterations OOMs a 2GB guest.
- tun: create 50 tun devices with IFF_MULTI_QUEUE, set tx_queue_len to 500000, open 8 queues each. ~1.6GB of ptr_ring allocations OOMs a 512MB guest.
- tap: same as tun with IFF_TAP. ~960MB OOMs a 512MB guest.
- On the fixed kernel the oversized tx_queue_len is rejected with -ERANGE at set time (all four paths: RTM_SETLINK, RTM_NEWLINK create, sysfs, ioctl - the latter two via this check, the former two via this check and the 2/3 parse policy respectively).
--- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/dev.c +1 -1
@@ -9932,7 +9932,7 @@ int netif_change_tx_queue_len(struct net_device *dev, unsigned long new_len)unsigned int orig_len = dev->tx_queue_len;int res; -if (new_len != (unsigned int )new_len) +if (new_len >S16_MAX )return -ERANGE ;if (new_len != orig_len) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 286/733 net: reject oversized tx_queue_len at netlink parse time
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 1aa9e143bf51405665a793d4cc925e1c4f0c5922 ]
rtnl_create_link() assigns IFLA_TXQLEN directly to dev->tx_queue_len without going through netif_change_tx_queue_len(), so a device created with "ip link add ... txqueuelen 500000" bypasses the S16_MAX cap and still triggers the oversized ring allocations in pfifo_fast, tun and tap. The veth peer nest (rtnl_nla_parse_ifinfomsg()) and the RTM_NEWLINK-on-existing-device path reach the same sinks.
Enforce the cap in ifla_policy instead: IFLA_TXQLEN becomes NLA_POLICY_FULL_RANGE(NLA_U32, &txqlen_range) with txqlen_range = { .min = 0, .max = S16_MAX }. All netlink consumers parse against this policy - rtnl_setlink(), rtnl_newlink() (create and change), and the veth peer nest - so every netlink path is capped at parse time and rejects the attribute with -ERANGE plus a proper "integer out of range" extack message before any device state is modified (the RTM_SETLINK half-application wart is gone with it).
Document the bound in the rt-link.yaml netlink spec.
Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_VETH=y, CONFIG_USER_NS=y, CONFIG_NET_NS=y.
- Unprivileged user in a fresh user+net namespace (unshare -Urn):
ip link add v0 txqueuelen 500000 type veth peer name v1
-> on the fixed kernel this is rejected with -ERANGE ("integer out of range" extack) instead of installing an oversized tx_queue_len that later inflates pfifo_fast/tun/tap ring allocations.
- ip link set v0 txqueuelen 500000 is likewise rejected at parse time.
--- Documentation/netlink/specs/rt-link.yaml | 2 ++ net/core/rtnetlink.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-)
Documentation/netlink/specs/rt-link.yaml +2 -0
@@ -898,6 +898,8 @@ attribute-sets: - name: txqlen type: u32 + checks: + max: 32767 - name: map type: binary
net/core/rtnetlink.c +6 -1
@@ -2236,6 +2236,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,return -EMSGSIZE ; } +static const struct netlink_range_validation txqlen_range = { + .min =0 , + .max =S16_MAX , +}; +static const struct nla_policy ifla_policy[IFLA_MAX +1 ] = { [IFLA_UNSPEC ] = { .strict_start_type =IFLA_DPLL_PIN }, [IFLA_IFNAME ] = { .type =NLA_STRING , .len =IFNAMSIZ -1 },
@@ -2246,7 +2251,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_LINK] = { .type = NLA_U32 }, [IFLA_MASTER] = { .type = NLA_U32 }, [IFLA_CARRIER] = { .type = NLA_U8 }, - [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = NLA_POLICY_FULL_RANGE(NLA_U32, &txqlen_range), [IFLA_WEIGHT] = { .type = NLA_U32 }, [IFLA_OPERSTATE] = { .type = NLA_U8 }, [IFLA_LINKMODE] = { .type = NLA_U8 },
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 287/733 pds_core: fix cmd_regs access racing BAR unmap on reset
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil P. Rao <[email protected]>
[ Upstream commit 7980325b2f71e3f65c1323c39792e2455da6fab6 ]
pdsc_reset_prepare() and pdsc_reset_done()'s pdsc_map_bars() error path clear/iounmap cmd_regs without devcmd_lock, and pdsc_legacy_firmware_update()'s download loop derefs cmd_regs after dropping and retaking the lock without re-checking. An FLR concurrent with a devlink flash can unmap cmd_regs under an in-flight devcmd, causing a NULL deref or a write to unmapped MMIO.
Take devcmd_lock across the BAR unmap/remap, and re-check cmd_regs in the download loop. Only the PF maps cmd_regs and runs devcmd, so skip the unmap on a VF, as pdsc_remove() and pdsc_reset_done() already do.
A reset that completes entirely within the unlocked window is not a correctness problem for the image: the device clears its update session, so a resumed download is rejected, and it verifies the staged image before writing a flash slot, reporting PDS_RC_BAD_FW rather than activating it.
pdsc_unmap_bars() also clears info_regs, intr_status and intr_ctrl. The interrupt and start/stop readers of those are quiesced before the unmap by pdsc_fw_down(), which frees the interrupts and tears down the queues. The debugfs readers are not, since those files outlive a reset; that is pre-existing and out of scope here.
--- drivers/net/ethernet/amd/pds_core/fw.c | 10 +++++++++- drivers/net/ethernet/amd/pds_core/main.c | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-)
drivers/net/ethernet/amd/pds_core/fw.c +9 -1
@@ -107,8 +107,10 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,dev_info (pdsc->dev,"Installing firmware\n" ); -if (!pdsc->cmd_regs) +if (!pdsc->cmd_regs) { +NL_SET_ERR_MSG_MOD (extack,"BARs not mapped" );return -ENXIO ; + } dl =priv_to_devlink (pdsc); devlink_flash_update_status_notify(dl, "Preparing to flash",
@@ -134,6 +136,12 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw, copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);mutex_lock (&pdsc->devcmd_lock); +if (!pdsc->cmd_regs) { +mutex_unlock (&pdsc->devcmd_lock); + err = -ENXIO ; +NL_SET_ERR_MSG_MOD (extack,"Device reset during flash" ); + goto err_out; + }memcpy_toio (&pdsc->cmd_regs->data, fw->data + offset, copy_sz); err =pdsc_devcmd_fw_download_locked (pdsc, data_addr, offset, copy_sz);
drivers/net/ethernet/amd/pds_core/main.c +7 -1
@@ -507,7 +507,11 @@ static void pdsc_reset_prepare(struct pci_dev *pdev)pdsc_auxbus_dev_del (pdsc, pdsc, &pdsc->padev); } -pdsc_unmap_bars (pdsc); +if (!pdev->is_virtfn) { +mutex_lock (&pdsc->devcmd_lock); +pdsc_unmap_bars (pdsc); +mutex_unlock (&pdsc->devcmd_lock); + }pci_release_regions (pdev);if (pci_is_enabled (pdev))pci_disable_device (pdev);
@@ -536,7 +540,9 @@ static void pdsc_reset_done(struct pci_dev *pdev)return ; } +mutex_lock (&pdsc->devcmd_lock); err =pdsc_map_bars (pdsc); +mutex_unlock (&pdsc->devcmd_lock);if (err)return ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 288/733 pds_core: dont release PCI regions for VFs on reset
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil P. Rao <[email protected]>
[ Upstream commit 73608de7e59246b4b533c1ffaee158a7048e186e ]
pdsc_reset_prepare() called pci_release_regions() unconditionally, but only PFs call pci_request_regions() (pdsc_init_pf). On a VF FLR this makes the kernel warn "Trying to free nonexistent resource".
--- drivers/net/ethernet/amd/pds_core/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/amd/pds_core/main.c +1 -1
@@ -511,8 +511,8 @@ static void pdsc_reset_prepare(struct pci_dev *pdev)mutex_lock (&pdsc->devcmd_lock);pdsc_unmap_bars (pdsc);mutex_unlock (&pdsc->devcmd_lock); +pci_release_regions (pdev); } -pci_release_regions (pdev);if (pci_is_enabled (pdev))pci_disable_device (pdev); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 289/733 bpf: mark a NULL call argument precise
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 1a3a10b030c96ea88868ccc060a16827c01eaa5a ]
check_func_arg() allows bpf_register_is_null() for nullable arguments w/o marking the underlying scalar register precise. Hence a checkpoint created on such a path would prune against arbitrary scalar value.
check_helper_call() enforces second parameter of the bpf_get_local_storage() to be zero, w/o marking the underlying scalar register precise. Hence a checkpoint created on such a path would prune against arbitrary scalar value.
Grouping these two into one patch, as they share the same fixes tag.
--- kernel/bpf/verifier.c | 9 ++++++++- .../selftests/bpf/progs/verifier_subprog_precision.c | 12 ++++++------ 2 files changed, 14 insertions(+), 7 deletions(-)
kernel/bpf/verifier.c +8 -1
@@ -8317,11 +8317,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,return err; } -if (bpf_register_is_null (reg) &&type_may_be_null (arg_type)) +if (bpf_register_is_null (reg) &&type_may_be_null (arg_type)) {/* A NULL register has a SCALAR_VALUE type, so skip * type checking. */ + err =mark_chain_precision (env, regno); +if (err) +return err; goto skip_type_check; + }/* arg_btf_id and arg_size are in a union. */ if (base_type(arg_type) == ARG_PTR_TO_BTF_ID ||
@@ -10402,6 +10406,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insnverbose (env,"get_local_storage() doesn't support non-zero flags\n" );return -EINVAL ; } + err =mark_chain_precision (env,BPF_REG_2 ); +if (err) +return err;break ;case BPF_FUNC_for_each_map_elem: err = push_callback_call(env, insn, insn_idx, meta.subprogno,
tools/testing/selftests/bpf/progs/verifier_subprog_precision.c +6 -6
@@ -287,9 +287,9 @@ __msg("17: (b7) r0 = 0") __msg("18: (95) exit") __msg("returning from callee:") __msg("to caller at 9:") -__msg("frame 0: propagating r1,r4") +__msg("frame 0: propagating r1,r3,r4") __msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") -__msg("mark_precise: frame0: regs=r1,r4 stack= before 18: (95) exit") +__msg("mark_precise: frame0: regs=r1,r3,r4 stack= before 18: (95) exit") __msg("from 18 to 9: safe")__naked int callback_result_precise (void ) {
@@ -419,9 +419,9 @@ __msg("to caller at 9:")/* r1, r4 are always precise for bpf_loop(), * r6 was marked before backtracking to callback body. */ -__msg("frame 0: propagating r1,r4,r6") +__msg("frame 0: propagating r1,r3,r4,r6") __msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") -__msg("mark_precise: frame0: regs=r1,r4,r6 stack= before 16: (95) exit") +__msg("mark_precise: frame0: regs=r1,r3,r4,r6 stack= before 16: (95) exit") __msg("mark_precise: frame1: regs= stack= before 15: (b7) r0 = 0") __msg("mark_precise: frame1: regs= stack= before 9: (85) call bpf_loop")__msg ("mark_precise: frame0: parent state regs= stack=:" )
@@ -575,9 +575,9 @@ __msg("to caller at 10:")/* r1, r4 are always precise for bpf_loop(), * fp-8 was marked before backtracking to callback body. */ -__msg("frame 0: propagating r1,r4,fp-8") +__msg("frame 0: propagating r1,r3,r4,fp-8") __msg("mark_precise: frame0: last_idx 10 first_idx 10 subseq_idx -1") -__msg("mark_precise: frame0: regs=r1,r4 stack=-8 before 18: (95) exit") +__msg("mark_precise: frame0: regs=r1,r3,r4 stack=-8 before 18: (95) exit") __msg("mark_precise: frame1: regs= stack= before 17: (b7) r0 = 0") __msg("mark_precise: frame1: regs= stack= before 10: (85) call bpf_loop#181")__msg ("mark_precise: frame0: parent state regs= stack=:" )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 290/733 bpf: propagate mark_chain_precision() errors out of loop_flag_is_zero()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 1d7f8f191c06f967a85922c4652dc33c132b585d ]
Stop verification if mark_chain_precision() fails when called from loop_flag_is_zero(). No functional change intended for the paths where backtracking succeeds.
--- kernel/bpf/verifier.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-)
kernel/bpf/verifier.c +25 -11
@@ -10188,33 +10188,45 @@ static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env)return &env->insn_aux_data[env->insn_idx]; } -static bool loop_flag_is_zero (struct bpf_verifier_env *env) +/* Returns 1 if R4 is a known zero, 0 if it is not, a negative errno on error. */ +static int loop_flag_is_zero (struct bpf_verifier_env *env) {struct bpf_reg_state *reg =reg_state (env,BPF_REG_4 ); -bool reg_is_null =bpf_register_is_null (reg); +int err; -if (reg_is_null) -mark_chain_precision (env,BPF_REG_4 ); +if (!bpf_register_is_null (reg)) +return 0 ; -return reg_is_null; + err =mark_chain_precision (env,BPF_REG_4 ); +if (err) +return err; +return 1 ; } -static void update_loop_inline_state (struct bpf_verifier_env *env,u32 subprogno) +static int update_loop_inline_state (struct bpf_verifier_env *env,u32 subprogno) {struct bpf_loop_inline_state *state = &cur_aux (env)->loop_inline_state; +int flag_is_zero;if (!state->initialized) { + flag_is_zero =loop_flag_is_zero (env); +if (flag_is_zero <0 ) +return flag_is_zero; state->initialized =1 ; - state->fit_for_inline =loop_flag_is_zero (env); + state->fit_for_inline = flag_is_zero; state->callback_subprogno = subprogno; -return ; +return 0 ; }if (!state->fit_for_inline) -return ; +return 0 ; - state->fit_for_inline = (loop_flag_is_zero (env) && + flag_is_zero =loop_flag_is_zero (env); +if (flag_is_zero <0 ) +return flag_is_zero; + state->fit_for_inline = (flag_is_zero && state->callback_subprogno == subprogno); +return 0 ; }/* Returns whether or not the given map can potentially elide
@@ -10426,7 +10438,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn err =check_bpf_snprintf_call (env, regs);break ;case BPF_FUNC_loop: -update_loop_inline_state (env, meta.subprogno); + err =update_loop_inline_state (env, meta.subprogno); +if (err) +return err;/* Verifier relies on R1 value to determine if bpf_loop() iteration * is finished, thus mark it precise. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 291/733 powerpc/kexec_file: Use inclusive range checks in add_usable_mem()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit c6755be4838d6ccd641effbcdc3d917b82631ff9 ]
add_usable_mem() adds usable memory ranges for the kdump kernel.
The ranges are inclusive, but the partial overlap check uses exclusive comparisons. This skips ranges with base == loc_end or end == loc_base. Use inclusive comparisons instead.
--- arch/powerpc/kexec/file_load_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kexec/file_load_64.c +1 -1
@@ -113,7 +113,7 @@ static int add_usable_mem(struct umem_info *um_info, u64 base, u64 end) loc_end = um_info->ranges[i].end;if (loc_base >= base && loc_end <= end) add = true; -else if (base < loc_end && end > loc_base) { +else if (base <= loc_end && end >= loc_base) {if (loc_base < base) loc_base = base;if (loc_end > end)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 292/733 powerpc/kexec_file: Use inclusive range checks for excluded memory
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 449f60f99f8f3cbe80a9bd2242945e827c5ed003 ]
arch_check_excluded_range() checks if a kexec segment overlaps an excluded memory range.
Both ranges use inclusive end addresses, but the overlap check uses exclusive comparisons. This skips ranges with start == ->ranges[i].end or end == ->ranges[i].start. Use inclusive comparisons instead.
--- arch/powerpc/kexec/file_load_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kexec/file_load_64.c +1 -1
@@ -57,7 +57,7 @@ int arch_check_excluded_range(struct kimage *image, unsigned long start, emem = image->arch.exclude_ranges;for (i =0 ; i < emem->nr_ranges; i++) -if (start < emem->ranges[i].end && end > emem->ranges[i].start) +if (start <= emem->ranges[i].end && end >= emem->ranges[i].start)return 1 ;return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 293/733 net: mctp: i3c: serialize probe with bus removal
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: XingWang Xiang <[email protected]>
[ Upstream commit 2b4707a149a55e8fa75c9ef32b359d60f470a566 ]
mctp_i3c_probe() drops busdevs_lock after finding the matching bus. A concurrent I3C_NOTIFY_BUS_REMOVE can then unregister and free the bus netdev before probe passes its private data to mctp_i3c_add_device(). The latter consequently adds a list node through a freed mbus pointer.
Keep busdevs_lock held until the device has been added. This also satisfies the __must_hold annotation on mctp_i3c_add_device().
--- drivers/net/mctp/mctp-i3c.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/net/mctp/mctp-i3c.c +6 -3
@@ -288,6 +288,7 @@ __must_hold(&busdevs_lock)static int mctp_i3c_probe (struct i3c_device *i3c) {struct mctp_i3c_bus *b =NULL , *mbus =NULL ; +int rc;/* Look for a known bus */ mutex_lock (&busdevs_lock);
@@ -296,14 +297,16 @@ static int mctp_i3c_probe(struct i3c_device *i3c) mbus = b;break ; } -mutex_unlock (&busdevs_lock);if (!mbus) {/* probably no "mctp-controller" property on the i3c bus */ -return -ENODEV ; + rc = -ENODEV ; + }else { + rc =mctp_i3c_add_device (mbus, i3c); } +mutex_unlock (&busdevs_lock); -return mctp_i3c_add_device (mbus, i3c); +return rc; }static void mctp_i3c_remove_device (struct mctp_i3c_device *mi)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 294/733 powerpc/entry: Fix irq_soft_mask corruption on replayed interrupt exit
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mukesh Kumar Chaurasiya (IBM) <[email protected]>
[ Upstream commit 63a7531ca31f9f097d9cc1cc3fe86ae683cdabdd ]
When __replay_soft_interrupts() replays a pending interrupt (e.g. PACA_IRQ_DEC -> timer_interrupt), it calls the handler directly with a synthetic pt_regs. The DEFINE_INTERRUPT_HANDLER_ASYNC wrapper around each handler calls arch_interrupt_async_exit_prepare() on the way out, which calls arch_interrupt_exit_prepare() -> local_irq_disable() -> arch_local_irq_disable(), which does:
irq_soft_mask_set(IRQS_DISABLED) /* 0x1 */
This unconditionally overwrites irq_soft_mask with IRQS_DISABLED (0x1), stripping the IRQS_PMI_DISABLED (0x2) bit. The result is that irq_soft_mask is 0x1 instead of IRQS_ALL_DISABLED (0x3) when the handler returns to __replay_soft_interrupts().
For a normally-taken interrupt this is harmless: the next interrupt always enters through arch_interrupt_enter_prepare() which unconditionally sets irq_soft_mask to IRQS_ALL_DISABLED. But during replay, next_interrupt() is called directly between replayed handlers without going back through arch_interrupt_enter_prepare(), so the stripped bit is never restored. next_interrupt() then fires a WARNING:
WARNING: arch/powerpc/kernel/irq_64.c:75
WARN_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED)The warning was observed early in boot on a POWER10 pseries guest during kmem_cache_init_late(), where a spinlock release triggers interrupt replay that processes a pending timer interrupt.
Debugger state confirming the bug:
Before timer_interrupt(®s):
irq_soft_mask = 0x3 (IRQS_ALL_DISABLED) correct
irq_happened = 0x41 (HARD_DIS|REPLAYING) correct
After timer_interrupt(®s) returns:
irq_soft_mask = 0x1 (IRQS_DISABLED) WRONG - PMI bit stripped
irq_happened = 0x41 unchangedThe fix is to replace local_irq_disable() with hard_irq_disable().
hard_irq_disable() is the right primitive here for two reasons:
1. On PPC64 (hw_irq.h:301) it calls irq_soft_mask_set_return(IRQS_ALL_DISABLED), setting the soft mask to 0x3 (both IRQS_DISABLED and IRQS_PMI_DISABLED), which preserves the PMI bit and fixes the WARNING. The additional work it does (__hard_irq_disable(), PACA_IRQ_HARD_DIS |=) is redundant but safe since both are already set at this point in the exit path; the trace_hardirqs_off() inside is guarded by
if (!arch_irqs_disabled_flags(flags)) so it will not double-fire.
2. On PPC32 (hw_irq.h:467) hard_irq_disable() maps to arch_local_irq_disable() -> __hard_irq_disable(), which clears MSR[EE] in hardware. This is exactly correct: PPC32 has no soft-mask PACA mechanism, so the hardware disable is the right way to satisfy irqentry_exit()'s requirement. This also fixes a build error on PPC32 where irq_soft_mask_set() is only defined under CONFIG_PPC64:
arch/powerpc/include/asm/entry-common.h:273: error: implicit declaration of function 'irq_soft_mask_set'
Using hard_irq_disable() requires no #ifdef and is consistent with how the rest of the entry code (e.g. entry-common.h:463) handles the same PPC32/PPC64 split.
--- arch/powerpc/include/asm/entry-common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/include/asm/entry-common.h +1 -1
@@ -270,7 +270,7 @@ static inline void arch_interrupt_exit_prepare(struct pt_regs *regs) }/* irqentry_exit expects to be called with interrupts disabled */ -local_irq_disable (); +hard_irq_disable (); }static inline void arch_interrupt_async_enter_prepare (struct pt_regs *regs)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 295/733 net/sched: defer qdisc freeing after failed creation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit e6662f2100f8d33b0f4d0047c219efd6bba186ea ]
An RTM_NEWQDISC request can make clsact bind a populated shared ingress block during ->init(), publishing an embedded mini_Qdisc to lockless readers. If the same request has an invalid TCA_RATE, estimator setup fails after ->init(); the unwind removes the pointer but synchronously frees its containing qdisc while tc_run() may still hold it.
Retire failed qdiscs through the same RCU helper as normal destruction. Inline the synchronous free into the callback now that no direct callers remain.
--- include/net/sch_generic.h | 2 +- net/sched/sch_api.c | 2 +- net/sched/sch_generic.c | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-)
include/net/sch_generic.h +1 -1
@@ -793,7 +793,7 @@ void qdisc_offload_query_caps(struct net_device *dev,struct Qdisc *qdisc_alloc (struct netdev_queue *dev_queue,const struct Qdisc_ops *ops,struct netlink_ext_ack *extack); -void qdisc_free (struct Qdisc *qdisc); +void qdisc_free_rcu (struct Qdisc *qdisc);struct Qdisc *qdisc_create_dflt (struct netdev_queue *dev_queue,const struct Qdisc_ops *ops,u32 parentid,struct netlink_ext_ack *extack);
net/sched/sch_api.c +1 -1
@@ -1385,7 +1385,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev, err_out3:qdisc_lock_uninit (sch, ops);netdev_put (dev, &sch->dev_tracker); -qdisc_free (sch); +qdisc_free_rcu (sch); err_out2:bpf_module_put (ops, ops->owner); err_out:
net/sched/sch_generic.c +10 -10
@@ -1086,21 +1086,21 @@ void qdisc_reset(struct Qdisc *qdisc) }EXPORT_SYMBOL (qdisc_reset); -void qdisc_free (struct Qdisc *qdisc) +static void qdisc_free_cb (struct rcu_head *head) { -if (qdisc_is_percpu_stats (qdisc)) { -free_percpu (qdisc->cpu_bstats); -free_percpu (qdisc->cpu_qstats); + struct Qdisc *q = container_of(head, struct Qdisc, rcu); + +if (qdisc_is_percpu_stats (q)) { +free_percpu (q->cpu_bstats); +free_percpu (q->cpu_qstats); } -kfree (qdisc); +kfree (q); } -static void qdisc_free_cb (struct rcu_head *head) +void qdisc_free_rcu (struct Qdisc *qdisc) { - struct Qdisc *q = container_of(head, struct Qdisc, rcu); - -qdisc_free (q); +call_rcu (&qdisc->rcu, qdisc_free_cb); }static void __qdisc_destroy (struct Qdisc *qdisc)
@@ -1127,7 +1127,7 @@ static void __qdisc_destroy(struct Qdisc *qdisc)trace_qdisc_destroy (qdisc); -call_rcu (&qdisc->rcu, qdisc_free_cb); +qdisc_free_rcu (qdisc); }void qdisc_destroy (struct Qdisc *qdisc)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 296/733 net/mlx5e: Fix missing FEC mode mapping for RS_544_514_INTERLEAVED_QUAD
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shahar Shitrit <[email protected]>
[ Upstream commit 802eedcc0b25bb3e1b492f0600ab74325274d53b ]
MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD is missing from pplm_fec_2_ethtool_linkmodes[], leaving index 4 zero-initialized. As a result, when this FEC mode is active, find_first_bit() returns index 4, causing __set_bit() to set bit 0 (ETHTOOL_LINK_MODE_10baseT_Half_BIT) instead of ETHTOOL_LINK_MODE_FEC_RS_BIT. Consequently, ethtool reports:
Advertised FEC modes: Not reported
Add the missing mapping to ETHTOOL_LINK_MODE_FEC_RS_BIT.
--- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +1 -0
@@ -1013,6 +1013,7 @@ static const u32 pplm_fec_2_ethtool_linkmodes[] = { [MLX5E_FEC_NOFEC] = ETHTOOL_LINK_MODE_FEC_NONE_BIT, [MLX5E_FEC_FIRECODE] = ETHTOOL_LINK_MODE_FEC_BASER_BIT, [MLX5E_FEC_RS_528_514] = ETHTOOL_LINK_MODE_FEC_RS_BIT, + [MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD] = ETHTOOL_LINK_MODE_FEC_RS_BIT, [MLX5E_FEC_RS_544_514] = ETHTOOL_LINK_MODE_FEC_RS_BIT, [MLX5E_FEC_LLRS_272_257_1] = ETHTOOL_LINK_MODE_FEC_LLRS_BIT, };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 297/733 net/mlx5e: Fix setting RS FEC after remapping
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shahar Shitrit <[email protected]>
[ Upstream commit b9d755c5a37519fb1354034db1dfeb30e1ba6856 ]
When a user sets a FEC mode via ethtool, the driver maps the ethtool FEC type to the lowest mlx5 bit of that type. For RS FEC, this is MLX5E_FEC_RS_528_514 (bit 2). The driver then checks whether this bit is supported by at least one link mode by inspecting the fec_override_cap fields via mlx5e_fec_in_caps(), and returns -EOPNOTSUPP if not.
This check is incorrect. RS FEC has three supported hardware variants:RS_528_514 (bit 2), RS_544_514_INTERLEAVED_QUAD (bit 4), and RS_544_514 (bit 7). mlx5e_remap_fec_conf_mode() already remaps bit 2 to the appropriate RS variant per link mode when writing the admin fields, but the early capability check is done against the raw unmapped bit. As a result, a device that supports RS_544_514 or RS_544_514_INTERLEAVED_QUAD but not RS_528_514 will incorrectly reject the user's RS FEC request.
Remove the early support check from mlx5e_set_fec_mode() and fold it into the existing write loop, checking caps against the remapped policy per link mode. Return -EOPNOTSUPP before the final register write if no link mode accepted the policy.
--- drivers/net/ethernet/mellanox/mlx5/core/en/port.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/port.c +9 -6
@@ -557,6 +557,7 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)u32 in[MLX5_ST_SZ_DW (pplm_reg)] = {};int sz =MLX5_ST_SZ_BYTES (pplm_reg);u16 fec_policy_auto =0 ; +bool fec_set = false;int err;int i;
@@ -569,9 +570,6 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)if (fec_policy >= (1 <<MLX5E_FEC_LLRS_272_257_1 ) && !fec_50g_per_lane)return -EOPNOTSUPP ; -if (fec_policy && !mlx5e_fec_in_caps (dev, fec_policy)) -return -EOPNOTSUPP ; -MLX5_SET (pplm_reg, in, local_port,1 ); err =mlx5_core_access_reg (dev, in, sz, out, sz,MLX5_REG_PPLM ,0 ,0 );if (err)
@@ -591,12 +589,17 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)mlx5e_get_fec_cap_field (out, &fec_caps, i);/* policy supported for link speed */ -if (fec_caps & conf_fec) +if (fec_caps & conf_fec) {mlx5e_fec_admin_field (out, &conf_fec,1 , i); -else -/* set FEC to auto*/ + fec_set = true; + }else { +/* set FEC to auto */ mlx5e_fec_admin_field (out, &fec_policy_auto,1 , i); + } } +if (fec_policy && !fec_set) +return -EOPNOTSUPP ; +return mlx5_core_access_reg (dev, out, sz, out, sz,MLX5_REG_PPLM ,0 ,1 ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 298/733 net/mlx5e: Fix reporting support for all RS FEC variants
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shahar Shitrit <[email protected]>
[ Upstream commit c84ce45a7a3f3f024502c7f53308db9c76e4ae71 ]
get_fec_supported_advertised() populates the FEC modes reported as supported to userspace. The MLX5E_ADVERTISE_SUPPORTED_FEC macro only checked MLX5E_FEC_RS_528_514, causing devices that support only the other RS variants (RS_544_514_INTERLEAVED_QUAD or RS_544_514) to not advertise RS as supported to ethtool at all.
Introduce MLX5E_FEC_RS_MASK covering all three RS bit positions, update the macro to accept a bitmask directly rather than a single enum value, and pass MLX5E_FEC_RS_MASK for the RS entry.
--- drivers/net/ethernet/mellanox/mlx5/core/en/port.h | 4 ++++ drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/port.h +4 -0
@@ -66,4 +66,8 @@ enum { MLX5E_FEC_LLRS_272_257_1 = 9, }; +#define MLX5E_FEC_RS_MASK (BIT(MLX5E_FEC_RS_528_514) | \ + BIT(MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD) | \ + BIT(MLX5E_FEC_RS_544_514)) +#endif
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +6 -6
@@ -1002,9 +1002,9 @@ static u32 pplm2ethtool_fec(u_long fec_mode, unsigned long size)return 0 ; } -#define MLX5E_ADVERTISE_SUPPORTED_FEC (mlx5_fec, ethtool_fec) \ +#define MLX5E_ADVERTISE_SUPPORTED_FEC (fec_mask, ethtool_fec) \ do { \ - if (mlx5e_fec_in_caps(dev, 1 << (mlx5_fec))) \ + if (mlx5e_fec_in_caps(dev, fec_mask)) \ __set_bit(ethtool_fec, \ link_ksettings->link_modes.supported);\ } while (0)
@@ -1030,13 +1030,13 @@ static int get_fec_supported_advertised(struct mlx5_core_dev *dev,if (err)return (err == -EOPNOTSUPP ) ?0 : err; -MLX5E_ADVERTISE_SUPPORTED_FEC (MLX5E_FEC_NOFEC , +MLX5E_ADVERTISE_SUPPORTED_FEC (BIT (MLX5E_FEC_NOFEC ),ETHTOOL_LINK_MODE_FEC_NONE_BIT ); -MLX5E_ADVERTISE_SUPPORTED_FEC (MLX5E_FEC_FIRECODE , +MLX5E_ADVERTISE_SUPPORTED_FEC (BIT (MLX5E_FEC_FIRECODE ),ETHTOOL_LINK_MODE_FEC_BASER_BIT ); -MLX5E_ADVERTISE_SUPPORTED_FEC (MLX5E_FEC_RS_528_514 , +MLX5E_ADVERTISE_SUPPORTED_FEC (MLX5E_FEC_RS_MASK ,ETHTOOL_LINK_MODE_FEC_RS_BIT ); -MLX5E_ADVERTISE_SUPPORTED_FEC (MLX5E_FEC_LLRS_272_257_1 , +MLX5E_ADVERTISE_SUPPORTED_FEC (BIT (MLX5E_FEC_LLRS_272_257_1 ),ETHTOOL_LINK_MODE_FEC_LLRS_BIT ); active_fec_long = active_fec;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 299/733 net/mlx5: LAG, use local tracker to update active ports
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Akiva Goldberger <[email protected]>
[ Upstream commit b3c79dee5038c5e8460c59d7d01cb1450bdf5ecb ]
The CREATE_LAG command is handled asynchronously by queuing a work, which stores a local copy of ldev->tracker. When the work is processed, it is possible that the values of the local copy and ldev->tracker have diverged.
A single CREATE_LAG command programs two related fields into the
firmware: the v2p (virtual-to-physical) map, which selects the physical egress port for each hash bucket, and the active_port bitmask, which tells the firmware which physical ports are currently up so it can redirect QP/TIS away from inactive ports. For the firmware to steer traffic correctly, both must be derived from the same view of the ports' link state.
The v2p map is computed by mlx5_infer_tx_affinity_mapping() from the local tracker snapshot, but lag_active_port_bits() called mlx5_infer_tx_enabled() on the live ldev->tracker instead. If ldev->tracker changed between the snapshot and command execution, the two fields reflect different port states: the v2p map may steer a bucket to a port that the active_port mask marks as inactive (or vice versa). The firmware then receives a self-contradictory configuration and can redirect or drop traffic on a port the mapping still points at, until a later event happens to reconcile the state.
Update lag_active_port_bits so that it receives the local version of the tracker from when the work was queued, effectively closing the window for injecting an inconsistency.
--- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +11 -8
@@ -63,14 +63,15 @@ static int get_port_sel_mode(enum mlx5_lag_mode mode, unsigned long flags)return MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY ; } -static u8 lag_active_port_bits (struct mlx5_lag *ldev) +static u8 lag_active_port_bits (struct mlx5_lag *ldev, +struct lag_tracker *tracker) {u8 enabled_ports[MLX5_MAX_PORTS ] = {};u8 active_port =0 ;int num_enabled;int idx; -mlx5_infer_tx_enabled (&ldev->tracker, ldev, enabled_ports, +mlx5_infer_tx_enabled (tracker, ldev, enabled_ports, &num_enabled);for (idx =0 ; idx < num_enabled; idx++) active_port |=BIT_MASK (enabled_ports[idx]);
@@ -79,7 +80,8 @@ static u8 lag_active_port_bits(struct mlx5_lag *ldev) }static int mlx5_cmd_create_lag (struct mlx5_core_dev *dev,struct mlx5_lag *ldev, -int mode,unsigned long flags) +struct lag_tracker *tracker,int mode, +unsigned long flags) {bool fdb_sel_mode =test_bit (MLX5_LAG_MODE_FLAG_FDB_SEL_MODE_NATIVE , &flags);
@@ -108,7 +110,7 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,break ;MLX5_SET (lagc, lag_ctx, active_port, -lag_active_port_bits (mlx5_lag_dev (dev))); +lag_active_port_bits (ldev, tracker));break ;default :break ;
@@ -787,7 +789,8 @@ static int mlx5_cmd_modify_active_port(struct mlx5_core_dev *dev, u8 ports)return mlx5_cmd_exec_in (dev, modify_lag, in); } -static int _mlx5_modify_lag (struct mlx5_lag *ldev,u8 *ports) +static int _mlx5_modify_lag (struct mlx5_lag *ldev, +struct lag_tracker *tracker,u8 *ports) {int idx =mlx5_lag_get_dev_index_by_seq (ldev,MLX5_LAG_P1 );struct mlx5_core_dev *dev0;
@@ -804,7 +807,7 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev, u8 *ports) !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table_bypass))return ret; - active_ports =lag_active_port_bits (ldev); + active_ports =lag_active_port_bits (ldev, tracker);return mlx5_cmd_modify_active_port (dev0, active_ports); }
@@ -868,7 +871,7 @@ void mlx5_modify_lag(struct mlx5_lag *ldev, idx = i * ldev->buckets + j;if (ports[idx] == ldev->v2p_map[idx])continue ; - err =_mlx5_modify_lag (ldev, ports); + err =_mlx5_modify_lag (ldev, tracker, ports);if (err) { mlx5_core_err(dev0, "Failed to modify LAG (%d)\n",
@@ -976,7 +979,7 @@ static int mlx5_create_lag(struct mlx5_lag *ldev,mlx5_core_info (dev0,"shared_fdb:%d mode:%s\n" , shared_fdb,mlx5_get_str_port_sel_mode (mode, flags)); - err =mlx5_cmd_create_lag (dev0, ldev, mode, flags); + err =mlx5_cmd_create_lag (dev0, ldev, tracker, mode, flags);if (err) { mlx5_core_err(dev0, "Failed to create LAG (%d)\n",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 300/733 net/mlx5e: Fix ETS zero BW reporting when one TC holds 100%
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carolina Jubran <[email protected]>
[ Upstream commit e7ee89740800a1cf253713e9249c3ee9203ebe91 ]
When ETS TCs with zero bandwidth are configured, the driver programs the firmware using an alternate representation. On get, it needs to recognize that representation so those TCs can be translated back and reported as 0% bandwidth.
The existing detection relied on the programmed bandwidth because it was enough to identify this representation. However, when a single ETS TC owns 100% of the bandwidth, its firmware representation becomes the same as a strict-priority TC, causing zero-bandwidth ETS TCs to be reported with non-zero bandwidth values.
Use the cached TSA instead to distinguish the ETS and strict-priority cases.
--- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +1 -1
@@ -148,7 +148,7 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev,if (err)return err; -if (ets->tc_tx_bw[i] <MLX5E_MAX_BW_ALLOC && +if (priv->dcbx.tc_tsa[i] ==IEEE_8021QAZ_TSA_ETS && tc_group[i] == (MLX5E_LOWEST_PRIO_GROUP +1 )) is_zero_bw_ets_tc = true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 301/733 net/mlx5e: Fix use-after-free race in sample_restore_put()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carolina Jubran <[email protected]>
[ Upstream commit af3aef0245abbab5e9f6302e7a7d6407187afb71 ]
Concurrent teardown of TC sample rules sharing the same restore context may re-read restore->count after dropping restore_lock. At that point another thread may already have completed cleanup and freed the restore object.
Use the result of the refcount decrement while holding restore_lock to determine whether cleanup is needed.
--- drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c +5 -2
@@ -311,12 +311,15 @@ sample_restore_get(struct mlx5e_tc_psample *tc_psample, u32 obj_id,static void sample_restore_put (struct mlx5e_tc_psample *tc_psample,struct mlx5e_sample_restore *restore) { +bool last; +mutex_lock (&tc_psample->restore_lock); -if (--restore->count ==0 ) + last = --restore->count ==0 ; +if (last)hash_del (&restore->hlist);mutex_unlock (&tc_psample->restore_lock); -if (!restore->count) { +if (last) {mlx5_del_flow_rules (restore->rule);mlx5_modify_header_dealloc (tc_psample->esw->dev, restore->modify_hdr);kfree (restore);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 302/733 net/mlx5: E-Switch: fix use-after-free in mlx5_eswitch_termtbl_put
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yael Chemla <[email protected]>
[ Upstream commit 7ee07f601f8f507c9faf25c68a49396ab8950596 ]
In mlx5_eswitch_termtbl_put(), the zero-ref cleanup check reads tt->ref_count after termtbl_mutex has been released. Two concurrent callers on the same mlx5_termtbl_handle race: one decrements ref_count to zero, removes the hash entry, and calls kfree(tt) while the other has already dropped the mutex and is about to evaluate
if (!tt->ref_count), producing a use-after-free.
Fix this by capturing the result of the decrement into a stack-local last variable before dropping the mutex. The cleanup decision is now made entirely under termtbl_mutex, and tt is not touched after kfree.
--- .../ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c +5 -2
@@ -163,12 +163,15 @@ void mlx5_eswitch_termtbl_put(struct mlx5_eswitch *esw, struct mlx5_termtbl_handle *tt) { + bool last; +mutex_lock (&esw->offloads.termtbl_mutex); -if (--tt->ref_count ==0 ) + last = (--tt->ref_count ==0 ); +if (last)hash_del (&tt->termtbl_hlist);mutex_unlock (&esw->offloads.termtbl_mutex); -if (!tt->ref_count) { +if (last) {mlx5_del_flow_rules (tt->rule);mlx5_destroy_flow_table (tt->termtbl);kfree (tt);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 303/733 net/mlx5: E-Switch, prevent mc_list repopulation during vport disable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lama Kayal <[email protected]>
[ Upstream commit c0c6f4ba8a37688f7b4d4044898d88f0450d44c2 ]
In mlx5_esw_vport_disable(), move esw_apply_vport_rx_mode() ahead of esw_vport_change_handle_locked() so vport->allmulti_rule is NULL before the change handler observes it.
During FW-fatal recovery the disable runs while dev->state == INTERNAL_ERROR. The promisc query inside esw_update_vport_rx_mode() fails and returns early, leaving vport->allmulti_rule intact, so esw_update_vport_mc_promisc() runs and adds MLX5_ACTION_ADD entries to vport->mc_list whose flow rules are then installed in the FDB by esw_add_mc_addr(). esw_destroy_legacy_table() tears down the FDB with those refs still held, corrupting the sub-tree and leaving dangling flow_rule pointers in vport->mc_list.
Two-stage failure on echo 1 > /sys/bus/pci/devices/<bdf>/reset:
refcount_t: underflow; use-after-free.
tree_put_node+0xef/0x110 [mlx5_core] clean_tree+0x44/0xd0 [mlx5_core] (x5) mlx5_fs_core_cleanup+0x57/0x1c0 [mlx5_core] mlx5_unload+0x65/0xd0 [mlx5_core] ... mlx5_health_try_recover BUG: unable to handle page fault for address: 0000000003000055 down_write+0x1c/0x60 mlx5_del_flow_rules+0x33/0x1f0 [mlx5_core] esw_del_mc_addr+0x7b/0x170 [mlx5_core] esw_apply_vport_addr_list+0x56/0xf0 [mlx5_core] esw_vport_change_handle_locked+0x28b/0x310 [mlx5_core] mlx5_esw_vport_enable+0x270/0x4a0 [mlx5_core] ... mlx5_load ... mlx5_health_try_recover
esw_apply_vport_rx_mode(false, false) clears vport->allmulti_rule via its local state machine even when the FW del fails. With the rule NULL the !IS_ERR_OR_NULL(allmulti_rule) gate in the change handler closes, no rules are installed during disable, and the reload starts with a clean mc_list.
--- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +7 -1
@@ -1040,13 +1040,19 @@ void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, struct mlx5_vport *vport) (vport->info.ipsec_crypto_enabled || vport->info.ipsec_packet_enabled)) esw->enabled_ipsec_vf_count--; +/* Clear rx-mode before esw_vport_change_handle_locked(): on +* MLX5_VPORT_PROMISC_CHANGE it calls esw_update_vport_mc_promisc() +* when vport->allmulti_rule is set, repopulating mc_list with FDB +* rules that dangle once the FDB is destroyed. NULL allmulti_rule +* here skips that path. +*/ +esw_apply_vport_rx_mode (esw, vport, false, false);/* We don't assume VFs will cleanup after themselves. * Calling vport change handler while vport is disabled will cleanup * the vport resources. */ esw_vport_change_handle_locked (vport); vport->enabled_events =0 ; -esw_apply_vport_rx_mode (esw, vport, false, false);esw_vport_cleanup (esw, vport); esw->enabled_vports--;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 304/733 net/mlx5e: Keep HW timestamp stats monotonic across reconfiguration
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carolina Jubran <[email protected]>
[ Upstream commit df99553f840e4c529c1ba4c29bd39396466ca11a ]
mlx5e_stats_ts_get() currently selects either DMA or port timestamp counters based on tx_ptp_opened. This flag is intentionally kept set once the PTP TX queues have been opened so their statistics remain available after queue teardown. As a result, DMA timestamps are no longer reported after switching from port timestamping back to DMA timestamping.
The function also reads statistics only from the currently active channels and TCs. Reducing the number of channels or TCs can therefore drop previously accumulated timestamp counters from the reported value.
Read the persistent channel statistics instead and always include DMA timestamp counters. Once the PTP TX queues have been opened, also include the port timestamp counters.
This also drops state_lock. It previously protected live channel/PTP pointers, the new code only reads persistent channel_stats and ptp_stats via mlx5e_stats_nch_read(), which is already safe for lockless stats access.
--- .../ethernet/mellanox/mlx5/core/en_stats.c | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +20 -31
@@ -1199,50 +1199,39 @@ void mlx5e_stats_rmon_get(struct mlx5e_priv *priv,void mlx5e_stats_ts_get (struct mlx5e_priv *priv,struct ethtool_ts_stats *ts_stats) { -int i, j; +u16 nch =mlx5e_stats_nch_read (priv); +int i, tc; -mutex_lock (&priv->state_lock); + ts_stats->pkts =0 ; -if (priv->tx_ptp_opened) { -struct mlx5e_ptp *ptp = priv->channels.ptp; +for (i =0 ; i < nch; i++) { +struct mlx5e_channel_stats *channel_stats = + priv->channel_stats[i]; - ts_stats->pkts =0 ; +for (tc =0 ; tc < priv->max_opened_tc; tc++) + ts_stats->pkts += channel_stats->sq[tc].timestamps; + } + +/* Accumulate DMA and port timestamp counters so values stay monotonic +* across channel teardown and mode switches. +*/ +if (priv->tx_ptp_opened) { +/* Err and Lost stats are only relevant for port timestamping, +* as the DMA layer will always successfully timestamp packets. +*/ ts_stats->err =0 ; ts_stats->lost =0 ; -if (!ptp) - goto out; - -/* Aggregate stats across all TCs */ -for (i =0 ; i < ptp->num_tc; i++) { +for (tc =0 ; tc < priv->max_opened_tc; tc++) {struct mlx5e_ptp_cq_stats *stats = - ptp->ptpsq[i].cq_stats; + &priv->ptp_stats.cq[tc]; ts_stats->pkts += stats->cqe; ts_stats->err += stats->abort + stats->err_cqe + - stats->late_cqe; + stats->late_cqe; ts_stats->lost += stats->lost_cqe; } - }else { -/* DMA layer will always successfully timestamp packets. Other -* counters do not make sense for this layer. -*/ - ts_stats->pkts =0 ; - -/* Aggregate stats across all SQs */ -for (j =0 ; j < priv->channels.num; j++) { -struct mlx5e_channel *c = priv->channels.c[j]; - -for (i =0 ; i < c->num_tc; i++) { -struct mlx5e_sq_stats *stats = c->sq[i].stats; - - ts_stats->pkts += stats->timestamps; - } - } } - -out: -mutex_unlock (&priv->state_lock); }#define PPORT_PHY_LAYER_OFF (c) \
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 305/733 net/sched: fq_pie: clamp quantum in change path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 4864f58c53eb47257d55e01f47d4a9f355f7f970 ]
fq_pie_change() accepts any quantum value from userspace, including 1. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 makes the deficit-refill loop spin ~2^31 times under the qdisc lock (a soft lockup / denial of service).
Add max(256U, ...) matching fq_codel_change().
Conditions to recreate the bug:
CONFIG_NET_SCH_FQ_PIE=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root fq_pie
tc qdisc change dev dummy0 root fq_pie quantum 1 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_fq_pie.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/sched/sch_fq_pie.c +2 -1
@@ -341,7 +341,8 @@ static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt, nla_get_u32(tb[TCA_FQ_PIE_BETA]));if (tb[TCA_FQ_PIE_QUANTUM ]) -WRITE_ONCE (q->quantum,nla_get_u32 (tb[TCA_FQ_PIE_QUANTUM ])); +WRITE_ONCE (q->quantum, +max (256U ,nla_get_u32 (tb[TCA_FQ_PIE_QUANTUM ])));if (tb[TCA_FQ_PIE_MEMORY_LIMIT ]) WRITE_ONCE(q->memory_limit,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 306/733 net/sched: sfq: clamp quantum in change path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit fb9f88a33c516ea5c0bcd9a22ca288b246b34567 ]
sfq_change() accepts any non-negative quantum (only rejects (int)ctl->quantum < 0). With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 makes the deficit-refill loop spin ~2^31 times under the qdisc lock (a soft lockup / denial of service).
Add max(256U, ...) matching fq_codel_change(). Reject quantum > 1<<20 with -EINVAL, matching fq_codel_change() and the init clamp.
Conditions to recreate the bug:
CONFIG_NET_SCH_SFQ=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root sfq
tc qdisc change dev dummy0 root sfq quantum 1 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_sfq.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
net/sched/sch_sfq.c +6 -1
@@ -660,6 +660,11 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt,return -EINVAL ; } +if (ctl->quantum >1 <<20 ) { +NL_SET_ERR_MSG_MOD (extack,"quantum too large" ); +return -EINVAL ; + } +if (ctl->perturb_period <0 || ctl->perturb_period >INT_MAX /HZ ) {NL_SET_ERR_MSG_MOD (extack,"invalid perturb period" );
@@ -688,7 +693,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt,/* update and validate configuration */ if (ctl->quantum) - quantum = ctl->quantum; + quantum =max (256U , ctl->quantum);if (ctl->flows) maxflows =min_t (u32, ctl->flows,SFQ_MAX_FLOWS );if (ctl->divisor) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 307/733 net/sched: hhf: clamp quantum in change and init paths
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit eb56a495f59baf6cad5ed80e3ffb9078098b1346 ]
hhf_change() accepts any quantum from userspace, including 1. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 makes the deficit-refill loop spin ~2^31 times under the qdisc lock (a soft lockup / denial of service).
Add max(256U, ...) in hhf_change() matching fq_codel_change(). Clamp hhf_init() to [256, 1<<20] matching the siblings, and remove the old fallback that only set quantum=256 on overflow.
Conditions to recreate the bug:
CONFIG_NET_SCH_HHF=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root hhf
tc qdisc change dev dummy0 root hhf quantum 1 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_hhf.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
net/sched/sch_hhf.c +2 -6
@@ -551,7 +551,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt,return err;if (tb[TCA_HHF_QUANTUM ]) - new_quantum =nla_get_u32 (tb[TCA_HHF_QUANTUM ]); + new_quantum =max (256U ,nla_get_u32 (tb[TCA_HHF_QUANTUM ]));if (tb[TCA_HHF_NON_HH_WEIGHT ]) new_hhf_non_hh_weight =nla_get_u32 (tb[TCA_HHF_NON_HH_WEIGHT ]);
@@ -613,7 +613,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,int i; sch->limit =1000 ; - q->quantum =psched_mtu (qdisc_dev (sch)); + q->quantum =clamp_t (u32,psched_mtu (qdisc_dev (sch)),256 ,1 <<20 );get_random_bytes (&q->perturbation,sizeof (q->perturbation));INIT_LIST_HEAD (&q->new_buckets);INIT_LIST_HEAD (&q->old_buckets);
@@ -624,10 +624,6 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt, q->hhf_evict_timeout =HZ ;/* 1 sec */ q->hhf_non_hh_weight =2 ; -if ((int )q->quantum <=0 || - (u64 )q->quantum * q->hhf_non_hh_weight >INT_MAX ) - q->quantum =256 ; -if (opt) {int err =hhf_change (sch, opt, extack);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 308/733 net/sched: dualpi2: clamp psched_mtu at all call sites
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 3c01f1ca5dfc6d6911b0e5b37f5062b1dc451b94 ]
dualpi2_calculate_c_protection(), must_drop(), and get_memory_limit() call psched_mtu() with no clamp. A huge MTU makes (s32)psched_mtu() overflow in the signed multiply for c_protection_init, and 2 * psched_mtu() wraps in get_memory_limit(). With a crafted size table qdisc_pkt_len reaches ~2 GiB, causing a soft lockup / denial of service.
Clamp psched_mtu() to [1, 1<<20] at all three call sites.
Conditions to recreate the bug:
CONFIG_NET_SCH_DUALPI2=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root dualpi2
tc qdisc change dev dummy0 root dualpi2 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_dualpi2.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
net/sched/sch_dualpi2.c +7 -3
@@ -208,9 +208,11 @@ static void dualpi2_reset_c_protection(struct dualpi2_sched_data *q)static void dualpi2_calculate_c_protection (struct Qdisc *sch,struct dualpi2_sched_data *q,u32 wc) { +u32 mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),1 ,1 <<20 ); + q->c_protection_wc = wc; q->c_protection_wl =MAX_WC - wc; - q->c_protection_init = (s32 )psched_mtu (qdisc_dev (sch)) * + q->c_protection_init = (s32 )mtu * ((int )q->c_protection_wc - (int )q->c_protection_wl);dualpi2_reset_c_protection (q); }
@@ -285,8 +287,9 @@ static bool must_drop(struct Qdisc *sch, struct dualpi2_sched_data *q,u64 local_l_prob;bool overload;u32 prob; +u32 mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),1 ,1 <<20 ); -if (sch->qstats.backlog <2 *psched_mtu (qdisc_dev (sch))) +if (sch->qstats.backlog <2 * mtu)return false; prob =READ_ONCE (q->pi2_prob);
@@ -712,7 +715,8 @@ static u32 get_memory_limit(struct Qdisc *sch, u32 limit)/* Apply rule of thumb, i.e., doubling the packet length, * to further include per packet overhead in memory_limit. */ -u64 memlim =mul_u32_u32 (limit,2 *psched_mtu (qdisc_dev (sch))); +u64 memlim =mul_u32_u32 (limit,2 *clamp_t (u32,psched_mtu (qdisc_dev (sch)), +1 ,1 <<20 ));if (upper_32_bits (memlim))return U32_MAX ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 309/733 net/sched: pie: clamp psched_mtu in pie_drop_early
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 54370e44c002770ae61fc889f28f699e91616ffc ]
pie_drop_early() calls psched_mtu() with no clamp. With mtu=0x80000000 the bytemode divide silently zeroes the drop probability, disabling AQM. Clamp to [1, 1<<20].
Conditions to recreate the bug:
CONFIG_NET_SCH_PIE=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root pie
tc qdisc change dev dummy0 root pie stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_pie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_pie.c +1 -1
@@ -35,7 +35,7 @@ bool pie_drop_early(struct Qdisc *sch, struct pie_params *params, {u64 rnd;u64 local_prob = vars->prob; -u32 mtu =psched_mtu (qdisc_dev (sch)); +u32 mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),1 ,1 <<20 );/* If there is still burst allowance left skip random early drop */ if (vars->burst_time >0 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 310/733 net/sched: drr: clamp quantum in change class
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 8382abec0f1568d0a5590d75a3df92f23fcf5196 ]
drr_change_class() rejects explicit quantum==0 but falls back to psched_mtu() with no floor. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 (or a zero psched_mtu on a headerless device) makes the deficit-refill loop spin under the qdisc lock.
Add clamp_t(u32, quantum, 256, 1<<20) after the zero reject and on the fallback path. The explicit-zero reject is preserved.
Conditions to recreate the bug:
CONFIG_NET_SCH_DRR=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root drr
tc class add dev dummy0 parent 1: classid 1:1 drr quantum 1
--- net/sched/sch_drr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/sched/sch_drr.c +2 -1
@@ -82,8 +82,9 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,NL_SET_ERR_MSG (extack,"Specified DRR quantum cannot be zero" );return -EINVAL ; } + quantum =clamp_t (u32, quantum,256 ,1 <<20 ); }else - quantum =psched_mtu (qdisc_dev (sch)); + quantum =clamp_t (u32, (u32 )psched_mtu (qdisc_dev (sch)),256 ,1 <<20 );if (cl !=NULL ) {if (tca[TCA_RATE ]) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 311/733 net/sched: ets: clamp quantum in parse and fallback paths
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 1c38487f46b243bfeefec0c0c86023a3904f2214 ]
ets_qdisc_change() falls back to psched_mtu() with no floor for bands without an explicit quantum. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so a zero psched_mtu on a headerless device makes the deficit-refill loop spin under the qdisc lock.
Move the floor into ets_quantum_parse() so explicitly configured quanta are also clamped to [256, 1<<20], not just the fallback path.
Conditions to recreate the bug:
CONFIG_NET_SCH_ETS=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root ets bands 3 strict 2 quanta 1 1
--- net/sched/sch_ets.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
net/sched/sch_ets.c +5 -7
@@ -83,11 +83,7 @@ static int ets_quantum_parse(struct Qdisc *sch, const struct nlattr *attr,unsigned int *quantum, struct netlink_ext_ack *extack) { - *quantum =nla_get_u32 (attr); -if (!*quantum) { -NL_SET_ERR_MSG (extack,"ETS quantum cannot be zero" ); -return -EINVAL ; - } + *quantum =clamp_t (u32,nla_get_u32 (attr),256 ,1 <<20 );return 0 ; }
@@ -632,11 +628,13 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,return err; }/* If there are more bands than strict + quanta provided, the remaining -* ones are ETS with quantum of MTU. Initialize the missing values here. +* ones are ETS with quantum of max(MTU, 256). Initialize the missing +* values here. */ for (i = nstrict; i < nbands; i++) {if (!quanta[i]) - quanta[i] =psched_mtu (qdisc_dev (sch)); + quanta[i] =clamp_t (u32, (u32 )psched_mtu (qdisc_dev (sch)), +256 ,1 <<20 ); }/* Before commit, make sure we can allocate all new qdiscs */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 312/733 net: macb: fix NULL pointer dereference on unbind with fixed-link
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vineeth Karumanchi <[email protected]>
[ Upstream commit 38b6be101006d3e7af972999f45d4f1e8250587a ]
When the device tree describes a fixed-link and has no "mdio" child node, macb_mii_init() returns early without allocating the MDIO bus, leaving bp->mii_bus as NULL.
Two cleanup paths then dereference this NULL bus:
1. On driver unbind, macb_remove() unconditionally calls mdiobus_unregister(bp->mii_bus), which oopses:
Unable to handle kernel NULL pointer dereference at virtual address 00000000000004a8 pc : mdiobus_unregister+0x14/0xa4 lr : macb_remove+0x38/0xa4 Call trace: mdiobus_unregister+0x14/0xa4 (P) macb_remove+0x38/0xa4 platform_remove+0x20/0x30 device_release_driver_internal+0x1c8/0x224 unbind_store+0xb4/0xbc
2. On the probe error path in macb_probe(), reached when macb_mii_init() has succeeded but a subsequent step fails, the err_out_unregister_mdio label runs the same unconditional cleanup.
mdiobus_unregister() and mdiobus_free() do not guard against a NULL bus, so guard the calls in both macb_remove() and the probe error path.
--- drivers/net/ethernet/cadence/macb_main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +8 -4
@@ -5964,8 +5964,10 @@ static int macb_probe(struct platform_device *pdev)return 0 ; err_out_unregister_mdio: -mdiobus_unregister (bp->mii_bus); -mdiobus_free (bp->mii_bus); +if (bp->mii_bus) { +mdiobus_unregister (bp->mii_bus); +mdiobus_free (bp->mii_bus); + } err_out_phy_exit:phy_exit (bp->phy);
@@ -5993,8 +5995,10 @@ static void macb_remove(struct platform_device *pdev) bp =netdev_priv (netdev);unregister_netdev (netdev);phy_exit (bp->phy); -mdiobus_unregister (bp->mii_bus); -mdiobus_free (bp->mii_bus); +if (bp->mii_bus) { +mdiobus_unregister (bp->mii_bus); +mdiobus_free (bp->mii_bus); + }device_set_wakeup_enable (&bp->pdev->dev,0 );cancel_delayed_work_sync (&bp->tx_lpi_work);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 313/733 bpf: Reject non-scalar bpf_loop iteration counts
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit c3fd8e5fd100f122bad503bdc0e9277219533253 ]
bpf_loop() declares its nr_loops argument as ARG_ANYTHING. Privileged programs may pass pointer values to such arguments, so check_func_arg() lets a pointer-valued R1 reach the helper-specific checks.
Since commit bb124da69c47 ("bpf: keep track of max number of bpf_loop callback iterations"), the verifier marks R1 precise and reads its upper bound to limit callback simulation. Precision backtracking only accepts scalar registers, so passing a pointer instead triggers the "backtracking misuse" verifier warning. Kernels with panic_on_warn enabled subsequently panic.
Introduce ARG_SCALAR for helper arguments that only accept scalar values and use it for bpf_loop() nr_loops. Generic helper argument validation then rejects pointers before loop inlining and precision processing.
--- include/linux/bpf.h | 1 + kernel/bpf/bpf_iter.c | 2 +- kernel/bpf/verifier.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-)
include/linux/bpf.h +1 -0
@@ -894,6 +894,7 @@ enum bpf_arg_type {ARG_PTR_TO_CTX ,/* pointer to context */ ARG_ANYTHING ,/* any (initialized) argument is ok */ +ARG_SCALAR ,/* scalar argument */ ARG_PTR_TO_SPIN_LOCK ,/* pointer to bpf_spin_lock */ ARG_PTR_TO_SOCK_COMMON ,/* pointer to sock_common */ ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */
kernel/bpf/bpf_iter.c +1 -1
@@ -754,7 +754,7 @@ const struct bpf_func_proto bpf_loop_proto = { .func = bpf_loop, .gpl_only = false, .ret_type = RET_INTEGER, - .arg1_type = ARG_ANYTHING, + .arg1_type = ARG_SCALAR, .arg2_type = ARG_PTR_TO_FUNC, .arg3_type = ARG_PTR_TO_STACK_OR_NULL, .arg4_type = ARG_ANYTHING,
kernel/bpf/verifier.c +1 -0
@@ -7877,6 +7877,7 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = { [ARG_CONST_SIZE] = &scalar_types, [ARG_CONST_SIZE_OR_ZERO] = &scalar_types, [ARG_CONST_ALLOC_SIZE_OR_ZERO] = &scalar_types, + [ARG_SCALAR] = &scalar_types, [ARG_CONST_MAP_PTR] = &const_map_ptr_types, [ARG_PTR_TO_CTX] = &context_types, [ARG_PTR_TO_SOCK_COMMON] = &sock_types,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 314/733 ALSA: caiaq: Decoupling ep1_in_urb in caiaq dev
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Edward Adam Davis <[email protected]>
[ Upstream commit 402a9d6aab7ac787ab075adeb562c3db8b8f564b ]
The epq_in_urb object belonging to the caiaq device is coupled within the struct snd_usb_caiaqdev. After usb_submit_urb(epq_in_urb, GFP_KERNEL) executes successfully, epq_in_urb is successfully added to the urbp_list queue of the dummy HCD driver (userspace specifies dummy_hcd as the HCD layer driver for the caiaq USB device).
When init_card() calls snd_usb_caiaq_send_command() which subsequently fails due to a timeout, and proceeds to call snd_card_free() to release the card, the embedded ep1_in_urb object is also freed. When the dummy HCD driver detects that the URB has been unlinked, it returns the URB (by usb_hcd_giveback_urb()), which triggers [1].
Decouple the ep1_in_urb object from the struct snd_usb_caiaqdev and switch to using a pointer instead. Separately allocate and manage the memory for ep1_in_urb to prevent the release of the snd_card memory object from interfering with it.
midi_out_urb has the same issue as ep1_in_urb and is handled in the same way.
[1]
BUG: KASAN: slab-use-after-free in usb_free_urb+0x24/0x120 drivers/usb/core/urb.c:96 Write of size 4 at addr ffff88803cee1050 by task ktimers/1/29 Call Trace: usb_free_urb+0x24/0x120 drivers/usb/core/urb.c:96 dummy_timer+0xaac/0x4d50 drivers/usb/gadget/udc/dummy_hcd.c:2019 __run_hrtimer kernel/time/hrtimer.c:2067 [inline] __hrtimer_run_queues+0x3eb/0xaf0 kernel/time/hrtimer.c:2124 hrtimer_run_softirq+0x1e1/0x2e0 kernel/time/hrtimer.c:2141 Allocated by task 36: snd_card_new+0x7b/0x110 sound/core/init.c:184 create_card sound/usb/caiaq/device.c:429 [inline] snd_probe+0x236/0x1af0 sound/usb/caiaq/device.c:544 Freed by task 36: snd_card_free_when_closed sound/core/init.c:630 [inline] snd_card_free+0x138/0x1d0 sound/core/init.c:662 snd_probe+0x162b/0x1af0 sound/usb/caiaq/device.c:553
--- sound/usb/caiaq/device.c | 36 ++++++++++++++++++++++++------------ sound/usb/caiaq/device.h | 4 ++-- sound/usb/caiaq/midi.c | 6 +++--- 3 files changed, 29 insertions(+), 17 deletions(-)
sound/usb/caiaq/device.c +24 -12
@@ -192,8 +192,8 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)break ; } - cdev->ep1_in_urb.actual_length =0 ; - ret =usb_submit_urb (&cdev->ep1_in_urb,GFP_ATOMIC ); + cdev->ep1_in_urb->actual_length =0 ; + ret =usb_submit_urb (cdev->ep1_in_urb,GFP_ATOMIC );if (ret <0 )dev_err (dev,"unable to submit urb. OOM!?\n" ); }
@@ -408,6 +408,10 @@ static void card_free(struct snd_card *card)#endif snd_usb_caiaq_audio_free (cdev);usb_put_dev (cdev->chip.dev); +usb_free_urb (cdev->ep1_in_urb); + cdev->ep1_in_urb =NULL ; +usb_free_urb (cdev->midi_out_urb); + cdev->midi_out_urb =NULL ; } static int create_card(struct usb_device *usb_dev,
@@ -457,22 +461,30 @@ static int init_card(struct snd_usb_caiaqdev *cdev)return -EIO ; } -usb_init_urb (&cdev->ep1_in_urb); -usb_init_urb (&cdev->midi_out_urb); + cdev->ep1_in_urb =usb_alloc_urb (0 ,GFP_KERNEL ); +if (!cdev->ep1_in_urb) +return -ENOMEM ; -usb_fill_bulk_urb (&cdev->ep1_in_urb, usb_dev, + cdev->midi_out_urb =usb_alloc_urb (0 ,GFP_KERNEL ); +if (!cdev->midi_out_urb) { +usb_free_urb (cdev->ep1_in_urb); + cdev->ep1_in_urb =NULL ; +return -ENOMEM ; + } + +usb_fill_bulk_urb (cdev->ep1_in_urb, usb_dev,usb_rcvbulkpipe (usb_dev,0x1 ), cdev->ep1_in_buf,EP1_BUFSIZE , usb_ep1_command_reply_dispatch, cdev); -usb_fill_bulk_urb (&cdev->midi_out_urb, usb_dev, +usb_fill_bulk_urb (cdev->midi_out_urb, usb_dev,usb_sndbulkpipe (usb_dev,0x1 ), cdev->midi_out_buf,EP1_BUFSIZE , snd_usb_caiaq_midi_output_done, cdev);/* sanity checks of EPs before actually submitting */ -if (usb_urb_ep_type_check (&cdev->ep1_in_urb) || -usb_urb_ep_type_check (&cdev->midi_out_urb)) { +if (usb_urb_ep_type_check (cdev->ep1_in_urb) || +usb_urb_ep_type_check (cdev->midi_out_urb)) {dev_err (dev,"invalid EPs\n" );return -EINVAL ; }
@@ -480,7 +492,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)init_waitqueue_head (&cdev->ep1_wait_queue);init_waitqueue_head (&cdev->prepare_wait_queue); -if (usb_submit_urb (&cdev->ep1_in_urb,GFP_KERNEL ) !=0 ) +if (usb_submit_urb (cdev->ep1_in_urb,GFP_KERNEL ) !=0 )return -EIO ; err =snd_usb_caiaq_send_command (cdev,EP1_CMD_GET_DEVICE_INFO ,NULL ,0 );
@@ -530,7 +542,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)return 0 ; err_kill_urb: -usb_kill_urb (&cdev->ep1_in_urb); +usb_kill_urb (cdev->ep1_in_urb);return err; }
@@ -576,8 +588,8 @@ static void snd_disconnect(struct usb_interface *intf)#endif snd_usb_caiaq_audio_disconnect (cdev); -usb_kill_urb (&cdev->ep1_in_urb); -usb_kill_urb (&cdev->midi_out_urb); +usb_kill_urb (cdev->ep1_in_urb); +usb_kill_urb (cdev->midi_out_urb);snd_card_free_when_closed (card); }
sound/usb/caiaq/device.h +2 -2
@@ -60,8 +60,8 @@ struct snd_usb_caiaq_cb_info;struct snd_usb_caiaqdev {struct snd_usb_audio chip; -struct urb ep1_in_urb; -struct urb midi_out_urb; +struct urb *ep1_in_urb; +struct urb *midi_out_urb;struct urb **data_urbs_in;struct urb **data_urbs_out;struct snd_usb_caiaq_cb_info *data_cb_info;
sound/usb/caiaq/midi.c +3 -3
@@ -43,7 +43,7 @@ static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substre {struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;if (cdev->midi_out_active) { -usb_kill_urb (&cdev->midi_out_urb); +usb_kill_urb (cdev->midi_out_urb); cdev->midi_out_active =0 ; }return 0 ;
@@ -64,9 +64,9 @@ static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *cdev,return ; cdev->midi_out_buf[2 ] = len; - cdev->midi_out_urb.transfer_buffer_length = len+3 ; + cdev->midi_out_urb->transfer_buffer_length = len+3 ; - ret =usb_submit_urb (&cdev->midi_out_urb,GFP_ATOMIC ); + ret =usb_submit_urb (cdev->midi_out_urb,GFP_ATOMIC );if (ret <0 )dev_err (dev, "snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 315/733 erofs: delimit inode_share cache key components
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengyu Zhu <[email protected]>
[ Upstream commit 96bf9831fbf423b8104f7948cd8fe7007ecfb46c ]
Previously, inode_share keys were encoded as follows:
fingerprint || domain_id
It would be better to have a separator between the fingerprint and domain ID so that the fingerprint won't be parsed as part of a domain ID.
Change the key encoding as follows:
domain_id || '\0' || fingerprint
Since domain_id is a NUL-terminated string, this makes the in-memory key indices unambiguous.
--- fs/erofs/xattr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
fs/erofs/xattr.c +5 -4
@@ -620,8 +620,8 @@ int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp, {struct erofs_sb_info *sbi =EROFS_SB (inode->i_sb);struct erofs_xattr_prefix_item *prefix; +int domainlen, valuelen, base_index;const char *infix; -int valuelen, base_index;if (!test_opt (&sbi->opt,INODE_SHARE ))return -EOPNOTSUPP ;
@@ -633,17 +633,18 @@ int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp, valuelen =erofs_getxattr (inode, base_index, infix,NULL ,0 );if (valuelen <=0 || valuelen > (1 << sbi->blkszbits))return -EFSCORRUPTED ; - fp->size = valuelen + (domain_id ?strlen (domain_id) :0 ); + domainlen =strlen (domain_id); + fp->size = domainlen +1 + valuelen; fp->opaque =kmalloc (fp->size,GFP_KERNEL );if (!fp->opaque)return -ENOMEM ; +memcpy (fp->opaque, domain_id, domainlen +1 );if (valuelen !=erofs_getxattr (inode, base_index, infix, - fp->opaque, valuelen)) { + fp->opaque + domainlen +1 , valuelen)) {kfree (fp->opaque); fp->opaque =NULL ;return -EFSCORRUPTED ; } -memcpy (fp->opaque + valuelen, domain_id, fp->size - valuelen); return 0; }#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 316/733 iommu/riscv: Add command queue lock
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fangyu Yu <[email protected]>
[ Upstream commit d5bcf9ccaa357396089bbfa47fc82b093f109b1e ]
Add a raw spinlock to the RISC-V IOMMU queue state so command queue publishing can be serialized by a later change.
--- drivers/iommu/riscv/iommu.c | 1 + drivers/iommu/riscv/iommu.h | 2 ++ 2 files changed, 3 insertions(+)
drivers/iommu/riscv/iommu.c +1 -0
@@ -1560,6 +1560,7 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu)int rc;RISCV_IOMMU_QUEUE_INIT (&iommu->cmdq,CQ ); +raw_spin_lock_init (&iommu->cmdq.lock);RISCV_IOMMU_QUEUE_INIT (&iommu->fltq,FQ ); rc =riscv_iommu_init_check (iommu);
drivers/iommu/riscv/iommu.h +2 -0
@@ -12,6 +12,7 @@#define _RISCV_IOMMU_H_#include <linux/iommu.h> +#include <linux/spinlock.h> #include <linux/types.h> #include <linux/iopoll.h>
@@ -23,6 +24,7 @@ struct riscv_iommu_queue {atomic_t prod;/* unbounded producer allocation index */ atomic_t head;/* unbounded shadow ring buffer consumer index */ atomic_t tail;/* unbounded shadow ring buffer producer index */ +raw_spinlock_t lock;/* serialize queue publishing */ unsigned int mask;/* index mask, queue length - 1 */ unsigned int irq;/* allocated interrupt number */ struct riscv_iommu_device *iommu;/* iommu device handling the queue when active */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 317/733 iommu/riscv: Serialize command queue publishing
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fangyu Yu <[email protected]>
[ Upstream commit ca58afa40946acd252a50fa4d4a86f15847a3d7d ]
Serialize command queue publishing so software producer state advances only after a command is written and the hardware tail is updated. Wait for hardware consumption outside the queue lock when the command queue is full so other CPUs are not blocked behind a long poll.
--- drivers/iommu/riscv/iommu.c | 100 +++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 42 deletions(-)
drivers/iommu/riscv/iommu.c +58 -42
@@ -382,77 +382,93 @@ static int riscv_iommu_queue_wait(struct riscv_iommu_queue *queue, (int)(cons - index) > 0, 0, timeout_us); } -/* Enqueue an entry and wait to be processed if timeout_us > 0 -* -* Error handling for IOMMU hardware not responding in reasonable time -* will be added as separate patch series along with other RAS features. -* For now, only report hardware failure and continue. -*/ +static int riscv_iommu_queue_wait_for_space (struct riscv_iommu_queue *queue, +unsigned int last) +{ +unsigned int head; +unsigned int tail; +unsigned int hw_head; +unsigned long flags; +int ret; + + ret =riscv_iommu_readl_timeout (queue->iommu,Q_HEAD (queue), hw_head, + !(hw_head & ~queue->mask) && hw_head != last, +0 ,RISCV_IOMMU_QUEUE_TIMEOUT ); +if (ret) +return ret; + +raw_spin_lock_irqsave (&queue->lock, flags); + head =atomic_read (&queue->head); + tail =atomic_read (&queue->tail); +if ((tail - head) >= queue->mask) { + last =Q_ITEM (queue, head); +/* +* Re-read hw_head under the lock so that it is consistent with +* the freshly computed 'last'. Using the pre-lock snapshot +* could produce a stale value that wraps around relative to the +* new 'last', advancing the shadow head past entries that have +* not yet been consumed by the hardware. +*/ + hw_head =riscv_iommu_readl (queue->iommu,Q_HEAD (queue)); +if (!(hw_head & ~queue->mask) && hw_head != last) +atomic_add ((hw_head - last) & queue->mask, &queue->head); + } +raw_spin_unlock_irqrestore (&queue->lock, flags); + +return 0 ; +} + +/* Enqueue an entry and publish it to the hardware queue. */ static unsigned int riscv_iommu_queue_send (struct riscv_iommu_queue *queue,void *entry,size_t entry_size) {unsigned int prod;unsigned int head; -unsigned int tail;unsigned long flags; +int ret; -/* Do not preempt submission flow. */ -local_irq_save (flags); +/* 1. Wait for space availability and reserve the next slot. */ +for (;;) { +raw_spin_lock_irqsave (&queue->lock, flags); -/* 1. Allocate some space in the queue */ - prod =atomic_inc_return (&queue->prod) -1 ; - head =atomic_read (&queue->head); + prod =atomic_read (&queue->tail); + head =atomic_read (&queue->head); -/* 2. Wait for space availability. */ -if ((prod - head) > queue->mask) { -if (readx_poll_timeout (atomic_read, &queue->head, - head, (prod - head) < queue->mask, -0 ,RISCV_IOMMU_QUEUE_TIMEOUT )) - goto err_busy; - }else if ((prod - head) == queue->mask) { -const unsigned int last =Q_ITEM (queue, head); +if ((prod - head) < queue->mask) +break ; + + head =Q_ITEM (queue, head); +raw_spin_unlock_irqrestore (&queue->lock, flags); -if (riscv_iommu_readl_timeout (queue->iommu,Q_HEAD (queue), head, - !(head & ~queue->mask) && head != last, -0 ,RISCV_IOMMU_QUEUE_TIMEOUT )) + ret =riscv_iommu_queue_wait_for_space (queue, head); +if (ret) goto err_busy; -atomic_add ((head - last) & queue->mask, &queue->head); } -/* 3. Store entry in the ring buffer */ +/* 2. Store entry in the ring buffer. */ memcpy (queue->base +Q_ITEM (queue, prod) * entry_size, entry, entry_size); -/* 4. Wait for all previous entries to be ready */ -if (readx_poll_timeout (atomic_read, &queue->tail, tail, prod == tail, -0 ,RISCV_IOMMU_QUEUE_TIMEOUT )) - goto err_busy; - -/* -* 5. Make sure the ring buffer update (whether in normal or I/O memory) is -* completed and visible before signaling the tail doorbell to fetch -* the next command. 'fence ow, ow' -*/ +/* 3. Make sure the entry is visible before updating the queue tail. */ dma_wmb ();riscv_iommu_writel (queue->iommu,Q_TAIL (queue),Q_ITEM (queue, prod +1 ));/* -* 6. Make sure the doorbell write to the device has finished before updating -* the shadow tail index in normal memory. 'fence o, w' +* 4. Make sure the doorbell write to the device has finished before +* updating the shadow tail index in normal memory. 'fence o, w' */ #ifdef CONFIG_MMIOWB mmiowb ();#endif -atomic_inc (&queue->tail); +atomic_set (&queue->tail, prod +1 ); +atomic_set (&queue->prod, prod +1 ); -/* 7. Complete submission and restore local interrupts */ -local_irq_restore (flags); +raw_spin_unlock_irqrestore (&queue->lock, flags);return prod; err_busy: -local_irq_restore (flags); +/* Report the failure and continue; full RAS recovery is not implemented. */ dev_err_once (queue->iommu->dev,"Hardware error: command enqueue failed\n" ); -return prod; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 318/733 iommu/riscv: Avoid waiting on failed command enqueue
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fangyu Yu <[email protected]>
[ Upstream commit 4c50bec3d54288230aafb7fe3d2930d42beb14fd ]
Do not wait for IOFENCE.C completion when the command failed to enter the queue. The command was not published to hardware, so waiting for its producer index can only report a misleading execution timeout.
--- drivers/iommu/riscv/iommu.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/iommu/riscv/iommu.c +13 -7
@@ -419,8 +419,9 @@ static int riscv_iommu_queue_wait_for_space(struct riscv_iommu_queue *queue, }/* Enqueue an entry and publish it to the hardware queue. */ -static unsigned int riscv_iommu_queue_send (struct riscv_iommu_queue *queue, -void *entry,size_t entry_size) +static int riscv_iommu_queue_send (struct riscv_iommu_queue *queue, +void *entry,size_t entry_size, +unsigned int *out_prod) {unsigned int prod;unsigned int head;
@@ -462,14 +463,16 @@ static unsigned int riscv_iommu_queue_send(struct riscv_iommu_queue *queue,atomic_set (&queue->tail, prod +1 );atomic_set (&queue->prod, prod +1 ); -raw_spin_unlock_irqrestore (&queue->lock, flags); +if (out_prod) + *out_prod = prod; -return prod; +raw_spin_unlock_irqrestore (&queue->lock, flags); +return 0 ; err_busy:/* Report the failure and continue; full RAS recovery is not implemented. */ dev_err_once (queue->iommu->dev,"Hardware error: command enqueue failed\n" ); -return prod; +return ret; }/*
@@ -508,7 +511,7 @@ static irqreturn_t riscv_iommu_cmdq_process(int irq, void *data)static void riscv_iommu_cmd_send (struct riscv_iommu_device *iommu,struct riscv_iommu_command *cmd) { -riscv_iommu_queue_send (&iommu->cmdq, cmd,sizeof (*cmd)); +riscv_iommu_queue_send (&iommu->cmdq, cmd,sizeof (*cmd),NULL ); }/* Send IOFENCE.C command and wait for all scheduled commands to complete. */
@@ -517,9 +520,12 @@ static void riscv_iommu_cmd_sync(struct riscv_iommu_device *iommu, {struct riscv_iommu_command cmd;unsigned int prod; +int ret;riscv_iommu_cmd_iofence (&cmd); - prod =riscv_iommu_queue_send (&iommu->cmdq, &cmd,sizeof (cmd)); + ret =riscv_iommu_queue_send (&iommu->cmdq, &cmd,sizeof (cmd), &prod); +if (ret) +return ;if (!timeout_us)return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 319/733 iommu/amd: Do not reallocate GA log buffers on resume
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 00a7dd64888d6dd72110b40e2824a088cf7b7386 ]
Commit c5e1a1eb9279 ("iommu/amd: Simplify and Consolidate Virtual APIC (AVIC) Enablement") moved the GA log allocation from iommu_init_pci() to enable_iommus_vapic(), which is called on every resume.
iommu_init_ga_log() assigns iommu->ga_log and iommu->ga_log_tail unconditionally. Each resume therefore replaces the boot-time pointers and leaks both old allocations. The function also uses GFP_KERNEL from a syscore resume callback, where interrupts are disabled and the non-boot CPUs are offline.
Return early if both buffers are already allocated. Clear the pointers in free_ga_log() so a partial allocation failure cannot leave ga_log dangling.
--- drivers/iommu/amd/init.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/iommu/amd/init.c +5 -0
@@ -906,7 +906,9 @@ static void free_ga_log(struct amd_iommu *iommu) {#ifdef CONFIG_IRQ_REMAP iommu_free_pages (iommu->ga_log); + iommu->ga_log =NULL ;iommu_free_pages (iommu->ga_log_tail); + iommu->ga_log_tail =NULL ;#endif }
@@ -953,6 +955,9 @@ static int iommu_init_ga_log(struct amd_iommu *iommu)if (WARN_ON_ONCE (!AMD_IOMMU_GUEST_IR_VAPIC (amd_iommu_guest_ir)))return -EINVAL ; +if (iommu->ga_log && iommu->ga_log_tail) +return 0 ; + iommu->ga_log =iommu_alloc_pages_node_sz (nid,GFP_KERNEL ,GA_LOG_SIZE );if (!iommu->ga_log) goto err_out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 320/733 iommu/amd: Fix premature break in init_iommu_one() again
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasant Hegde <[email protected]>
[ Upstream commit eb29b7bbc8ba28bbb0b9fdd655e931e1d1fa625c ]
Commit 283d245468a2 ("iommu/amd: Fix premature break in init_iommu_one()") unintentionally broke older platforms - such as the ASRockRack B550D4-4L - where the BIOS advertises incorrect IOMMU features.
Move the HATDis check ahead of the GASup check, and re-introduce the break inside the GASup check to restore correct behavior on affected platforms.
This is a short-term fix to resolve the regression. Longer term, we should rework how EFRs are tracked and prioritize the MMIO-advertised EFR over the one reported via IVRS. That requires more extensive changes and will be addressed separately.
--- drivers/iommu/amd/init.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/iommu/amd/init.c +6 -5
@@ -1919,19 +1919,20 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h,else iommu->mmio_phys_end =MMIO_CNTR_CONF_OFFSET ; +if (h->efr_attr &BIT (IOMMU_IVHD_ATTR_HATDIS_SHIFT )) { +pr_warn_once ("Host Address Translation is not supported.\n" ); + amd_iommu_hatdis = true; + } +/* XT and GAM require GA mode. */ if ((h->efr_reg & (0x1 <<IOMMU_EFR_GASUP_SHIFT )) ==0 ) { amd_iommu_guest_ir =AMD_IOMMU_GUEST_IR_LEGACY ; +break ; }else {if (h->efr_reg &BIT (IOMMU_EFR_XTSUP_SHIFT )) amd_iommu_xt_mode =IRQ_REMAP_X2APIC_MODE ; } -if (h->efr_attr &BIT (IOMMU_IVHD_ATTR_HATDIS_SHIFT )) { -pr_warn_once ("Host Address Translation is not supported.\n" ); - amd_iommu_hatdis = true; - } -early_iommu_features_init (iommu, h);break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 321/733 iommu/amd: Fix ineffective error check in nested domain allocation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hemanth Selam <[email protected]>
[ Upstream commit fa5c0827f0b7bac6d0a188f10118151769ae68fd ]
amd_iommu_pdom_id_alloc() returns an int: a domain ID on success, or the negative errno from ida_alloc_range() when the ID space is exhausted or memory is short. amd_iommu_alloc_domain_nested() stores that return value in gdom_info->hdom_id, which is a u32, and only then tests it:
gdom_info->hdom_id = amd_iommu_pdom_id_alloc();
if (gdom_info->hdom_id <= 0) {The assignment discards the sign, so -ENOSPC becomes 0xffffffe4 and the test never fires. The nested domain is then set up with a host domain ID that was never allocated, instead of the allocation failing with -ENOSPC.
Keep the value in an int, test it there, and store it only once it is known to be valid, which is what the other amd_iommu_pdom_id_alloc() callers already do.
--- drivers/iommu/amd/nested.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/iommu/amd/nested.c +4 -3
@@ -96,7 +96,7 @@ struct iommu_domain * amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, const struct iommu_user_data *user_data) { - int ret; + int ret, hdom_id;unsigned long irqflags;struct nested_domain *ndom;struct guest_domain_mapping_info *gdom_info;
@@ -161,8 +161,8 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, }/* The gDomID does not exist. We allocate new hdom_id */ - gdom_info->hdom_id =amd_iommu_pdom_id_alloc (); -if (gdom_info->hdom_id <=0 ) { + hdom_id =amd_iommu_pdom_id_alloc (); +if (hdom_id <=0 ) {__xa_cmpxchg (&aviommu->gdomid_array, ndom->gdom_id, gdom_info,NULL ,GFP_ATOMIC );xa_unlock_irqrestore (&aviommu->gdomid_array, irqflags);
@@ -170,6 +170,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, goto out_err_gdom_info; } + gdom_info->hdom_id = hdom_id; ndom->gdom_info = gdom_info;refcount_set (&gdom_info->users,1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 322/733 hwmon: (ltc4282) Make sure clk_init_data is fully initialized
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geert Uytterhoeven <[email protected]>
[ Upstream commit e317326d1755ea054b98e7a4833b929157461c35 ]
The clk_init_data structure contains several mutually-exclusive members for different methods to specify the possible parents of a clock, prompting drivers to initialize only the members they need. However, not initializing all members may cause subtle issues, which are only exposed when CONFIG_INIT_STACK_ALL_PATTERN or CONFIG_INIT_STACK_NONE is enabled.
ltc428_clk_provider_setup() does not fill in any parent clocks, and assumes that init.num_parents is NULL. However, the latter in uninitialized, and thus may cause a crash.
Make sure all members are fully initialized, to fix such bugs, and to avoid future breakage when converting drivers to a different method for specifying the parents.
--- drivers/hwmon/ltc4282.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/ltc4282.c +1 -1
@@ -1106,7 +1106,7 @@ static const struct clk_ops ltc4282_ops = {static int ltc428_clk_provider_setup (struct ltc4282_state *st,struct device *dev) { -struct clk_init_data init; +struct clk_init_data init = {};int ret;if (!IS_ENABLED (CONFIG_COMMON_CLK ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 323/733 Documentation/hwmon: Document hwmon_notify_event()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit b4fffa75c1d6f87e6dc6191dec900f2b5bd23a1c ]
The hwmon core provides hwmon_notify_event() for drivers to report events such as alarm or fault conditions to userspace via sysfs notifications and uevents, as well as to the thermal subsystem for temperature sensors. However, this function is not documented in the hwmon kernel API guide.
Add the function prototype and description of hwmon_notify_event() to Documentation/hwmon/hwmon-kernel-api.rst.
--- Documentation/hwmon/hwmon-kernel-api.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
Documentation/hwmon/hwmon-kernel-api.rst +15 -0
@@ -42,6 +42,9 @@ register/unregister functions:: char *devm_hwmon_sanitize_name(struct device *dev, const char *name); + int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel); + void hwmon_lock(struct device *dev); void hwmon_unlock(struct device *dev);
@@ -90,6 +93,18 @@ implemented in the driver, or debugfs functions, hwmon_lock() and hwmon_unlock() can be used to ensure that calls to those functions are serialized. Those functions also support guard() and scoped_guard() variants. +Drivers can call hwmon_notify_event() to notify userspace and the thermal +subsystem when a hardware monitoring event (such as an alarm or a fault +condition) occurs or clears. The parameters are the hwmon device, the sensor +type, the attribute identifier associated with the event (such as +hwmon_temp_max_alarm or hwmon_fan_fault), and the sensor channel number. +hwmon_notify_event() generates a sysfs event (calling sysfs_notify()) and a +udev event with the attribute name passed in the NAME environment property +(e.g., "NAME=temp1_max_alarm"). If the event is for a temperature sensor and +the sensor is attached to a thermal zone, it also notifies the thermal +subsystem to update the thermal zone. hwmon_notify_event() returns 0 on +success or a negative error code on failure. + Using devm_hwmon_device_register_with_info() --------------------------------------------
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 324/733 hwmon: Fix potential UAF in pec_store
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 354ccc99b2dc8ba0cf6d4de34e520bcf6ecca5c2 ]
Sashiko reports:
In pec_store(), a guard(mutex)(&hwdev->lock) is taken. If the chip write operation returns an error other than -EOPNOTSUPP, the code jumps to the put label, which calls put_device(hdev). If this drops the final reference, the device is freed. When the function then returns, the guard cleanup function runs and attempts to unlock the freed mutex.
Use scoped_guard() instead of guard() to avoid the problem.
--- drivers/hwmon/hwmon.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
drivers/hwmon/hwmon.c +10 -11
@@ -371,18 +371,17 @@ static ssize_t pec_store(struct device *dev, struct device_attribute *devattr,* handling is not required. */ hwdev =to_hwmon_device (hdev); -guard (mutex)(&hwdev->lock); -if (hwdev->chip->ops->write) { - err = hwdev->chip->ops->write (hdev, hwmon_chip, hwmon_chip_pec,0 , val); -if (err && err != -EOPNOTSUPP ) - goto put; + scoped_guard(mutex, &hwdev->lock) { +if (hwdev->chip->ops->write) { + err = hwdev->chip->ops->write (hdev, hwmon_chip, hwmon_chip_pec,0 , val); +if (err && err != -EOPNOTSUPP ) + goto put; + } +if (!val) + client->flags &= ~I2C_CLIENT_PEC ; +else + client->flags |=I2C_CLIENT_PEC ; } - -if (!val) - client->flags &= ~I2C_CLIENT_PEC ; -else - client->flags |=I2C_CLIENT_PEC ; - err = count; put:put_device (hdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 325/733 hwmon: (ina2xx) Acquire hwmon_lock in shunt_resistor_show()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit 8afc94bfb0ffdfc4a168081785820aa4818d1d23 ]
shunt_resistor_store() currently acquires hwmon_lock to set data->rshunt, but the corresponding access in shunt_resistor_show() is unprotected. Acquire the lock in shunt_resistor_show() as well to ensure proper synchronization.
--- drivers/hwmon/ina2xx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/hwmon/ina2xx.c +5 -1
@@ -868,8 +868,12 @@ static ssize_t shunt_resistor_show(struct device *dev, struct device_attribute *da, char *buf) { struct ina2xx_data *data = dev_get_drvdata(dev); +long rshunt; -return sysfs_emit (buf,"%li\n" , data->rshunt); + scoped_guard(hwmon_lock, dev) { + rshunt = data->rshunt; + } +return sysfs_emit (buf,"%li\n" , rshunt); } static ssize_t shunt_resistor_store(struct device *dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 326/733 hwmon: (ina2xx) Parameterize ina2xx_data in ina226_alert_read()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit 3d44ab826e0244a8b9eaf0f1f604f4cb8b890325 ]
Mirror ina226_alert_limit_read/write and use struct ina2xx_data instead of struct regmap in ina226_alert_read's parameters.
Stable-dep-of: 35760f5efd7b ("hwmon: (ina2xx) Decouple in0 and curr1 alarms")
--- drivers/hwmon/ina2xx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/hwmon/ina2xx.c +7 -7
@@ -483,12 +483,12 @@ static int ina2xx_chip_read(struct device *dev, u32 attr, long *val)return 0 ; } -static int ina226_alert_read (struct regmap *regmap,u32 mask,long *val) +static int ina226_alert_read (struct ina2xx_data *data,u32 mask,long *val) {unsigned int regval;int ret; - ret =regmap_read_bypassed (regmap,INA226_MASK_ENABLE , ®val); + ret =regmap_read_bypassed (data->regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret;
@@ -523,9 +523,9 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val)return ina226_alert_limit_read (data, over_voltage_mask, voltage_reg, val);case hwmon_in_lcrit_alarm: -return ina226_alert_read (regmap, under_voltage_mask, val); +return ina226_alert_read (data, under_voltage_mask, val);case hwmon_in_crit_alarm: -return ina226_alert_read (regmap, over_voltage_mask, val); +return ina226_alert_read (data, over_voltage_mask, val);default :return -EOPNOTSUPP ; }
@@ -582,7 +582,7 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)return ina226_alert_limit_read (data,INA226_POWER_OVER_LIMIT_MASK ,INA2XX_POWER , val);case hwmon_power_crit_alarm: -return ina226_alert_read (data->regmap,INA226_POWER_OVER_LIMIT_MASK , val); +return ina226_alert_read (data,INA226_POWER_OVER_LIMIT_MASK , val);default :return -EOPNOTSUPP ; }
@@ -624,9 +624,9 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val)return ina226_alert_limit_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK ,INA2XX_CURRENT , val);case hwmon_curr_lcrit_alarm: -return ina226_alert_read (regmap,INA226_SHUNT_UNDER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , val);case hwmon_curr_crit_alarm: -return ina226_alert_read (regmap,INA226_SHUNT_OVER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK , val);default :return -EOPNOTSUPP ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 327/733 hwmon: (ina2xx) Replace masks with enum in alert functions
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit e92b9208415a90e9cc1d923c5d8c058dde77be68 ]
Instead of passing an explicit mask to alert/limit functions like ina226_alert_read(), introduce an enum ina2xx_alert_type that can be converted to a mask internally. This semantically separates current from shunt voltage in helpers that use function masks, which previously saw the same mask for the two functions.
Stable-dep-of: 35760f5efd7b ("hwmon: (ina2xx) Decouple in0 and curr1 alarms")
--- drivers/hwmon/ina2xx.c | 90 +++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 23 deletions(-)
drivers/hwmon/ina2xx.c +67 -23
@@ -128,6 +128,17 @@ enum ina2xx_ids {sy24655 }; +enum ina2xx_alert_type { +INA2XX_ALERT_NONE , +INA2XX_ALERT_CURRENT_LOW , +INA2XX_ALERT_CURRENT_HIGH , +INA2XX_ALERT_POWER_HIGH , +INA2XX_ALERT_BUS_VOLTAGE_LOW , +INA2XX_ALERT_BUS_VOLTAGE_HIGH , +INA2XX_ALERT_SHUNT_VOLTAGE_LOW , +INA2XX_ALERT_SHUNT_VOLTAGE_HIGH , +}; +struct ina2xx_config {u16 config_default;bool has_alerts;/* chip supports alerts and limits */
@@ -413,16 +424,43 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) } } -static int ina226_alert_limit_read (struct ina2xx_data *data,u32 mask,int reg,long *val) +static u32 ina2xx_alert_type_to_mask (enum ina2xx_alert_type alert) +{ +switch (alert) { +case INA2XX_ALERT_CURRENT_LOW : +case INA2XX_ALERT_SHUNT_VOLTAGE_LOW : +return INA226_SHUNT_UNDER_VOLTAGE_MASK ; +case INA2XX_ALERT_CURRENT_HIGH : +case INA2XX_ALERT_SHUNT_VOLTAGE_HIGH : +return INA226_SHUNT_OVER_VOLTAGE_MASK ; +case INA2XX_ALERT_BUS_VOLTAGE_LOW : +return INA226_BUS_UNDER_VOLTAGE_MASK ; +case INA2XX_ALERT_BUS_VOLTAGE_HIGH : +return INA226_BUS_OVER_VOLTAGE_MASK ; +case INA2XX_ALERT_POWER_HIGH : +return INA226_POWER_OVER_LIMIT_MASK ; +case INA2XX_ALERT_NONE : +return 0 ; +default : +/* programmer error */ +WARN_ON_ONCE (1 ); +return 0 ; + } +} + +static int ina226_alert_limit_read (struct ina2xx_data *data,enum ina2xx_alert_type alert, +int reg,long *val) {struct regmap *regmap = data->regmap;int regval; +u32 mask;int ret; ret =regmap_read (regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret; + mask =ina2xx_alert_type_to_mask (alert);if (regval & mask) { ret =regmap_read (regmap,INA226_ALERT_LIMIT , ®val);if (ret)
@@ -434,9 +472,11 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg,return 0 ; } -static int ina226_alert_limit_write (struct ina2xx_data *data,u32 mask,int reg,long val) +static int ina226_alert_limit_write (struct ina2xx_data *data,enum ina2xx_alert_type alert, +int reg,long val) {struct regmap *regmap = data->regmap; +u32 mask;int ret;if (val <0 )
@@ -457,9 +497,11 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg,if (ret <0 )return ret; -if (val) +if (val) { + mask =ina2xx_alert_type_to_mask (alert);return regmap_update_bits (regmap,INA226_MASK_ENABLE ,INA226_ALERT_CONFIG_MASK , mask); + }return 0 ; }
@@ -483,15 +525,17 @@ static int ina2xx_chip_read(struct device *dev, u32 attr, long *val)return 0 ; } -static int ina226_alert_read (struct ina2xx_data *data,u32 mask,long *val) +static int ina226_alert_read (struct ina2xx_data *data,enum ina2xx_alert_type alert,long *val) {unsigned int regval; +u32 mask;int ret; ret =regmap_read_bypassed (data->regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret; + mask =ina2xx_alert_type_to_mask (alert); *val = (regval & mask) && (regval &INA226_ALERT_FUNCTION_FLAG );return 0 ;
@@ -500,10 +544,10 @@ static int ina226_alert_read(struct ina2xx_data *data, u32 mask, long *val)static int ina2xx_in_read (struct device *dev,u32 attr,int channel,long *val) {int voltage_reg = channel ?INA2XX_BUS_VOLTAGE :INA2XX_SHUNT_VOLTAGE ; -u32 under_voltage_mask = channel ?INA226_BUS_UNDER_VOLTAGE_MASK - :INA226_SHUNT_UNDER_VOLTAGE_MASK ; -u32 over_voltage_mask = channel ?INA226_BUS_OVER_VOLTAGE_MASK - :INA226_SHUNT_OVER_VOLTAGE_MASK ; +enum ina2xx_alert_type under_voltage_alert = channel ?INA2XX_ALERT_BUS_VOLTAGE_LOW + :INA2XX_ALERT_SHUNT_VOLTAGE_LOW ; +enum ina2xx_alert_type over_voltage_alert = channel ?INA2XX_ALERT_BUS_VOLTAGE_HIGH + :INA2XX_ALERT_SHUNT_VOLTAGE_HIGH ;struct ina2xx_data *data =dev_get_drvdata (dev);struct regmap *regmap = data->regmap;unsigned int regval;
@@ -517,15 +561,15 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val) *val =ina2xx_get_value (data, voltage_reg, regval);break ;case hwmon_in_lcrit: -return ina226_alert_limit_read (data, under_voltage_mask, +return ina226_alert_limit_read (data, under_voltage_alert, voltage_reg, val);case hwmon_in_crit: -return ina226_alert_limit_read (data, over_voltage_mask, +return ina226_alert_limit_read (data, over_voltage_alert, voltage_reg, val);case hwmon_in_lcrit_alarm: -return ina226_alert_read (data, under_voltage_mask, val); +return ina226_alert_read (data, under_voltage_alert, val);case hwmon_in_crit_alarm: -return ina226_alert_read (data, over_voltage_mask, val); +return ina226_alert_read (data, over_voltage_alert, val);default :return -EOPNOTSUPP ; }
@@ -579,10 +623,10 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)case hwmon_power_average:return sy24655_average_power_read (data,SY24655_EIN , val);case hwmon_power_crit: -return ina226_alert_limit_read (data,INA226_POWER_OVER_LIMIT_MASK , +return ina226_alert_limit_read (data,INA2XX_ALERT_POWER_HIGH ,INA2XX_POWER , val);case hwmon_power_crit_alarm: -return ina226_alert_read (data,INA226_POWER_OVER_LIMIT_MASK , val); +return ina226_alert_read (data,INA2XX_ALERT_POWER_HIGH , val);default :return -EOPNOTSUPP ; }
@@ -618,15 +662,15 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val) *val =ina2xx_get_value (data,INA2XX_CURRENT , regval);return 0 ;case hwmon_curr_lcrit: -return ina226_alert_limit_read (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , +return ina226_alert_limit_read (data,INA2XX_ALERT_CURRENT_LOW ,INA2XX_CURRENT , val);case hwmon_curr_crit: -return ina226_alert_limit_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK , +return ina226_alert_limit_read (data,INA2XX_ALERT_CURRENT_HIGH ,INA2XX_CURRENT , val);case hwmon_curr_lcrit_alarm: -return ina226_alert_read (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA2XX_ALERT_CURRENT_LOW , val);case hwmon_curr_crit_alarm: -return ina226_alert_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA2XX_ALERT_CURRENT_HIGH , val);default :return -EOPNOTSUPP ; }
@@ -670,12 +714,12 @@ static int ina2xx_in_write(struct device *dev, u32 attr, int channel, long val)switch (attr) {case hwmon_in_lcrit:return ina226_alert_limit_write (data, - channel ?INA226_BUS_UNDER_VOLTAGE_MASK :INA226_SHUNT_UNDER_VOLTAGE_MASK , + channel ?INA2XX_ALERT_BUS_VOLTAGE_LOW :INA2XX_ALERT_SHUNT_VOLTAGE_LOW , channel ?INA2XX_BUS_VOLTAGE :INA2XX_SHUNT_VOLTAGE , val);case hwmon_in_crit:return ina226_alert_limit_write (data, - channel ?INA226_BUS_OVER_VOLTAGE_MASK :INA226_SHUNT_OVER_VOLTAGE_MASK , + channel ?INA2XX_ALERT_BUS_VOLTAGE_HIGH :INA2XX_ALERT_SHUNT_VOLTAGE_HIGH , channel ?INA2XX_BUS_VOLTAGE :INA2XX_SHUNT_VOLTAGE , val);default :
@@ -690,7 +734,7 @@ static int ina2xx_power_write(struct device *dev, u32 attr, long val)switch (attr) {case hwmon_power_crit: -return ina226_alert_limit_write (data,INA226_POWER_OVER_LIMIT_MASK , +return ina226_alert_limit_write (data,INA2XX_ALERT_POWER_HIGH ,INA2XX_POWER , val);default :return -EOPNOTSUPP ;
@@ -704,10 +748,10 @@ static int ina2xx_curr_write(struct device *dev, u32 attr, long val)switch (attr) {case hwmon_curr_lcrit: -return ina226_alert_limit_write (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , +return ina226_alert_limit_write (data,INA2XX_ALERT_CURRENT_LOW ,INA2XX_CURRENT , val);case hwmon_curr_crit: -return ina226_alert_limit_write (data,INA226_SHUNT_OVER_VOLTAGE_MASK , +return ina226_alert_limit_write (data,INA2XX_ALERT_CURRENT_HIGH ,INA2XX_CURRENT , val);default :return -EOPNOTSUPP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 328/733 hwmon: (ina2xx) Decouple in0 and curr1 alarms
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit 35760f5efd7bfa7a44a3831f47e19fe9cbafc905 ]
INA2XX current limits are converted into shunt voltage limits internally using the shunt resistor value. Once a current limit's corresponding voltage limit is written to the hardware, shunt voltage and current alarms are indistinguishable from each other.
This causes two issues:
1. in0/curr1 alarms may be unintentionally cleared by reading from the opposite input's alarm.
2. When a limit for either in0 (shunt voltage) or curr1 (current) is set, both of their alarms are triggered, and both of their limits read nonzero.
An example of this behavior on an INA231:
# cd /sys/class/hwmon/hwmon0
# head {curr1,in0}_input
==> curr1_input <==
1713 ==> in0_input <==
2
# echo 1800 >curr1_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
1
==> in0_lcrit_alarm <==
0
# head {in0,curr1}_lcrit_alarm
==> in0_lcrit_alarm <==
1
==> curr1_lcrit_alarm <==
0
# head {in0,curr1}_lcrit_alarm
==> in0_lcrit_alarm <==
1
==> curr1_lcrit_alarm <==
1This is because curr1 uses the same underlying masks (INA226_SHUNT_*_VOLTAGE_MASK) as in0 on the hardware. As a result, ina2xx_{curr,in}_read() both read the shunt voltage alarms/limits without considering whether the voltage or current is currently set.
To fix this, track the active alarm type in ina2xx_data and guard alarm/limit reads with a check that returns zero if the active alarm is for a different type. The new field is initialized based on the MASK_ENABLE register's set function, assuming voltage instead of current when the shunt voltage mask is set.
After this fix, the alarms only read back 1 if their corresponding limit is set:
# echo 0 >curr1_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
0 ==> in0_lcrit_alarm <==
0
# echo 9999 >curr1_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
1
==> in0_lcrit_alarm <==
0
# echo 9999 >in0_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
0
==> in0_lcrit_alarm <==
1--- drivers/hwmon/ina2xx.c | 65 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-)
drivers/hwmon/ina2xx.c +63 -2
@@ -8,6 +8,7 @@*/ #include <linux/bitfield.h> +#include <linux/bitops.h> #include <linux/bits.h> #include <linux/delay.h> #include <linux/device.h>
@@ -158,6 +159,7 @@ struct ina2xx_data {const struct ina2xx_config *config;enum ina2xx_ids chip; +enum ina2xx_alert_type active_alert;long rshunt;long current_lsb_uA;long power_lsb_uW;
@@ -448,6 +450,35 @@ static u32 ina2xx_alert_type_to_mask(enum ina2xx_alert_type alert) } } +static enum ina2xx_alert_type ina2xx_mask_to_alert_type (u32 mask) +{ +int top_bit =fls (mask &INA226_ALERT_CONFIG_MASK ); + +if (!top_bit) +return INA2XX_ALERT_NONE ; + +/* +* Multiple bits may be set, with the highest-set function taking +* precedence according to the datasheet. Shunt voltage masks are +* assumed to map to voltage monitoring rather than current monitoring, +* since the latter isn't directly implemented in the hardware. +*/ +switch (BIT (top_bit -1 )) { +case INA226_SHUNT_OVER_VOLTAGE_MASK : +return INA2XX_ALERT_SHUNT_VOLTAGE_HIGH ; +case INA226_SHUNT_UNDER_VOLTAGE_MASK : +return INA2XX_ALERT_SHUNT_VOLTAGE_LOW ; +case INA226_BUS_OVER_VOLTAGE_MASK : +return INA2XX_ALERT_BUS_VOLTAGE_HIGH ; +case INA226_BUS_UNDER_VOLTAGE_MASK : +return INA2XX_ALERT_BUS_VOLTAGE_LOW ; +case INA226_POWER_OVER_LIMIT_MASK : +return INA2XX_ALERT_POWER_HIGH ; +default : +return INA2XX_ALERT_NONE ; + } +} +static int ina226_alert_limit_read (struct ina2xx_data *data,enum ina2xx_alert_type alert,int reg,long *val) {
@@ -456,6 +487,12 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, enum ina2xx_alert_tu32 mask;int ret; +/* Avoid nonzero reads from inactive alerts caused by shared limit register */ +if (data->active_alert != alert) { + *val =0 ; +return 0 ; + } + ret =regmap_read (regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret;
@@ -491,6 +528,7 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, enum ina2xx_alert_ INA226_ALERT_CONFIG_MASK, 0);if (ret <0 )return ret; + data->active_alert =INA2XX_ALERT_NONE ; ret =regmap_write (regmap,INA226_ALERT_LIMIT ,ina226_alert_to_reg (data, reg, val));
@@ -499,9 +537,13 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, enum ina2xx_alert_if (val) { mask =ina2xx_alert_type_to_mask (alert); -return regmap_update_bits (regmap,INA226_MASK_ENABLE , -INA226_ALERT_CONFIG_MASK , mask); + ret =regmap_update_bits (regmap,INA226_MASK_ENABLE , +INA226_ALERT_CONFIG_MASK , mask); +if (ret <0 ) +return ret; + data->active_alert = alert; } +return 0 ; }
@@ -531,6 +573,15 @@ static int ina226_alert_read(struct ina2xx_data *data, enum ina2xx_alert_type alu32 mask;int ret; +/* +* With alert latching, reading alerts from hardware also clears the +* alert, so return early if the alert is inactive. +*/ +if (data->active_alert != alert) { + *val =0 ; +return 0 ; + } + ret =regmap_read_bypassed (data->regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret;
@@ -973,6 +1024,16 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)if (data->config->has_alerts) {bool active_high =device_property_read_bool (dev,"ti,alert-polarity-active-high" ); +unsigned int mask_enable; + +/* +* Infer active alert from MASK_ENABLE in case it's already +* configured (e.g., by a past probe or firmware) +*/ + ret =regmap_read (regmap,INA226_MASK_ENABLE , &mask_enable); +if (ret <0 ) +return ret; + data->active_alert =ina2xx_mask_to_alert_type (mask_enable); regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_LATCH_ENABLE | INA226_ALERT_POLARITY,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 329/733 Documentation: hwmon: replace full-width colon by a standard ASCII colon
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Antonin Godard <[email protected]>
[ Upstream commit 013c5a93e8062014177d68af799e8867cf03958d ]
It prevented the pdfdocs target to complete, prompting the following error:
Latexmk: ====Problematic refs and citations with line #s in .tex file:
Missing character: There is no : (U+FF1A) in font DejaVu Serif/OT:script=latn;l
--- Documentation/hwmon/gpd-fan.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/hwmon/gpd-fan.rst +1 -1
@@ -67,7 +67,7 @@ pwm1_enable at full speed. Write "1" to set to manual, write "2" to let the EC control decide fan speed. Read this attribute to see current status. - NB:In consideration of the safety of the device, when setting to manual mode, + NB: In consideration of the safety of the device, when setting to manual mode, the pwm speed will be set to the maximum value (255) by default. You can set a different value by writing pwm1 later.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 330/733 hwmon: (yogafan) fix non-kernel-doc comment
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: hanzhijian <[email protected]>
[ Upstream commit 100eb7c7d0b28c52ad1b25d51c316fdc46c27c71 ]
The file description comment starts with "/**" which is reserved for kernel-doc comments, triggering a kernel-doc checker warning. Change it to a plain "/*" comment since it does not document any function or struct.
--- drivers/hwmon/yogafan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/yogafan.c +1 -1
@@ -1,5 +1,5 @@// SPDX-License-Identifier: GPL-2.0-only -/** +/* * yoga_fan.c - Lenovo Yoga/Legion Fan Hardware Monitoring Driver * * Provides fan speed monitoring for Lenovo Yoga, Legion, and IdeaPad
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 331/733 hwmon: (sht4x) Add missing locks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 06b7cf395b1fb652a50db39674a759658fbfba0d ]
Sashiko reports:
Heater sysfs callbacks (heater_enable_store, heater_power_store, and heater_time_store) are exposed to data races without the hwmon lock.
If a user-space process reads hwmon data while another process enables the heater, heater_enable_store() executes without holding hwmon_lock(dev). This can interleave I2C commands and mutate shared state (data->heating_complete and data->data_pending) concurrently with sht4x_read_values(), leading to corrupted I2C sequences.
--- drivers/hwmon/sht4x.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/hwmon/sht4x.c +6 -0
@@ -277,6 +277,8 @@ static ssize_t heater_enable_store(struct device *dev, heating_time_bound =1100 ; } +guard (hwmon_lock)(dev); +if (time_before (jiffies, data->heating_complete))return -EBUSY ;
@@ -314,6 +316,8 @@ static ssize_t heater_power_store(struct device *dev,if (power !=20 && power !=110 && power !=200 )return -EINVAL ; +guard (hwmon_lock)(dev); + data->heater_power = power;return count;
@@ -344,6 +348,8 @@ static ssize_t heater_time_store(struct device *dev,if (time !=100 && time !=1000 )return -EINVAL ; +guard (hwmon_lock)(dev); + data->heater_time = time;return count;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 332/733 hwmon: (sht4x) Fix return value from heater_enable_store()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 70c33e211b2b78830f76c908e5236b77ffde63a0 ]
Sashiko reports:
The return value in heater_enable_store() causes an unexpected write failure in user-space.
When the heater is successfully enabled, the function returns 0 instead of count:
drivers/hwmon/sht4x.c:heater_enable_store() {
...
data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
data->data_pending = true;
return 0;
}Returning 0 signals to VFS that no bytes were processed. Standard user-space tools will retry the write with the remaining bytes. On the retry, time_before(jiffies, data->heating_complete) evaluates to true, and the function immediately fails with -EBUSY.
Return count as expected to fix the problem.
--- drivers/hwmon/sht4x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/sht4x.c +1 -1
@@ -288,7 +288,7 @@ static ssize_t heater_enable_store(struct device *dev, data->heating_complete = jiffies +msecs_to_jiffies (heating_time_bound); data->data_pending = true; -return 0 ; +return count; } static ssize_t heater_power_show(struct device *dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 333/733 ASoC: bcm: bcm63xx: Publish the OF module aliases
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: hpp.iscas <[email protected]>
[ Upstream commit 32689f0fc54fd801f1cd11637666e534984cb04a ]
The BCM63xx I2S platform driver matches brcm,bcm63xx-i2s using snd_soc_bcm_audio_match. With SND_BCM63XX_I2S_WHISTLER=m, the platform bus emits an OF modalias but snd-soc-63xx does not publish that table.
Export the existing OF IDs for module autoloading. The PCM companion and the probe path remain unchanged.
--- sound/soc/bcm/bcm63xx-i2s-whistler.c | 1 + 1 file changed, 1 insertion(+)
sound/soc/bcm/bcm63xx-i2s-whistler.c +1 -0
@@ -285,6 +285,7 @@ static const struct of_device_id snd_soc_bcm_audio_match[] = { {.compatible = "brcm,bcm63xx-i2s"}, { } }; +MODULE_DEVICE_TABLE (of, snd_soc_bcm_audio_match);#endif static struct platform_driver bcm63xx_i2s_driver = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 334/733 ASoC: Intel: SST: Publish the PCI module aliases
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: hpp.iscas <[email protected]>
[ Upstream commit d112159df5c6cc5ee6ab91cc32bf6ed29939df38 ]
The legacy SST PCI driver matches Intel Tangier devices using intel_sst_ids, but its only explicit module alias is "sst". That alias does not match PCI modalias events when this driver is built as a module.
Publish its PCI table. The independently configurable SOF driver does not provide aliases for the legacy SST module.
--- sound/soc/intel/atom/sst/sst_pci.c | 1 + 1 file changed, 1 insertion(+)
sound/soc/intel/atom/sst/sst_pci.c +1 -0
@@ -167,6 +167,7 @@ static const struct pci_device_id intel_sst_ids[] = { { PCI_DEVICE_DATA(INTEL, SST_TNG, 0) }, { 0, } }; +MODULE_DEVICE_TABLE (pci, intel_sst_ids);static struct pci_driver sst_driver = { .name = SST_DRV_NAME,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 335/733 btrfs: fix unnecessary transaction commit fallback from btrfs_log_all_parents()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 36f9aafa46f5b9fecf92d9218c5574f1ef6b4907 ]
When btrfs_log_all_parents() returns without doing any work (because all parent directories were already logged), it returns 1, which is propagated up the fsync call chain up to btrfs_log_dentry_safe(), and that causes btrfs_sync_file() to trigger am unnecessary transaction commit.
This all happens because the call to btrfs_search_slot() in btrfs_log_all_parents() always returns 1, as there can not be any inode ref keys with an offset 0 (an invalid inode number), so if the while loop below it does not do any work because all parent directories were already logged, the 'ret' variable remains with a value of 1, which is then returned up the call chain to btrfs_sync_file().
Fix this by setting 'ret' to 0 after the call to btrfs_search_slot().
--- fs/btrfs/tree-log.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
fs/btrfs/tree-log.c +16 -0
@@ -7307,6 +7307,22 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans, ret =btrfs_search_slot (NULL , root, &key, path,0 ,0 );if (ret <0 ) goto out; +/* +* There can't be an inode ref key with offset 0 because inode numbers +* start at BTRFS_FIRST_FREE_OBJECTID. +*/ +if (WARN_ON_ONCE (ret ==0 )) { +btrfs_err (trans->fs_info, +"found inode ref key with offset 0 for root %llu inode %llu" , +btrfs_root_id (root), ino); + ret =BTRFS_LOG_FORCE_COMMIT ; + goto out; + } +/* +* Set to 0 so that in case we don't do any work below, we won't return +* 1 and trigger an unnecessary transaction commit. +*/ + ret =0 ;while (true) {struct extent_buffer *leaf = path->nodes[0 ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 336/733 netfilter: nfnetlink_log: cope with concurrent instance destruction
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 387d744fa7e499d2c3748a4e60e02ebb24e7fb16 ]
Instances are refcounted. However, only memory release happens on the 1 -> 0 transition; the unlink from hashes can occur with any refcount.
Uncooperative userspace can force a situation where a queue is pending for destruction from netlink event while a different socket with same portid processes an UNBIND request.
With right timing, this will unhash the instance again:
Oops: general protection fault, [..] Call Trace: <TASK> nfulnl_recv_config+0x31a/0xd50 nfnetlink_rcv_msg+0x7c2/0xeb0
--- net/netfilter/nfnetlink_log.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
net/netfilter/nfnetlink_log.c +9 -4
@@ -228,13 +228,18 @@ static void __nfulnl_flush(struct nfulnl_instance *inst);static void __instance_destroy (struct nfulnl_instance *inst) { +spin_lock (&inst->lock); +if (inst->copy_mode ==NFULNL_COPY_DISABLED ) { +/* attempt to UNBIND a queue already pending +* destruction via netlink close event. Ignore. +*/ +spin_unlock (&inst->lock); +return ; + } +/* first pull it out of the global list */ hlist_del_rcu (&inst->hlist); -/* then flush all pending packets from skb */ - -spin_lock (&inst->lock); -/* lockless readers wont be able to use us */ inst->copy_mode =NFULNL_COPY_DISABLED ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 337/733 netfilter: ip6_tables: set F_PROTO when proto value is nonzero
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit da4afc5a956d407443988e97a4d4ca14c2e999c7 ]
The ip6tables traverser doesn't search the extension header chain unless userspace did set the IP6T_F_PROTO flag.
This also means that userspace that sets the e->ipv6.proto flag can bypass the protocol check for the rule by not setting this flag.
That in turn means that all ip6_tables modules and targets that want to reject rules without '-p' flag MUST also check for that flag.
Not all do, likely because they got copied from iptables which lacks this flag (no extension headers).
Instead of fixing up all the relevant targets, emulate ip6tables behaviour in the kernel (like nft_compat.c) and set the flag if the protocol is set.
--- net/ipv6/netfilter/ip6_tables.c | 5 +++++ 1 file changed, 5 insertions(+)
net/ipv6/netfilter/ip6_tables.c +5 -0
@@ -647,6 +647,11 @@ check_entry_size_and_hooks(struct ip6t_entry *e,/* Clear counters and comefrom */ e->counters = ((struct xt_counters ) {0 ,0 }); e->comefrom =0 ; + +/* set F_PROTO, else ip6_packet_match won't do the right thing. */ +if (e->ipv6.proto) + e->ipv6.flags |=IP6T_F_PROTO ; +return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 338/733 regulator: pf1550: fix which regulator is notified
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
[ Upstream commit f3e6ef13e24c9f26dca0d35de57fcdf04f78e378 ]
The interrupt handler distinguishes the rail that reported the fault, but the body ignores it. Every SW interrupt walks the regulator array looking for the name "SW3" and every LDO interrupt looks for "LDO3", so an over-current on SW1 is reported to the consumers of SW3 while the consumers of SW1 hear nothing.
The lookup itself is unreliable as well. rdev_get_name() returns the device tree regulator-name property whenever the board supplies one, and only falls back to the name in the driver descriptor when it does not. The binding example for this device sets regulator-name to "sw3" and "ldo3", which strcmp() does not match against the upper case literals used here, so a board that follows the documentation gets no over-current notification at all. A board that names its rails after the schematic does not match either. No other driver in the tree selects a notification target this way.
Replace the name lookup with rdev_get_id(), which returns the descriptor id set by the driver and cannot be overridden from the device tree, and take both the id and the event from a table indexed by the interrupt. The die temperature interrupts keep notifying every regulator since they report a chip wide condition.
--- drivers/regulator/pf1550-regulator.c | 82 ++++++++++++++-------------- 1 file changed, 40 insertions(+), 42 deletions(-)
drivers/regulator/pf1550-regulator.c +40 -42
@@ -283,63 +283,61 @@ static struct pf1550_desc pf1550_regulators[] = { PF_LDO1(PF1550, "ldo3", LDO3, 0x1f, pf1550_ldo13_volts), }; +/* +* The _LS interrupts indicate an over-current event. The _HS +* interrupts, which are more accurate and can detect catastrophic +* faults, issue an error event. The current limit FAULT interrupt is +* similar to the _HS. +*/ +static const struct pf1550_regulator_irq { +unsigned int event; +u8 id; +} pf1550_regulator_irqs[] = { + [PF1550_PMIC_IRQ_SW1_LS ] = {REGULATOR_EVENT_OVER_CURRENT_WARN ,PF1550_SW1 }, + [PF1550_PMIC_IRQ_SW2_LS ] = {REGULATOR_EVENT_OVER_CURRENT_WARN ,PF1550_SW2 }, + [PF1550_PMIC_IRQ_SW3_LS ] = {REGULATOR_EVENT_OVER_CURRENT_WARN ,PF1550_SW3 }, + [PF1550_PMIC_IRQ_SW1_HS ] = {REGULATOR_EVENT_OVER_CURRENT ,PF1550_SW1 }, + [PF1550_PMIC_IRQ_SW2_HS ] = {REGULATOR_EVENT_OVER_CURRENT ,PF1550_SW2 }, + [PF1550_PMIC_IRQ_SW3_HS ] = {REGULATOR_EVENT_OVER_CURRENT ,PF1550_SW3 }, + [PF1550_PMIC_IRQ_LDO1_FAULT ] = {REGULATOR_EVENT_OVER_CURRENT ,PF1550_LDO1 }, + [PF1550_PMIC_IRQ_LDO2_FAULT ] = {REGULATOR_EVENT_OVER_CURRENT ,PF1550_LDO2 }, + [PF1550_PMIC_IRQ_LDO3_FAULT ] = {REGULATOR_EVENT_OVER_CURRENT ,PF1550_LDO3 }, +}; +static irqreturn_t pf1550_regulator_irq_handler (int irq,void *data) { +const struct pf1550_regulator_irq *map;struct pf1550_regulator_info *info = data;struct device *dev = info->dev;struct platform_device *pdev =to_platform_device (dev);int i, irq_type =-1 ; -unsigned int event;for (i =0 ; i <PF1550_REGULATOR_IRQ_NR ; i++)if (irq ==platform_get_irq (pdev, i)) irq_type = i; -switch (irq_type) { -/* The _LS interrupts indicate over-current event. The _HS interrupts -* which are more accurate and can detect catastrophic faults, issue -* an error event. The current limit FAULT interrupt is similar to the -* _HS' -*/ -case PF1550_PMIC_IRQ_SW1_LS : -case PF1550_PMIC_IRQ_SW2_LS : -case PF1550_PMIC_IRQ_SW3_LS : - event =REGULATOR_EVENT_OVER_CURRENT_WARN ; -for (i =0 ; i <PF1550_MAX_REGULATOR ; i++) -if (!strcmp (rdev_get_name (info->rdevs[i]),"SW3" )) -regulator_notifier_call_chain (info->rdevs[i], - event,NULL ); -break ; -case PF1550_PMIC_IRQ_SW1_HS : -case PF1550_PMIC_IRQ_SW2_HS : -case PF1550_PMIC_IRQ_SW3_HS : - event =REGULATOR_EVENT_OVER_CURRENT ; -for (i =0 ; i <PF1550_MAX_REGULATOR ; i++) -if (!strcmp (rdev_get_name (info->rdevs[i]),"SW3" )) -regulator_notifier_call_chain (info->rdevs[i], - event,NULL ); -break ; -case PF1550_PMIC_IRQ_LDO1_FAULT : -case PF1550_PMIC_IRQ_LDO2_FAULT : -case PF1550_PMIC_IRQ_LDO3_FAULT : - event =REGULATOR_EVENT_OVER_CURRENT ; -for (i =0 ; i <PF1550_MAX_REGULATOR ; i++) -if (!strcmp (rdev_get_name (info->rdevs[i]),"LDO3" )) -regulator_notifier_call_chain (info->rdevs[i], - event,NULL ); -break ; -case PF1550_PMIC_IRQ_TEMP_110 : -case PF1550_PMIC_IRQ_TEMP_125 : - event =REGULATOR_EVENT_OVER_TEMP ; +/* The die temperature concerns every rail. */ +if (irq_type ==PF1550_PMIC_IRQ_TEMP_110 || + irq_type ==PF1550_PMIC_IRQ_TEMP_125 ) {for (i =0 ; i <PF1550_MAX_REGULATOR ; i++)regulator_notifier_call_chain (info->rdevs[i], - event,NULL ); -break ; -default : -dev_err (dev,"regulator interrupt: irq %d occurred\n" , - irq_type); +REGULATOR_EVENT_OVER_TEMP , +NULL ); +return IRQ_HANDLED ; + } + +if (irq_type <0 || irq_type >= (int )ARRAY_SIZE (pf1550_regulator_irqs)) { +dev_err (dev,"regulator interrupt: irq %d occurred\n" , irq_type); +return IRQ_HANDLED ; } + map = &pf1550_regulator_irqs[irq_type]; + +for (i =0 ; i <PF1550_MAX_REGULATOR ; i++) +if (rdev_get_id (info->rdevs[i]) == map->id) +regulator_notifier_call_chain (info->rdevs[i], + map->event,NULL ); +return IRQ_HANDLED ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 339/733 ASoC: mt6351: Publish the OF module alias
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: hpp.iscas <[email protected]>
[ Upstream commit 9c3882ec10399c14c59b7e4599d33c4395367c37 ]
The MT6351 codec platform driver uses mt6351_of_match to bind devices with compatible mediatek,mt6351-sound. The codec can be a separate module, but the OF table is not exported to module alias metadata.
Publish the existing table without changing codec matching, register access or the machine-driver configuration.
--- sound/soc/codecs/mt6351.c | 1 + 1 file changed, 1 insertion(+)
sound/soc/codecs/mt6351.c +1 -0
@@ -1478,6 +1478,7 @@ static const struct of_device_id mt6351_of_match[] = { {.compatible = "mediatek,mt6351-sound",}, {} }; +MODULE_DEVICE_TABLE (of, mt6351_of_match);static struct platform_driver mt6351_codec_driver = { .driver = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 340/733 irqchip/gic-v5: Preserve ICC_CR0_EL1 state
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sascha Bischoff <[email protected]>
[ Upstream commit 1017911fcc03584b6854b1b8f0aafeb25f5a8d25 ]
In addition to EN, ICC_CR0_EL1 contains other fields, such as LINK and LINK_IDLE. The driver only needs to modify EN, and must preserve the values of all other fields when enabling or disabling the CPU interface.
Define the missing LINK and LINK_IDLE fields, and use read-modify-write accesses to update EN without affecting the rest of ICC_CR0_EL1.
--- arch/arm64/tools/sysreg | 4 +++- drivers/irqchip/irq-gic-v5.c | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-)
arch/arm64/tools/sysreg +3 -1
@@ -3736,7 +3736,9 @@ Sysreg ICC_CR0_EL1 3 1 12 0 1 Res0 63:39 Field 38 PID Field 37:32 IPPT -Res0 31:1 +Res0 31:3 +Field 2 LINK_IDLE +Field 1 LINK Field 0 EN EndSysreg
drivers/irqchip/irq-gic-v5.c +4 -2
@@ -974,7 +974,8 @@ static void gicv5_cpu_disable_interrupts(void) {u64 cr0; - cr0 =FIELD_PREP (ICC_CR0_EL1_EN ,0 ); + cr0 =read_sysreg_s (SYS_ICC_CR0_EL1 ); + cr0 &= ~ICC_CR0_EL1_EN_MASK ;write_sysreg_s (cr0,SYS_ICC_CR0_EL1 );isb (); }
@@ -991,7 +992,8 @@ static void gicv5_cpu_enable_interrupts(void) pcr =FIELD_PREP (ICC_PCR_EL1_PRIORITY ,GICV5_IRQ_PRI_MI );write_sysreg_s (pcr,SYS_ICC_PCR_EL1 ); - cr0 =FIELD_PREP (ICC_CR0_EL1_EN ,1 ); + cr0 =read_sysreg_s (SYS_ICC_CR0_EL1 ); + cr0 |=ICC_CR0_EL1_EN_MASK ;write_sysreg_s (cr0,SYS_ICC_CR0_EL1 ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 341/733 virtio_ring: fix stale descriptor flags after a failed packed add
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Graf <[email protected]>
[ Upstream commit 75d276e5bb68778b2916f98a2bc30f142ebadc64 ]
In a packed ring the AVAIL and USED bits sit in the descriptor itself, so writing them makes that descriptor available. Those bit combinations flip meaning on every round of the ring, tracked by a wrap counter, so invalidating or validating a descriptor means inverting both bits.
Commit 1ce9e6055fa0 ("virtio_ring: introduce packed ring support") has virtqueue_add_packed() make every descriptor of a chain available as it maps the chain, and write the head last. The device consumes the ring in order and stops at a head that is not available yet, so it never reaches the rest.
When vring_map_one_sg() fails partway, unmap_release unmaps the segments and restores avail_used_flags, but the descriptors it wrote to in the ring stay marked with AVAIL and USED bits. The head is now the only entry that keeps the device from consuming these stale entries.
For example, the ring would look like this now.
Z - pre-previous command
A - previous command
B - aborted command
C - current command
[A1 DONE] [A2 DONE] <C1 EMPTY> [B2] [B3] [Z1 DONE]
When the driver now attempts to issue the C command, the next add starts at the same head as B. If C spans less descriptors than B, there is no end marker because AVAIL and USED bits were still in place. And that means the device will start interpreting these stale entries (B2/B3) as another command entry, which then blocks the queue.
This effect typically happens in swiotlb configurations under memory pressure, because vring_map_one_sg() can then fail with larger I/O requests which then leads to command abortions.
There are broadly 2 ways to avoid leaving those flags behind:
1) Defer those flags too until the chain is complete.
2) Rewrite those flags for the previous wrap counter.
Implement the second option in both packed add paths. The first option traverses the chain a second time on every successful add. The second option invalidates all added descriptors when any add fails.
With this patch applied, a packed virtqueue keeps completing requests after a failed add.
Message-ID: <[email protected]>
--- drivers/virtio/virtio_ring.c | 38 ++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
drivers/virtio/virtio_ring.c +34 -4
@@ -1670,7 +1670,7 @@ static inline int virtqueue_add_packed(struct vring_virtqueue *vq,struct scatterlist *sg;unsigned int i, n, c, descs_used, err_idx, len;__le16 head_flags, flags; -u16 head, id, prev, curr, avail_used_flags; +u16 head, id, prev, curr, avail_used_flags, unpub_flags;int err;START_USE (vq);
@@ -1798,15 +1798,30 @@ static inline int virtqueue_add_packed(struct vring_virtqueue *vq, curr = vq->free_head; vq->packed.avail_used_flags = avail_used_flags; + unpub_flags = avail_used_flags ^ (1 <<VRING_PACKED_DESC_F_AVAIL | +1 <<VRING_PACKED_DESC_F_USED );for (n =0 ; n < total_sg; n++) {if (i == err_idx)break ; +/* +* The mapping loop made every descriptor but the head +* available. Stamp the previous wrap counter's AVAIL and USED +* bits on those, so that a later and shorter chain at this head +* does not leave one of them available beyond its own last +* descriptor. Marking them used instead would hand +* is_used_desc_packed() a completion we never made. +*/ +if (i != head) + desc[i].flags =cpu_to_le16 (unpub_flags);vring_unmap_extra_packed (vq, &vq->packed.desc_extra[curr]); curr = vq->packed.desc_extra[curr].next; i++; -if (i >= vq->packed.vring.num) +if (i >= vq->packed.vring.num) { i =0 ; + unpub_flags ^=1 <<VRING_PACKED_DESC_F_AVAIL | +1 <<VRING_PACKED_DESC_F_USED ; + } }END_USE (vq);
@@ -1828,7 +1843,7 @@ static inline int virtqueue_add_packed_in_order(struct vring_virtqueue *vq,struct scatterlist *sg;unsigned int i, n, sg_count, err_idx, total_in_len =0 ;__le16 head_flags, flags; -u16 head, avail_used_flags; +u16 head, avail_used_flags, unpub_flags;bool avail_wrap_counter;int err;
@@ -1955,14 +1970,29 @@ static inline int virtqueue_add_packed_in_order(struct vring_virtqueue *vq, i = head; vq->packed.avail_used_flags = avail_used_flags; vq->packed.avail_wrap_counter = avail_wrap_counter; + unpub_flags = avail_used_flags ^ (1 <<VRING_PACKED_DESC_F_AVAIL | +1 <<VRING_PACKED_DESC_F_USED );for (n =0 ; n < total_sg; n++) {if (i == err_idx)break ; +/* +* The mapping loop made every descriptor but the head +* available. Stamp the previous wrap counter's AVAIL and USED +* bits on those, so that a later and shorter chain at this head +* does not leave one of them available beyond its own last +* descriptor. Marking them used instead would hand +* is_used_desc_packed() a completion we never made. +*/ +if (i != head) + desc[i].flags =cpu_to_le16 (unpub_flags);vring_unmap_extra_packed (vq, &vq->packed.desc_extra[i]); i++; -if (i >= vq->packed.vring.num) +if (i >= vq->packed.vring.num) { i =0 ; + unpub_flags ^=1 <<VRING_PACKED_DESC_F_AVAIL | +1 <<VRING_PACKED_DESC_F_USED ; + } }END_USE (vq);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 342/733 virtio: fix use-after-free in unregister_virtio_device()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 3f9a0fceb730f5107d52421ead5568eae25a0049 ]
device_unregister() is device_del() plus put_device(). When the caller holds no extra reference, that drops the last one and runs the release callback, which for several transports frees the memory the embedded struct virtio_device sits in. unregister_virtio_device() then calls virtio_debug_device_exit(), which reads dev->debugfs_dir out of the freed object.
Affected transports are the ones whose release callback frees and whose remove path takes no reference: virtio_mmio, virtio_vdpa, virtio_uml, mlxbf-tmfifo and virtio_ccw. virtio_pci is unaffected because virtio_pci_remove() brackets the call with get_device() and put_device().
Remove the debugfs entries before the device can go away. They are only accessed through the protected debugfs interface, so debugfs_remove_recursive() waits for in-progress file operations before returning. Tearing them down while the device is still alive is therefore safe.
Reproduced on User-Mode Linux with CONFIG_KASAN and CONFIG_VIRTIO_DEBUG by unbinding a virtio-uml device:
BUG: KASAN: slab-use-after-free in virtio_debug_device_exit+0x36/0x4d Read of size 8 at addr 00000000616e0b10 by task init/1 __asan_report_load8_noabort virtio_debug_device_exit+0x36/0x4d unregister_virtio_device+0x48/0x75 virtio_uml_remove platform_remove device_release_driver_internal unbind_store Freed by task 1: kfree virtio_uml_release_dev device_release kobject_put put_device device_unregister
With this applied, the report is gone and unbind is clean.
Message-ID: <[email protected]>
--- drivers/virtio/virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/virtio/virtio.c +1 -1
@@ -604,8 +604,8 @@ void unregister_virtio_device(struct virtio_device *dev) {int index = dev->index;/* save for after device release */ -device_unregister (&dev->dev);virtio_debug_device_exit (dev); +device_unregister (&dev->dev);ida_free (&virtio_index_ida, index); }EXPORT_SYMBOL_GPL (unregister_virtio_device);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 343/733 virtio_console: do not free control-out buffers on remove
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jia Jia <[email protected]>
[ Upstream commit 894f98e73983f37354214a89a3a7fd35bf9e3072 ]
__send_control_msg() publishes &portdev->cpkt as the control-out virtqueue cookie. remove_vqs() walks every virtqueue and passes leftover cookies to free_buf(), which treats them as struct port_buffer and reads sgpages.
If a control message is still on c_ovq when the device is unbound, free_buf() reads past the ports_device object.
KASAN reported slab-out-of-bounds in free_buf():
free_buf
remove_vqs
virtcons_remove
unbind_store
The object was the ports_device allocated in virtcons_probe().
Drain c_ovq without freeing. The packet lives in portdev and is released with it.
Message-ID: <[email protected]>
--- drivers/char/virtio_console.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-)
drivers/char/virtio_console.c +18 -3
@@ -1891,13 +1891,28 @@ static const struct file_operations portdev_fops = {static void remove_vqs (struct ports_device *portdev) {struct virtqueue *vq; +bool multiport =use_multiport (portdev);virtio_device_for_each_vq (portdev->vdev, vq) {struct port_buffer *buf; +unsigned int len; -flush_bufs (vq, true); -while ((buf =virtqueue_detach_unused_buf (vq))) -free_buf (buf, true); +/* +* c_ovq cookies are &portdev->cpkt, not port_buffer. +* Detach them but do not free_buf(). +*/ +if (multiport && vq == portdev->c_ovq) { +spin_lock (&portdev->c_ovq_lock); +while (virtqueue_get_buf (vq, &len)) + ; +while (virtqueue_detach_unused_buf (vq)) + ; +spin_unlock (&portdev->c_ovq_lock); + }else { +flush_bufs (vq, true); +while ((buf =virtqueue_detach_unused_buf (vq))) +free_buf (buf, true); + }cond_resched (); } portdev->vdev->config->del_vqs (portdev->vdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 344/733 vhost/vdpa: reject VRING_NUM larger than device max
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jia Jia <[email protected]>
[ Upstream commit ccb1dc7c527f8c925925cf92afc76ae590dac311 ]
vhost_vring_set_num() accepts any non-zero power-of-two queue size that fits in 16 bits. vhost-vdpa then passes that value to set_vq_num() without comparing it with get_vq_num_max().
A process with access to /dev/vhost-vdpa-* can therefore configure a queue larger than the device advertises. With vdpa_sim, the worker can walk descriptors beyond the mapped descriptor ring. KASAN reports a 16-byte out-of-bounds read, corresponding to one vring_desc, in the vringh IOTLB path:
BUG: KASAN: out-of-bounds in _copy_from_iter Read of size 16 copy_from_iotlb copydesc_iotlb vringh_getdesc_iotlb vdpasim_net_work
Cache get_vq_num_max() immediately after reset. Some backends derive it from writable queue-size state, so querying it after SET_NUM may return the current size instead of the device capability. Invalidate the cached value before reset so a failed reset leaves SET_NUM disabled.
For VHOST_SET_VRING_NUM, copy the complete vring state once and use the same index and size for validation, vq->num, and set_vq_num(). This ensures that validation and use operate on the same copied values.
Message-ID: <[email protected]>
--- drivers/vhost/vdpa.c | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-)
drivers/vhost/vdpa.c +37 -7
@@ -58,6 +58,7 @@ struct vhost_vdpa {struct cdev cdev;atomic_t opened;u32 nvqs; +u16 vq_num_max;int virtio_id;int minor;struct eventfd_ctx *config_ctx;
@@ -236,7 +237,9 @@ static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid)static int _compat_vdpa_reset (struct vhost_vdpa *v) {struct vdpa_device *vdpa = v->vdpa; +const struct vdpa_config_ops *ops = vdpa->config;u32 flags =0 ; +int ret; v->suspended = false;
@@ -246,7 +249,14 @@ static int _compat_vdpa_reset(struct vhost_vdpa *v) VDPA_RESET_F_CLEAN_MAP :0 ; } -return vdpa_reset (vdpa, flags); + v->vq_num_max =0 ; + ret =vdpa_reset (vdpa, flags); +if (!ret) { +/* Some backends derive the max from mutable queue state. */ + v->vq_num_max = ops->get_vq_num_max (vdpa); + } + +return ret; }static int vhost_vdpa_reset (struct vhost_vdpa *v)
@@ -648,9 +658,15 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,u32 idx;long r; - r =get_user (idx, (u32 __user *)argp); -if (r <0 ) -return r; +if (cmd ==VHOST_SET_VRING_NUM ) { +if (copy_from_user (&s, argp,sizeof (s))) +return -EFAULT ; + idx = s.index; + }else { + r =get_user (idx, (u32 __user *)argp); +if (r <0 ) +return r; + }if (idx >= v->nvqs)return -ENOBUFS ;
@@ -659,6 +675,23 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, vq = &v->vqs[idx];switch (cmd) { +case VHOST_SET_VRING_NUM : +mutex_lock (&vq->mutex); +if (vq->private_data) { + r = -EBUSY ; + }else if (!s.num || s.num >0xffff || + s.num > v->vq_num_max || + (s.num & (s.num -1 ))) { + r = -EINVAL ; + }else { + vq->num = s.num; + r =0 ; + } +mutex_unlock (&vq->mutex); +if (r) +return r; + ops->set_vq_num (vdpa, idx, s.num); +return 0 ;case VHOST_VDPA_SET_VRING_ENABLE :if (copy_from_user (&s, argp,sizeof (s)))return -EFAULT ;
@@ -772,9 +805,6 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, ops->set_vq_cb (vdpa, idx, &cb);break ; -case VHOST_SET_VRING_NUM : - ops->set_vq_num (vdpa, idx, vq->num); -break ; }return r;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 345/733 vhost-vdpa: dont install the eventfd_ctx_fdget() error in config_ctx
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yu Zhang <[email protected]>
[ Upstream commit e74a9fa50749b9940b4fb13199652325e08d3c4a ]
vhost_vdpa_set_config_call() swaps the eventfd_ctx_fdget() return value into v->config_ctx before checking it, so on failure the field briefly holds an ERR_PTR:
ctx = fd == VHOST_FILE_UNBIND ? NULL : eventfd_ctx_fdget(fd);
swap(ctx, v->config_ctx);
if (!IS_ERR_OR_NULL(ctx))
eventfd_ctx_put(ctx);
if (IS_ERR(v->config_ctx)) {
long ret = PTR_ERR(v->config_ctx);
v->config_ctx = NULL;
return ret;
}Commit 0bde59c1723a ("vhost-vdpa: set v->config_ctx to NULL if eventfd_ctx_fdget() fails") added that clearing, and spelled out the invariant the rest of the file relies on: "we consider 'v->config_ctx' valid if it is not NULL". The window between the swap and the clearing still breaks it. vhost_vdpa_config_cb() only tests for NULL, so a config interrupt delivered inside the window hands the ERR_PTR to eventfd_signal().
Check the fd before installing it instead. That closes the window and matches how vhost_vring_ioctl() handles the same failure for the vq call fd.
It also stops a rejected fd from tearing down a config interrupt that was
working: until now the swap replaced the live context and put it, so after an EBADF the device silently stopped delivering config interrupts until userspace installed a new fd.
Message-ID: <[email protected]>
--- drivers/vhost/vdpa.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
drivers/vhost/vdpa.c +4 -8
@@ -546,18 +546,14 @@ static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp)return -EFAULT ; ctx = fd ==VHOST_FILE_UNBIND ?NULL :eventfd_ctx_fdget (fd); +if (IS_ERR (ctx)) +return PTR_ERR (ctx); +swap (ctx, v->config_ctx); -if (!IS_ERR_OR_NULL (ctx)) +if (ctx)eventfd_ctx_put (ctx); -if (IS_ERR (v->config_ctx)) { -long ret =PTR_ERR (v->config_ctx); - - v->config_ctx =NULL ; -return ret; - } - v->vdpa->config->set_config_cb (v->vdpa, &cb);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 346/733 vhost-vdpa: protect config_ctx from being freed under the config callback
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yu Zhang <[email protected]>
[ Upstream commit 62be4e3e5f5f947fbf765b914cebdc478f715d12 ]
vhost_vdpa_config_cb() loads v->config_ctx and signals it without taking a reference and without holding any lock:
struct eventfd_ctx *config_ctx = v->config_ctx; if (config_ctx) eventfd_signal(config_ctx);
VHOST_VDPA_SET_CONFIG_CALL replaces that field and drops what is normally the last reference to the old context:
swap(ctx, v->config_ctx); if (ctx) eventfd_ctx_put(ctx);
eventfd_ctx_put() drops the last kref and frees the context immediately, with no RCU grace period, so a callback that has already loaded the pointer goes on to dereference freed memory. The two sides share no
lock: the ioctl runs under vhost_dev.mutex, while the parent invokes the callback from its own interrupt or workqueue context.
This is not the reopen refcount underflow fixed by commit f6bbf0010ba0 ("vhost-vdpa: fix use-after-free of v->config_ctx"), which was about vhost_vdpa_config_put() leaving a stale pointer behind. Here the pointer is maintained correctly and it is the read side that is unprotected.
With VDUSE as the parent this is reachable from userspace with access to /dev/vduse (root by default). VDUSE_DEV_INJECT_CONFIG_IRQ queues dev->inject, and vduse_dev_irq_inject() runs the callback under VDUSE's own dev->irq_lock, which vhost does not hold. vduse_dev_reset() does flush_work(&dev->inject), but VHOST_VDPA_SET_CONFIG_CALL never goes through reset, so an inject already in flight is not waited for. A process that injects config interrupts on the VDUSE fd while another thread swaps the call fd on the vhost-vdpa fd hits it in seconds:
BUG: KASAN: slab-use-after-free in native_queued_spin_lock_slowpath Read of size 4 at addr ffff888107d21808 by task kworker/u17:1/2993 Workqueue: vduse-irq vduse_dev_irq_inject Call Trace: native_queued_spin_lock_slowpath+0x97/0x5b0 _raw_spin_lock_irqsave+0xd4/0xe0 eventfd_signal_mask+0x69/0x120 vhost_vdpa_config_cb+0x34/0x50 vduse_dev_irq_inject+0x46/0x60 process_one_work+0x468/0x950 Allocated by task 2992: do_eventfd+0x50/0x200 __x64_sys_eventfd2+0x2e/0x40 Freed by task 2992: eventfd_ctx_put+0xb9/0xc0 vhost_vdpa_unlocked_ioctl+0x116c/0x2190
Add a spinlock covering every access to config_ctx, so the callback either signals a context that is still alive or observes NULL, and the put happens only once no callback can reach the old value.
Clearing the parent's callback before the put would not be enough: of the in-tree set_config_cb() implementations only VDUSE takes a lock, the rest store the pointer unlocked, so that would not order against an in-flight invocation.
Message-ID: <[email protected]>
--- drivers/vhost/vdpa.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-)
drivers/vhost/vdpa.c +25 -7
@@ -62,6 +62,8 @@ struct vhost_vdpa {int virtio_id;int minor;struct eventfd_ctx *config_ctx; +/* Serialises vhost_vdpa_config_cb() against config_ctx being replaced. */ +spinlock_t config_lock;int in_batch;struct vdpa_iova_range range;u32 batch_asid;
@@ -195,10 +197,12 @@ static irqreturn_t vhost_vdpa_virtqueue_cb(void *private)static irqreturn_t vhost_vdpa_config_cb (void *private) {struct vhost_vdpa *v = private; -struct eventfd_ctx *config_ctx = v->config_ctx; +unsigned long flags; -if (config_ctx) -eventfd_signal (config_ctx); +spin_lock_irqsave (&v->config_lock, flags); +if (v->config_ctx) +eventfd_signal (v->config_ctx); +spin_unlock_irqrestore (&v->config_lock, flags);return IRQ_HANDLED ; }
@@ -528,15 +532,22 @@ static long vhost_vdpa_get_vring_num(struct vhost_vdpa *v, u16 __user *argp)static void vhost_vdpa_config_put (struct vhost_vdpa *v) { -if (v->config_ctx) { -eventfd_ctx_put (v->config_ctx); - v->config_ctx =NULL ; - } +struct eventfd_ctx *ctx; +unsigned long flags; + +spin_lock_irqsave (&v->config_lock, flags); + ctx = v->config_ctx; + v->config_ctx =NULL ; +spin_unlock_irqrestore (&v->config_lock, flags); + +if (ctx) +eventfd_ctx_put (ctx); }static long vhost_vdpa_set_config_call (struct vhost_vdpa *v,u32 __user *argp) {struct vdpa_callback cb; +unsigned long flags;int fd;struct eventfd_ctx *ctx;
@@ -549,8 +560,14 @@ static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp)if (IS_ERR (ctx))return PTR_ERR (ctx); +spin_lock_irqsave (&v->config_lock, flags);swap (ctx, v->config_ctx); +spin_unlock_irqrestore (&v->config_lock, flags); +/* +* The callback can no longer reach the old context, so this is the +* last reference to it. +*/ if (ctx)eventfd_ctx_put (ctx);
@@ -1639,6 +1656,7 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa) }atomic_set (&v->opened,0 ); +spin_lock_init (&v->config_lock); v->minor = minor; v->vdpa = vdpa; v->nvqs = vdpa->nvqs;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 347/733 vdpa_sim_blk: reject out-of-range sector starts
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linfeng Sun <[email protected]>
[ Upstream commit 0a8693f00c408d85f086ad85d29e7030bf1e2055 ]
vdpasim_blk_check_range() logs an invalid start sector but continues validating the request. The subsequent unsigned capacity subtraction can underflow and let an out-of-range buffer offset reach the data path.
The invalid offset is used by three request paths. VIRTIO_BLK_T_OUT copies guest data to blk->buffer + offset through vringh_iov_pull_iotlb(), causing an out-of-bounds write in _copy_from_iter() or memcpy(). VIRTIO_BLK_T_IN copies from blk->buffer + offset to the guest through vringh_iov_push_iotlb(), causing an out-of-bounds read in _copy_to_iter(). VIRTIO_BLK_T_WRITE_ZEROES passes blk->buffer + offset to memset(), causing an out-of-bounds write.
Reject starts at or beyond the capacity before the subtraction. Treat the capacity boundary as invalid because the IN and OUT paths round byte counts down to sectors for validation but later copy the original byte counts. A sub-sector request at the capacity boundary would otherwise still access past the end of the buffer.
I found this bug myself, though the patch was written with AI assistance.
Message-ID: <[email protected]>
--- drivers/vdpa/vdpa_sim/vdpa_sim_blk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +2 -1
@@ -79,10 +79,11 @@ static void vdpasim_blk_buffer_unlock(struct vdpasim_blk *blk)static bool vdpasim_blk_check_range (struct vdpasim *vdpasim,u64 start_sector,u64 num_sectors,u64 max_sectors) { -if (start_sector >VDPASIM_BLK_CAPACITY ) { +if (start_sector >=VDPASIM_BLK_CAPACITY ) {dev_dbg (&vdpasim->vdpa.dev,"starting sector exceeds the capacity - start: 0x%llx capacity: 0x%x\n" , start_sector,VDPASIM_BLK_CAPACITY ); +return false; }if (num_sectors > max_sectors) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 348/733 vdpa_sim_net: check TX pull result before RX copy
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linfeng Sun <[email protected]>
[ Upstream commit 0d195797a80b77f2ec56718cd26d3ee65d0093e8 ]
vringh_iov_pull_iotlb() returns a signed byte count. A failed TX pull is currently added to the unsigned byte counter and then passed as a size_t length to receive_filter() and vringh_iov_push_iotlb(). A negative error can therefore become a large length in the RX path.
Handle non-positive pull results before every length use. Count the TX error and complete the consumed TX descriptor with zero bytes.
I found this bug myself, though the patch was written with AI assistance.
Message-ID: <[email protected]>
--- drivers/vdpa/vdpa_sim/vdpa_sim_net.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/vdpa/vdpa_sim/vdpa_sim_net.c +6 -1
@@ -225,10 +225,15 @@ static void vdpasim_net_work(struct vdpasim *vdpasim)break ; } - ++tx_pkts; read =vringh_iov_pull_iotlb (&txq->vring, &txq->out_iov, net->buffer,PAGE_SIZE ); +if (read <=0 ) { + ++tx_errors; +vdpasim_net_complete (txq,0 ); +continue ; + } + ++tx_pkts; tx_bytes += read;if (!receive_filter (vdpasim, read)) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 349/733 virtio-pci: return IRQ_HANDLED after non-zero ISR
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrew Stellman <[email protected]>
[ Upstream commit 93fa09455fb1a9624b73d42ac1f83771f4818e80 ]
vp_interrupt() reads the ISR before dispatching config-change and vring handling. Reading the ISR also clears it, so once the read returns non-zero the interrupt was from this device and has already been consumed.
Currently vp_interrupt() returns the result of vp_vring_interrupt(). For a config-change interrupt with no vring work, that can return IRQ_NONE even though the ISR was non-zero and the interrupt was handled.
Call vp_vring_interrupt() for any queue work, but once the ISR is non-zero return IRQ_HANDLED.
Tested with QEMU virtio-blk-pci forced to INTx using vectors=0 and pci=nomsi. On an idle device, 200 config-change interrupts were generated using QMP block_resize.
Before this change, irq_handler_exit reported ret=unhandled and /proc/irq/11/spurious increased from 0 to 200 unhandled interrupts. After this change, irq_handler_exit reported ret=handled and the unhandled count remained at 0.
The issue was found during an LLM-assisted Quality Playbook review.
Message-ID: <[email protected]>
--- drivers/virtio/virtio_pci_common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/virtio/virtio_pci_common.c +3 -1
@@ -120,7 +120,9 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)if (isr &VIRTIO_PCI_ISR_CONFIG )vp_config_changed (irq, opaque); -return vp_vring_interrupt (irq, opaque); +vp_vring_interrupt (irq, opaque); + +return IRQ_HANDLED ; } static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 350/733 vduse: validate virtqueue alignment
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jia Jia <[email protected]>
[ Upstream commit fa2c25b4add57888acfa89e398389e267bff3dcf ]
vduse_validate_config() only checks the upper bound of vq_align. Invalid values can therefore reach vring_create_virtqueue_map(). The split-ring helpers use align - 1 as a bit mask, so the alignment must be a non-zero power of two. A zero value makes vring_size() drop the descriptor and available-ring part and vring_init() leave the used ring pointer NULL.
The VIRTIO spec requires the used ring to start at an address aligned to at least 4 bytes. Reject values below VRING_USED_ALIGN_SIZE as well as non-power-of-two values before they reach the virtio ring helpers.
Opening a virtio-net device created with vq_align=0 triggered:
BUG: KASAN: null-ptr-deref in virtqueue_kick_prepare_split+0xe3/0x100 Read of size 2 at addr 0000000000000000 by task systemd-network/1062
Call Trace (relevant frames):
dump_stack_lvl
print_report
kasan_report
__asan_load2
virtqueue_kick_prepare_split+0xe3/0x100 virtqueue_kick_prepare+0x40/0x60 try_fill_recv+0x857/0x1250 virtnet_open+0x189/0x460 __dev_open+0x225/0x390 __dev_change_flags+0x368/0x3b0 netif_change_flags+0x56/0xc0 do_setlink.isra.0+0x68c/0x1e30
Validate the value before it reaches the virtio ring helpers.
Message-ID: <[email protected]>
--- drivers/vdpa/vdpa_user/vduse_dev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/vdpa/vdpa_user/vduse_dev.c +3 -1
@@ -2094,7 +2094,9 @@ static bool vduse_validate_config(struct vduse_dev_config *config,return false; } -if (config->vq_align >PAGE_SIZE ) +if (config->vq_align <VRING_USED_ALIGN_SIZE || + !is_power_of_2 (config->vq_align) || + config->vq_align >PAGE_SIZE )return false;if (config->config_size >PAGE_SIZE )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 351/733 vhost: invalidate vring access on IOTLB transitions
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jia Jia <[email protected]>
[ Upstream commit e4f4761879a230aa59e569102a6ab9851847d833 ]
When VIRTIO_F_ACCESS_PLATFORM changes, cached vring pointers and IOTLB metadata are interpreted in a different address space. Keeping them across the transition can leave stale ring mappings in use.
Clearing d->iotlb before taking the VQ locks also lets a worker observe a transient NULL d->iotlb and fall back to d->umem while translating a descriptor.
Add a common vhost_clear_device_iotlb() helper for vhost-net and vhost-vsock. Take all VQ mutexes in index order before dropping the device-wide IOTLB, invalidate each VQ's cached ring access and metadata, clear pending IOTLB messages, and free the old table after the handoff. This serializes the transition with workers and prevents mixed address space mappings.
On the first direct-to-IOTLB transition, invalidate the cached vring addresses. When an existing device IOTLB is replaced, preserve the GIOVA ring addresses and reset only the metadata cache. After clearing ACCESS_PLATFORM, userspace must configure the vring addresses for the new address mode.
vhost_vq_invalidate_access() clears desc, avail, and used together. Treat the VQ as invalidated only when all three are NULL, since a single GIOVA address may legitimately be zero.
Message-ID: <[email protected]>
--- drivers/vhost/net.c | 2 ++ drivers/vhost/vhost.c | 57 ++++++++++++++++++++++++++++++++++++++++++- drivers/vhost/vhost.h | 1 + drivers/vhost/vsock.c | 2 ++ 4 files changed, 61 insertions(+), 1 deletion(-)
drivers/vhost/net.c +2 -0
@@ -1696,6 +1696,8 @@ static int vhost_net_set_features(struct vhost_net *n, const u64 *features)if (virtio_features_test_bit (features,VIRTIO_F_ACCESS_PLATFORM )) {if (vhost_init_device_iotlb (&n->dev)) goto out_unlock; + }else { +vhost_clear_device_iotlb (&n->dev); }for (i =0 ; i <VHOST_NET_VQ_MAX ; ++i) {
drivers/vhost/vhost.c +56 -1
81 lines not on this page · raw message
drivers/vhost/vhost.h +1 -0
@@ -277,6 +277,7 @@ ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to, int noblock);ssize_t vhost_chr_write_iter (struct vhost_dev *dev,struct iov_iter *from); +void vhost_clear_device_iotlb (struct vhost_dev *d);int vhost_init_device_iotlb (struct vhost_dev *d); void vhost_iotlb_map_free(struct vhost_iotlb *iotlb,
drivers/vhost/vsock.c +2 -0
@@ -868,6 +868,8 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features)if ((features & (1ULL <<VIRTIO_F_ACCESS_PLATFORM ))) {if (vhost_init_device_iotlb (&vsock->dev)) goto err; + }else { +vhost_clear_device_iotlb (&vsock->dev); } vsock->seqpacket_allow = features & (1ULL <<VIRTIO_VSOCK_F_SEQPACKET );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 352/733 virtio_input: reset device if input_register_device() fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiong Weimin <[email protected]>
[ Upstream commit 81489b32a21c9360f8750d1fb600155d27452e19 ]
Probe marks the device DRIVER_OK with virtio_device_ready() before calling input_register_device(). If registration fails, the error path cleared vi->ready and called del_vqs() while the device was still live, so the device could keep DMA to queues that were already torn down.
Match remove/freeze: call virtio_reset_device() on that path before tearing down the virtqueues.
Message-ID: <[email protected]>
--- drivers/virtio/virtio_input.c | 1 + 1 file changed, 1 insertion(+)
drivers/virtio/virtio_input.c +1 -0
@@ -331,6 +331,7 @@ static int virtinput_probe(struct virtio_device *vdev)spin_lock_irqsave (&vi->lock, flags); vi->ready = false;spin_unlock_irqrestore (&vi->lock, flags); +virtio_reset_device (vdev); err_mt_init_slots:input_free_device (vi->idev); err_input_alloc:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 353/733 virtio_input: stop callbacks before unregistering input device
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit d7808b37da0a619cf1fa541c2384e783fecc2480 ]
virtinput_remove() unregisters the input device before resetting the virtio device. virtinput_recv_events() drops vi->lock around input_event(), so clearing vi->ready does not stop a callback that passed the entry check. It can still use vi->idev, requeue buffers and kick the queue.
Reset first, as virtinput_freeze() already does. With the preceding core change, reset waits for callbacks before input_unregister_device() can free vi->idev. Recheck vi->ready after taking the lock again: keep draining completed events so an input packet is not truncated, but stop requeueing buffers and kicking the queue.
With evdev attached, input_unregister_handle() currently waits for an RCU grace period, which also waits out IRQ callbacks. This masks the lifetime bug on PCI and MMIO, but does not protect sleepable callbacks on other transports.
Message-ID: <[email protected]>
--- drivers/virtio/virtio_input.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/virtio/virtio_input.c +6 -2
@@ -49,9 +49,12 @@ static void virtinput_recv_events(struct virtqueue *vq)le16_to_cpu (event->code), le32_to_cpu(event->value));spin_lock_irqsave (&vi->lock, flags); +if (!vi->ready) +continue ;virtinput_queue_evtbuf (vi, event); } -virtqueue_kick (vq); +if (vi->ready) +virtqueue_kick (vq); }spin_unlock_irqrestore (&vi->lock, flags); }
@@ -351,8 +354,9 @@ static void virtinput_remove(struct virtio_device *vdev) vi->ready = false;spin_unlock_irqrestore (&vi->lock, flags); -input_unregister_device (vi->idev); +/* Callbacks use vi->idev. */ virtio_reset_device (vdev); +input_unregister_device (vi->idev);while ((buf =virtqueue_detach_unused_buf (vi->sts)) !=NULL )kfree (buf); vdev->config->del_vqs (vdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 354/733 af_unix: Update last skb marker in manage_oob().
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 94fd4debd2e3a69cf93e766c8b328a810c228119 ]
Fahad Alharbi reported that blocking recv(MSG_PEEK) could hog CPU due to OOB skb.
In the following cases, manage_oob() skips OOB skb(s) and returns NULL for the last recv(MSG_PEEK):
socketpair(AF_UNIX, SOCK_STREAM, 0, sk);
1) skb -> OOB skb -> NULL
send(sk[0], "ab", 2, MSG_OOB);
recv(sk[1], buf, 0, MSG_PEEK);
2) skb -> consumed OOB skb -> NULL
send(sk[0], "ab", 2, MSG_OOB);
recv(sk[1], buf, 1, MSG_OOB);
recv(sk[1], buf, 0, MSG_PEEK);
3) consumed OOB skb -> OOB skb -> NULL
send(sk[0], "a", 1, MSG_OOB);
recv(sk[1], buf, 0, MSG_OOB);
send(sk[0], "b", 1, MSG_OOB);
recv(sk[1], buf, 1, MSG_PEEK);Then, @copied is 0 in unix_stream_read_generic() (zero-length buffer, or non-OOB skb is not yet consumed), and unix_stream_data_wait() is called.
However, it returns immediately because @last is not updated in unix_stream_read_generic(), and the thread busy-waits for a new skb.
Let's update @last in manage_oob().
For MSG_PEEK, @last is updated with the skipped OOB, and for the non-peek case, @last matches the returned value (when !copied) because OOB is unlinked.
Note that manage_oob() is inlined and no stack canary is added.
--- net/unix/af_unix.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
net/unix/af_unix.c +7 -3
@@ -2805,8 +2805,8 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)return 1 ; } -static struct sk_buff *manage_oob (struct sk_buff *skb,struct sock *sk, -int flags,int copied) +static struct sk_buff *manage_oob (struct sk_buff *skb,struct sk_buff **last, +struct sock *sk,int flags,int copied) {struct sk_buff *read_skb =NULL , *unread_skb =NULL ;struct unix_sock *u =unix_sk (sk);
@@ -2820,11 +2820,13 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,if (copied && (!u->oob_skb || skb == u->oob_skb)) { skb =NULL ; }else if (flags &MSG_PEEK ) { + *last = skb; skb =skb_peek_next (skb, &sk->sk_receive_queue); }else { read_skb = skb; skb =skb_peek_next (skb, &sk->sk_receive_queue);__skb_unlink (read_skb, &sk->sk_receive_queue); + *last = skb; }if (!skb)
@@ -2843,8 +2845,10 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,__skb_unlink (skb, &sk->sk_receive_queue); unread_skb = skb; skb =skb_peek (&sk->sk_receive_queue); + *last = skb; } }else if (!sock_flag (sk,SOCK_URGINLINE )) { + *last = skb; skb =skb_peek_next (skb, &sk->sk_receive_queue); }
@@ -2964,7 +2968,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, again:#if IS_ENABLED (CONFIG_AF_UNIX_OOB )if (skb) { - skb =manage_oob (skb, sk, flags, copied); + skb =manage_oob (skb, &last, sk, flags, copied);if (!skb && copied) {unix_state_unlock (sk);break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 355/733 af_unix: Return immediately when manage_oob() returns NULL for 0-length buffer.
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 6e5ee08eb5858d175da6768d75d163817b6a9d4a ]
Fahad Alharbi reported that recv(0, MSG_PEEK) triggers busy-wait in unix_stream_read_generic() if recv() is blocking and the last skb in the queue is MSG_OOB skb.
In such a situation, TCP returns 0 immediately regardless of blocking or non-blocking.
Let's follow the behaviour.
--- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/unix/af_unix.c +1 -1
@@ -2969,7 +2969,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,#if IS_ENABLED (CONFIG_AF_UNIX_OOB )if (skb) { skb =manage_oob (skb, &last, sk, flags, copied); -if (!skb && copied) { +if (!skb && (copied || !state->size)) {unix_state_unlock (sk);break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 356/733 net: ipv4: Fix UDP length overflow with PMTU discover and big MTU
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Mikityanska <[email protected]>
[ Upstream commit b83641e0ab8b20eefcc4cdc5a059f897375291a2 ]
This commit bounds cork->base.fragsize to IP_MAX_MTU to avoid a possible overflow of UDP length that triggers a WARN in udp_set_len_short when setsockopt IP_MTU_DISCOVER is set to IP_PMTUDISC_PROBE, and a large packet is sent over a netdev with an unusually large MTU.
Steps to reproduce:
1. Set device MTU bigger than IP_MAX_MTU + 20. cork->base.fragsize will be set to that MTU in ip_setup_cork.
2. Set IP_MTU_DISCOVER to IP_PMTUDISC_PROBE. It lets maxnonfragsize be set to device MTU (cork->fragsize) in __ip_append_data, rather than to IP_MAX_MTU.
3. Send 65528 bytes of payload (+8 bytes of UDP header, +20 bytes of IPv4 header). Device MTU allows it (it's only one byte bigger than IP_MAX_MTU + IPv4 header, and the device MTU is bigger than that).
4. The UDP length in the built packet is 65536, which overflows the 16-bit length field and triggers the WARN in udp_set_len_short.
Note: IP_PMTUDISC_DO with IPv4 is safe, because ip_dst_mtu_maybe_forward always clamps at IP_MAX_MTU, unlike ip6_dst_mtu_maybe_forward.
The Fixes tag points at the first commit where I could reproduce the overflow with IPv4 and IP_PMTUDISC_PROBE.
--- net/ipv4/ip_output.c | 1 + 1 file changed, 1 insertion(+)
net/ipv4/ip_output.c +1 -0
@@ -1303,6 +1303,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, cork->fragsize =ip_sk_use_pmtu (sk) ?dst4_mtu (&rt->dst) :READ_ONCE (rt->dst.dev->mtu); + cork->fragsize =min (cork->fragsize,IP_MAX_MTU );if (!inetdev_valid_mtu (cork->fragsize))return -ENETUNREACH ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 357/733 net: ipv6: Fix UDP length overflow with PMTU discover and big MTU
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Mikityanska <[email protected]>
[ Upstream commit 0ae10b6be49b425827659b23bcce498f80eb7182 ]
This commit bounds cork->base.fragsize to IP6_MAX_MTU for UDP sockets to avoid a possible overflow of UDP length that triggers a WARN in udp_set_len_short when setsockopt IPV6_MTU_DISCOVER is set to IPV6_PMTUDISC_DO or IPV6_PMTUDISC_PROBE, and a large packet is sent over a netdev with an unusually large MTU.
Steps to reproduce (included in the new selftest):
1. Set device MTU bigger than IP6_MAX_MTU. cork->base.fragsize will be set to that MTU in ip6_setup_cork.
2. Set IPV6_MTU_DISCOVER to IPV6_PMTUDISC_PROBE or IPV6_PMTUDISC_DO. It lets maxnonfragsize be set to device MTU (cork->fragsize) in __ip6_append_data, rather than to IP6_MAX_MTU.
3. Send 65528 bytes of payload (+8 bytes of UDP header, +40 bytes of IPv6 header). Device MTU allows it (it's only one byte bigger than IP6_MAX_MTU, and the device MTU is bigger than that).
4. The UDP length in the built packet is 65536, which overflows the 16-bit length field and triggers the WARN in udp_set_len_short.
To avoid breaking sending UDP jumbograms over raw IPv6 sockets, limit the change to UDP sockets only.
The original overflow bug with IPv6 and IPV6_PMTUDISC_DO seems to predate git history (verified reproduction on 2.6.21), was fixed later, and then reappeared in commit 427faee167bc ("net: ipv6: introduce ip6_dst_mtu_maybe_forward"), which is chosen as the Fixes tag here. The overflow with IPV6_PMTUDISC_PROBE reproduces since its introduction in commit 628a5c561890 ("[INET]: Add IP(V6)_PMTUDISC_RPOBE").
--- net/ipv6/ip6_output.c | 2 ++ 1 file changed, 2 insertions(+)
net/ipv6/ip6_output.c +2 -0
@@ -1432,6 +1432,8 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,if (frag_size && frag_size < mtu) mtu = frag_size; +if (sk_is_udp (sk)) + mtu =min (mtu,IP6_MAX_MTU ); cork->base.fragsize = mtu; cork->base.gso_size = ipc6->gso_size; cork->base.tx_flags =0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 358/733 net: ipv6: Clamp to IP6_MAX_MTU in ip6_dst_mtu_maybe_forward
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Mikityanska <[email protected]>
[ Upstream commit 199271ebc71c1e0913b2fad988a7bff330a8828a ]
Commit 427faee167bc ("net: ipv6: introduce ip6_dst_mtu_maybe_forward") dropped the IP6_MAX_MTU clamp that used to be present in ip6_mtu(). A similar IPv4 commit ac6627a28dbf ("net: ipv4: Consolidate ipv4_mtu and ip_dst_mtu_maybe_forward") preserves the IP_MAX_MTU clamp.
Restore the upper bound in the IPv6 flow to avoid potential 16-bit overflows in forwarding paths.
--- include/net/ip6_route.h | 2 ++ 1 file changed, 2 insertions(+)
include/net/ip6_route.h +2 -0
@@ -382,6 +382,8 @@ static inline unsigned int ip6_dst_mtu_maybe_forward(const struct dst_entry *dstrcu_read_unlock (); out: + mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); +return mtu -lwtunnel_headroom (dst->lwtstate, mtu); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 359/733 vduse: return compat ioctl results directly
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linfeng Sun <[email protected]>
[ Upstream commit 48a4ee65e677559776349128e6a81a6041986c99 ]
The compat handler handles VDUSE_IOTLB_GET_FD and VDUSE_VQ_GET_INFO, but then calls the native handler. Their different command sizes make native dispatch return -ENOIOCTLCMD.
For GET_FD, this overwrites receive_fd()'s return value after the descriptor is installed, leaking one fd per call. Return handled compat results directly and use native dispatch only for other commands.
Message-ID: <[email protected]>
--- drivers/vdpa/vdpa_user/vduse_dev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/vdpa/vdpa_user/vduse_dev.c +3 -3
@@ -1751,11 +1751,11 @@ static long vduse_dev_compat_ioctl(struct file *file, unsigned int cmd, break; }default : - ret = -ENOIOCTLCMD ; -break ; +return vduse_dev_ioctl (file, cmd, + (unsigned long )compat_ptr (arg)); } -return vduse_dev_ioctl (file, cmd, (unsigned long )compat_ptr (arg)); +return ret; }#else #define vduse_dev_compat_ioctl compat_ptr_ioctl
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 360/733 net: macb: zero the link settings taprio reads back
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksei Sviridkin <[email protected]>
[ Upstream commit 0523d5c52a450590bf5992bd6925394f3cc403e8 ]
macb_taprio_setup_replace() calls phylink_ethtool_ksettings_get() with an uninitialised kset, and kset is not only an out-parameter. On a fixed link, or an in-band link with no PHY, phylink writes speed and duplex only if kset->base.rate_matching already reads RATE_MATCH_NONE, a field it never writes itself; in PHY mode before the PHY is attached it writes port and supported and nothing more. Either way the speed read back afterwards can be stack garbage. The ethtool core zeroes the structure on every path into the op, which is why its callers never see this; taprio is the only in-kernel caller passing its own variable.
--- drivers/net/ethernet/cadence/macb_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +2 -2
@@ -4331,9 +4331,9 @@ static int macb_taprio_setup_replace(struct net_device *netdev,u64 total_on_time =0 , start_time_sec =0 , start_time = conf->base_time;u32 configured_queues =0 , speed =0 , start_time_nsec;struct macb_queue_enst_config *enst_queue; -struct tc_taprio_sched_entry *entry; +struct ethtool_link_ksettings kset = {};struct macb *bp =netdev_priv (netdev); -struct ethtool_link_ksettings kset; +struct tc_taprio_sched_entry *entry;struct macb_queue *queue;u32 queue_mask;u8 queue_id;
Signature · 1 line
2.53.0